using System;
using Microsoft.SPOT;

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

using Microsoft.SPOT.Hardware;

namespace miniCPF
{
    class Optode
    {
        public static SerialPort optodePort = new SerialPort("COM2", 9600, Parity.None, 8, StopBits.One);

        private const string wakeUpCmd = "\r";
        private const string getAllCmd = "get all\r\n";
        private const string doSampleCmd = "Do Sample\r\n";

        private static byte[] txBytes = new byte[64];

        private static byte ETX = 10;            //End of text = LF

        private static int msgIndex = 0;
        private static int numPortBytes = 0;

        private static object optodeLock = new object();

        private static StructQueue.qStruct qS = new StructQueue.qStruct();

        //MEASUREMENT	4330	1532	
                  //   O2Concentration[uM]	2.644894E+02	AirSaturation[%]	9.722411E+01	
                  //    Temperature[Deg.C]	2.217678E+01	CalPhase[Deg]	    2.919595E+01	
                  //    TCPhase[Deg]	    2.919595E+01    C1RPh[Deg]	        3.770208E+01	
                  //    C2RPh[Deg]	        8.506126E+00	C1Amp[mV]	        8.645895E+02	
                  //    C2Amp[mV]	        9.500998E+02	RawTemp[mV]	        1.244538E+02

        public enum OptodeSampleFields : int
        {
            O2Conc = 0,
            AirSat,
            TOpt,
            CalPhase,
            TCPhase,
            C1RPh,
            C2RPh,
            C1Amp,
            C2Amp,
            RawTemp
        }
            

        public static void init()
        {
            qS.byteArray = new byte[StructQueue.qRecordWidth];

            optodePort.ReadTimeout = 5000;
            optodePort.Open();

            optodePort.DiscardInBuffer();
            optodePort.DiscardOutBuffer();

            wakeUp();
            getAll();

            optodePort.DataReceived += new SerialDataReceivedEventHandler(optodeDataReceived);
            optodePort.DiscardInBuffer();
            optodePort.DiscardOutBuffer();
        }

        public static void wakeUp()
        {
            //Debug.Print("Sending Wake Up");
            Array.Clear(txBytes, 0, txBytes.Length);
            txBytes = Encoding.UTF8.GetBytes(wakeUpCmd);
            optodePort.Write(txBytes, 0, wakeUpCmd.Length);
        }

        private static bool endGetAll = false;
        private static int lineNum = 0;
        private static StringBuilder sbTemp = new StringBuilder(4096);
        private static byte[] getAllPortBytes = new byte[4096];
        private static byte[] getAllMsgBytes = new byte[4096];

        public static void getAll()
        {
            optodePort.DiscardInBuffer();
            optodePort.DiscardOutBuffer();

            //Debug.Print("Sending Get All");
            Array.Clear(txBytes, 0, txBytes.Length);
            txBytes = Encoding.UTF8.GetBytes(getAllCmd);
            optodePort.Write(txBytes, 0, getAllCmd.Length);
            Thread.Sleep(1000);

            sbTemp.Clear();
            Array.Clear(getAllMsgBytes, 0, msgBytes.Length);
            Array.Clear(getAllPortBytes, 0, portBytes.Length);
            msgIndex = 0;

            endGetAll = false;
            while (!endGetAll)
            {
                numPortBytes = optodePort.BytesToRead;
                try
                {
                    optodePort.Read(getAllPortBytes, 0, numPortBytes);
                }
                catch
                {
                    Debug.Print("Optode Port Get All Read Exception");          //Probably should do something smarter here        
                }

                for (int i = 0; i < numPortBytes; i++)
                {
                    if (getAllPortBytes[i] != ETX)
                    {
                        if ((msgIndex == 0) && (getAllPortBytes[i] == '#'))
                        {
                            endGetAll = true;
                        }
                        else
                        {
                            getAllMsgBytes[msgIndex] = getAllPortBytes[i];
                            msgIndex = msgIndex + 1;
                        }
                    }
                    else
                    {
                        sbTemp.Append(UTF8Encoding.UTF8.GetChars(getAllMsgBytes, 0, (msgIndex - 1)));
                        lock (optodeLock) { EngrLogger.write(EngrLogger.ColumnNums.comment, EngrLogger.getPrefix(sbTemp)); }
                        sbTemp.Clear();
                        msgIndex = 0;
                        lineNum = lineNum + 1;
                    }
                }
            }
            optodePort.DiscardInBuffer();
            optodePort.DiscardOutBuffer();
        }

        private static DateTime timeStamp;
        private static Program.CPFStates cpfState;

        public static void doSample()
        {
            //Debug.Print("Sending Do Sample");
            Array.Clear(txBytes, 0, txBytes.Length);
            txBytes = Encoding.UTF8.GetBytes(doSampleCmd);
            optodePort.Write(txBytes, 0, doSampleCmd.Length);
            timeStamp = DateTime.Now;
            cpfState = Program.CPFState;
        }

        private static byte[] portBytes = new byte[512];
        private static byte[] msgBytes = new byte[512];

        private static void optodeDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            numPortBytes = optodePort.BytesToRead;

            try
            {
                optodePort.Read(portBytes, 0, numPortBytes);
            }
            catch
            {
                Debug.Print("Optode Port Read Exception");          //Probably should do something smarter here        
            }

            for (int i = 0; i < numPortBytes; i++)
            {
                if (portBytes[i] != 35)
                {
                  
                        msgBytes[msgIndex] = portBytes[i];
                        msgIndex = msgIndex + 1;
                }
                else
                {
                    Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                    Array.Copy(msgBytes, qS.byteArray, (msgIndex - 1));
                    qS.timeStamp = timeStamp;
                    qS.state = cpfState;
                    qS.qDel = qDelegate;
                    lock (optodeLock) { Program.sQ.Enqueue(qS); }

                    msgIndex = 0;
                }
            }
        }

        private static int startIndex = 0;
        private static int endIndex = 0;
        private static int measurementIndex = 0;
        private static byte[] valueBytes = new byte[64];
        private static byte[] inByteArray = new byte[512];
        private static double[] optodeMeasurements = new double[16];
 
        private static void parseOptodeSample(byte[] inByteArray)
        {
            measurementIndex = 0;
            startIndex = Array.IndexOf(inByteArray, '\t');
            Array.Clear(optodeMeasurements, 0, optodeMeasurements.Length);
            for (int i = 0; i < 22; i++)
            {
                if (i == 21)
                    endIndex = Array.IndexOf(inByteArray, 0);
                else
                    endIndex = Array.IndexOf(inByteArray, '\t', startIndex + 1);

                if ((i == 3) || (i == 5) || (i == 7) || (i == 9) || (i == 11) || (i == 13) || (i == 15) ||
                    (i == 17) || (i == 19) || (i == 21))
                {
                    Array.Clear(valueBytes, 0, valueBytes.Length);

                    //The last field will have a /r in it and we need to get rid of it before Parsing
                    if(i != 21)
                        Array.Copy(inByteArray, startIndex + 1, valueBytes, 0, endIndex - startIndex - 1);
                    else
                        Array.Copy(inByteArray, startIndex + 1, valueBytes, 0, endIndex - startIndex - 2);

                    sbTemp.Clear();
                    sbTemp.Append(UTF8Encoding.UTF8.GetChars(valueBytes));
                    optodeMeasurements[measurementIndex] = double.Parse(sbTemp.ToString());
                    measurementIndex = measurementIndex + 1;
                }
                startIndex = endIndex;
            }
        }

        private static StringBuilder sbLogMsg = new StringBuilder(StructQueue.qRecordWidth);
        private static StringBuilder sbLogPrefix = new StringBuilder(StructQueue.qRecordWidth);
        private static readonly StringBuilder optodeComment = new StringBuilder("O2 Msg");

        private static void qDelegate(StructQueue.qStruct inQStruct)
        {
            sbTemp.Clear();
            sbTemp.Append(UTF8Encoding.UTF8.GetChars(inQStruct.byteArray));
            Debug.Print(sbTemp.ToString());

            parseOptodeSample(inQStruct.byteArray);
            
            sbLogMsg.Clear();
            sbLogPrefix.Clear();

            sbLogMsg = EngrLogger.formatRecord( EngrLogger.ColumnNums.comment, EngrLogger.getPrefix(inQStruct.timeStamp, inQStruct.state, optodeComment),
                                                EngrLogger.ColumnNums.TOpt, optodeMeasurements[(int)OptodeSampleFields.TOpt],
                                                EngrLogger.ColumnNums.TPhase, optodeMeasurements[(int)OptodeSampleFields.TCPhase],
                                                EngrLogger.ColumnNums.RPhase, optodeMeasurements[(int)OptodeSampleFields.C2RPh]);

            Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
            Array.Copy(UTF8Encoding.UTF8.GetBytes(sbLogMsg.ToString()), qS.byteArray, sbLogMsg.Length);
            qS.timeStamp = timeStamp;
            qS.state = cpfState;
            qS.qDel = EngrLogger.qDelegate;
            lock (optodeLock) { Program.sQ.Enqueue(qS); }
        }
    }
}
