using System;
using System.IO.Ports;
using System.Text;

using System.Threading;

using SWModules;
using Microsoft.SPOT;

namespace HWModules
{
    //TODO P2 probably need to make this a singleton if we want to process responses from the Twitter

    public class ElmoTwitter
    {
        private NativeUART IOPort;
        private MotherBoard mb;

        public ElmoTwitter(string id, string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int readTimeout, int writeTimeout, bool useSTX, byte STX, byte ETX)
        {
            IOPort = new NativeUART(id, QRecordType.Elmo, processMessage, portName, baudRate, parity, dataBits, stopBits, readTimeout, writeTimeout, useSTX, STX, ETX);
            //IOPort.ReleaseDefaultDataHandler();
           mb = MotherBoard.Instance;
        }
        
        private static byte[] ElmoStop = UTF8Encoding.UTF8.GetBytes("ST;"),
                                ElmoMotorOn = UTF8Encoding.UTF8.GetBytes("MO=1;"),
                                ElmoMotorOff = UTF8Encoding.UTF8.GetBytes("MO=0;"),
                                setEchoOff = UTF8Encoding.UTF8.GetBytes("EO=0;"),
                                setEchoOn = UTF8Encoding.UTF8.GetBytes("EO=1;"),
                                ElmoJV = UTF8Encoding.UTF8.GetBytes("JV="),
                                ElmoJV0 = UTF8Encoding.UTF8.GetBytes("JV=0;"),
                                ElmoBegin = UTF8Encoding.UTF8.GetBytes("BG;"),
                                ElmoVelocityMode = UTF8Encoding.UTF8.GetBytes("UM=2;"),
                                ElmoStatusRegister = UTF8Encoding.UTF8.GetBytes("SR;"),
                                semicolon = UTF8Encoding.UTF8.GetBytes(";");

        public int init()
        {
            if (!IOPort.IsOpen)
            {
                IOPort.Open();
            }

            IOPort.DiscardInBuffer();
            IOPort.DiscardOutBuffer();

            mb.EnableChannelPower(MotherBoard.ChannelNames.PumpMotorControllerElexChannel);
            Thread.Sleep(1000);

            stopMotor();
            //sendEchoOn();
            
            sendEchoOff();
            Thread.Sleep(1000);

            getSerialNumber();
            Thread.Sleep(1000);

            EngrLogger.Comment("Setting Elmo current and acc limits");
            setCurrentAndAcLimits();

            Thread.Sleep(1000);

            getStats();
      
            return (0);
        }

        //SendJV Stuff
        private static byte[] TXBytes = new byte[16];
        private static int TXBytesLength = 0;

        public bool sendJV(int jogVelocityCPS)
        {
            EngrLogger.Comment("BE::sendJV: " + jogVelocityCPS);

            IOPort.DiscardOutBuffer();

            IOPort.Write(ElmoMotorOn, 0, ElmoMotorOn.Length);
            IOPort.Write(ElmoJV, 0, ElmoJV.Length);

            Array.Clear(TXBytes, 0, TXBytes.Length);
            TXBytes = UTF8Encoding.UTF8.GetBytes(jogVelocityCPS.ToString());
            TXBytesLength = Array.IndexOf(TXBytes, 0);
            IOPort.Write(TXBytes, 0, TXBytesLength);

            IOPort.Write(semicolon, 0, semicolon.Length);
            IOPort.Write(ElmoBegin, 0, ElmoBegin.Length);

            Thread.Sleep(100);
       
            return (true); //TODO P2 need to do error checking on .Write and Elmo response eventually and return false on error
        }

        public int stopMotor()
        {
            IOPort.DiscardOutBuffer();

            IOPort.Write(ElmoMotorOn, 0, ElmoMotorOn.Length);
            IOPort.Write(ElmoJV0, 0, ElmoJV0.Length);
            IOPort.Write(ElmoBegin, 0, ElmoBegin.Length);
            IOPort.Write(ElmoStop, 0, ElmoStop.Length);
            IOPort.Write(ElmoMotorOff, 0, ElmoMotorOff.Length);

            return (0);       
        }

        public void getStats()
        {
            getVelocity();
            Thread.Sleep(200);
            getActiveCurrent();
            Thread.Sleep(200);
            getReactiveCurrent();
            Thread.Sleep(200);
            getSR();
            Thread.Sleep(200);
            getCL1();
            Thread.Sleep(200);
            getPL1();
            Thread.Sleep(200);
            getPL2();
            Thread.Sleep(200);
            getAccDeacc();
        }

         private static byte[] getCL1Cmd = UTF8Encoding.UTF8.GetBytes("CL[1];");
        private int getCL1()
        {

            IOPort.DiscardOutBuffer();
            //EngrLogger.writeToColumns("Sending Elmo SR command");
            IOPort.Write(getCL1Cmd, 0, getCL1Cmd.Length);
            return (0);
        }

        private static byte[] getPL1Cmd = UTF8Encoding.UTF8.GetBytes("PL[1];");
        private int getPL1()
        {
            IOPort.DiscardOutBuffer();
            //EngrLogger.writeToColumns("Sending Elmo SR command");
            IOPort.Write(getPL1Cmd, 0, getPL1Cmd.Length);
            return (0);
        }

        private static byte[] getPL2Cmd = UTF8Encoding.UTF8.GetBytes("PL[2];");
        private int getPL2()
        {
            IOPort.DiscardOutBuffer();
            //EngrLogger.writeToColumns("Sending Elmo SR command");
            IOPort.Write(getPL2Cmd, 0, getPL2Cmd.Length);
            return (0);
        }

        private int getSR()
        {
            IOPort.DiscardOutBuffer();
            //EngrLogger.writeToColumns("Sending Elmo SR command");
            IOPort.Write(ElmoStatusRegister, 0, ElmoStatusRegister.Length);
            return (0);
        }

        private static byte[] activeCurrent = UTF8Encoding.UTF8.GetBytes("ID;");
        private int getActiveCurrent()
        {
            IOPort.DiscardOutBuffer();
            //EngrLogger.writeToColumns("Sending Elmo Active Current command");
            IOPort.Write(activeCurrent, 0, activeCurrent.Length);
            return (0);
        }

        private static byte[] reactiveCurrent = UTF8Encoding.UTF8.GetBytes("IQ;");
        private int getReactiveCurrent()
        {
            IOPort.DiscardOutBuffer();
            //EngrLogger.writeToColumns("Sending Elmo Reactive Current command");
            IOPort.Write(reactiveCurrent, 0, reactiveCurrent.Length);
            return (0);
        }

        private static byte[] velocity = UTF8Encoding.UTF8.GetBytes("VX;");
        private int getVelocity()
        {
            IOPort.DiscardOutBuffer();
            //EngrLogger.writeToColumns("Sending Elmo Main Encoder Velocity command");
            IOPort.Write(velocity, 0, velocity.Length);
            return (0);
        }

        private static byte[] getUM = UTF8Encoding.UTF8.GetBytes("UM;");
        public int getUnitMode()
        {
            IOPort.DiscardOutBuffer();
            IOPort.DiscardInBuffer();
            //EngrLogger.writeToColumns("Sending Elmo Main Encoder Velocity command");
            IOPort.Write(getUM, 0, getUM.Length);
            return (0);
        }

        private static byte[] setUMSpeed = UTF8Encoding.UTF8.GetBytes("UM = 2;");
        public int setUnitModeSpeed()
        {
            IOPort.DiscardOutBuffer();
            //EngrLogger.writeToColumns("Sending Elmo Main Encoder Velocity command");
            IOPort.Write(setUMSpeed, 0, setUMSpeed.Length);
            return (0);
        }

        private static byte[] getAcc = UTF8Encoding.UTF8.GetBytes("AC;");
        private static byte[] getDeacc = UTF8Encoding.UTF8.GetBytes("DC;");
        public int getAccDeacc()
        {
            IOPort.DiscardOutBuffer();
            EngrLogger.writeToColumns("getting Elmo acceleration limit");
            IOPort.Write(getAcc, 0, getAcc.Length);

            EngrLogger.writeToColumns("getting Elmo deacceleration limit");
            IOPort.Write(getDeacc, 0, getDeacc.Length);

            return (0);
        }

        private static byte[] cl1 = UTF8Encoding.UTF8.GetBytes("CL[1];");
        private static byte[] pl1 = UTF8Encoding.UTF8.GetBytes("PL[1];");
        private static byte[] pl2 = UTF8Encoding.UTF8.GetBytes("PL[2];");
        public int readCurrentLimits()
        {
            IOPort.DiscardOutBuffer();

            EngrLogger.writeToColumns("Sending Elmo CL[1] command");
            IOPort.Write(cl1, 0, cl1.Length);
            Thread.Sleep(100);

            EngrLogger.writeToColumns("Sending Elmo PL[1] command");
            IOPort.Write(pl1, 0, pl1.Length);
            Thread.Sleep(100);

            EngrLogger.writeToColumns("Sending Elmo PL[2] command");
            IOPort.Write(pl2, 0, pl2.Length);

            return (0);
        }

        private static byte[] cl1_47 = UTF8Encoding.UTF8.GetBytes("CL[1]=4.7;");
        public int setCurrentLimit47()
        {
            IOPort.DiscardOutBuffer();

            EngrLogger.writeToColumns("Sending Elmo CL[1]=4.7 command");
            IOPort.Write(cl1_47, 0, cl1_47.Length);

            return (0);
        }

        private const string curLimit65 = "CL[1]=6.5;";
        private static byte[] cl1_65 = UTF8Encoding.UTF8.GetBytes(curLimit65);
        public int setCurrentLimit65()
        {
            IOPort.DiscardOutBuffer();

            EngrLogger.writeToColumns("Sending Elmo CL[1]=6.5 command");
            IOPort.Write(cl1_65, 0, cl1_65.Length);

            return (0);
        }


        private static byte[] cl1_476 = UTF8Encoding.UTF8.GetBytes("CL[1]=4.76;");
        public int setCurrentLimit476()
        {
            IOPort.DiscardOutBuffer();

            EngrLogger.writeToColumns("Sending Elmo CL[1]=4.76 command");
            IOPort.Write(cl1_476, 0, cl1_476.Length);

            return (0);
        }

        private static byte[] pl1_135 = UTF8Encoding.UTF8.GetBytes("PL[1]=13.5;");
        private static byte[] pl2_2 = UTF8Encoding.UTF8.GetBytes("PL[2]=2;");
        public int setCurrentLimits476_135()
        {
            IOPort.DiscardOutBuffer();

            EngrLogger.writeToColumns("Sending Elmo CL[1]=4.76 command");
            IOPort.Write(cl1_476, 0, cl1_476.Length);

            Thread.Sleep(1000);

            EngrLogger.writeToColumns("Sending Elmo PL1[1]=13.5 command");
            IOPort.Write(pl1_135, 0, pl1_135.Length);

            Thread.Sleep(1000);

            EngrLogger.writeToColumns("Sending Elmo PL2[1]=2 command");
            IOPort.Write(pl2_2, 0, pl2_2.Length);

            return (0);
        }

        public void setCurrentAndAcLimits()
        {
            if (mb.QueryChannelPower(MotherBoard.ChannelNames.PumpMotorControllerElexChannel))
            {
                setAccDeacc1e9();
                setCurrentLimits476_135();
                getStats();
            }
            else
            {
                mb.EnableChannelPower(MotherBoard.ChannelNames.PumpMotorControllerElexChannel);
                Thread.Sleep(1000);
                setAccDeacc1e9();
                setCurrentLimits476_135();
                getStats();
                mb.DisableChannelPower(MotherBoard.ChannelNames.PumpMotorControllerElexChannel);
            }
        }

        private static byte[] snCmd = UTF8Encoding.UTF8.GetBytes("SN;");
        public int getSerialNumber()
        {
            IOPort.DiscardOutBuffer();
            //EngrLogger.writeToColumns("Sending Elmo Main Encoder Velocity command");
            IOPort.Write(snCmd, 0, snCmd.Length);
            return (0);
        }

        //TODO P0 need to make this value part of MissionConfig
        private StringBuilder sbTemp2 = new StringBuilder(64);
        private const string accelLimit = "1e9";
        public int setAccDeacc1e9()
        {
            IOPort.DiscardOutBuffer();

            sbTemp.Clear();
            sbTemp.Append("AC[1]=");
            sbTemp.Append(accelLimit);
            sbTemp.Append(";");
            IOPort.Write(Encoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length);

            sbTemp2.Clear();
            sbTemp2.Append("Setting Elmo Acceleration limit = ");
            sbTemp2.Append(sbTemp);
            EngrLogger.writeToColumns(sbTemp2); 
            
            sbTemp.Clear();
            sbTemp.Append("DC=");
            sbTemp.Append(accelLimit);
            sbTemp.Append(";");
            IOPort.Write(Encoding.UTF8.GetBytes(sbTemp.ToString()), 0, sbTemp.Length);
            
            sbTemp2.Clear();
            sbTemp2.Append("Setting Elmo Deacceleration limit = ");
            sbTemp2.Append(sbTemp);
            EngrLogger.writeToColumns(sbTemp2); 

            return (0);
        }


        public int sendEchoOff()
        {
            IOPort.DiscardOutBuffer();

            EngrLogger.writeToColumns("Sending Elmo Echo Off command");
            IOPort.Write(setEchoOff, 0, setEchoOff.Length);

            return (0);
        }

        public int sendEchoOn()
        {
            IOPort.DiscardOutBuffer();

            EngrLogger.writeToColumns("Sending Elmo Echo On command");
            IOPort.Write(setEchoOn, 0, setEchoOn.Length);

            return (0);
        }

        private static StringBuilder sbTemp = new StringBuilder(128);
        private void processMessage(QRecord qRecord)
        {
            sbTemp.Clear();
            sbTemp.Append("Processing Twitter message: ");
            for (int i = 0; i < qRecord.length; i++ )
            {
                if (qRecord.byteArray[i] > 127)
                    qRecord.byteArray[i] = 126;
            }
                sbTemp.Append(SafeEncoding.GetChars(qRecord.byteArray));
            EngrLogger.writeToColumns(0, EngrLiterals.ColumnNums.dateTime, qRecord.timeStamp,
                                          EngrLiterals.ColumnNums.state, qRecord.state,
                                          EngrLiterals.ColumnNums.comment, "Twitter msg",
                                          EngrLiterals.ColumnNums.pumpMotor, qRecord.byteArray);
        }
    }
}
