using System;
using Microsoft.SPOT;

using Microsoft.SPOT.Hardware;

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

using GHI.Premium.System;

namespace CPF
{
    class ADuC
    {
        private static byte[] ADuCPortReadBytes = new byte[10240];
        private static SerialPort ADuCPort = new SerialPort("COM2", 9600, Parity.None);
        private static int ADuCPortBytesToRead = 0;
        private static byte[] ADuCPortInByteArray = new byte[256];
        private static int ADuCPortInByteArrayIndex = 0;

        private static object ADuCLock = new object();

        private static byte STX = 36;            //Start of text = "$"
        private static byte ETX = 10;            //End of text = LF
        private static byte negativeSign = 45;     //negative sign
        private static byte decimalPt = 46;     //decimal point
        private static byte CR = 13;
        private static byte token = 32;         //token (separator) = space

        private static int messageNum = 0;

        private static StringBuilder sb = new StringBuilder(256);

        private static byte[] parserField = new byte[16];
        private static int parserI = 0;
        private static int parserJ = 0;
        private static int decimalPtIndex = -1;

        private static double[] aducVolts = new double[12];
        private static int fieldNum = 0;
        private static double accum = 0;
        private static int lastByte = 0;
        private static int fieldSign = 1;

        public static void Init()
        {
            ADuCPort.Open();
            ADuCPort.DiscardInBuffer();
            ADuCPort.DiscardOutBuffer();
            ADuCPort.DataReceived += new SerialDataReceivedEventHandler(ADuCPortDataReceived);
        }

        private static void ADuCPortDataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            ADuCPortBytesToRead = ADuCPort.BytesToRead;

            try
            {
                ADuCPort.Read(ADuCPortReadBytes, 0, ADuCPortBytesToRead);
                //charArray = UTF8Encoding.UTF8.GetChars(ADuCPortReadBytes);
            }
            catch
            {
                Debug.Print("ADuC Read Exception");          //TODO do something smarter here
            }

            for (int i = 0; i < ADuCPortBytesToRead; i++)
            {
                ADuCPortInByteArray[ADuCPortInByteArrayIndex] = ADuCPortReadBytes[i];
                if (ADuCPortInByteArrayIndex == 0)    //Check to make sure first character is = LF
                {
                    if (ADuCPortInByteArray[0] == STX)  //only move on to byte index 1 after we get a LF
                    {
                        ADuCPortInByteArrayIndex = ADuCPortInByteArrayIndex + 1;
                    }
                }
                else
                {
                    if (ADuCPortInByteArray[ADuCPortInByteArrayIndex] == ETX)
                    {
                        ADuCPortInByteArrayIndex = 0;
                        messageNum = messageNum + 1;

                        if ((messageNum % configFile.aducEnqueueDecimator) == 0)
                        {
                            lock (ADuCLock) { Program.MessageQueue.Enqueue(ADuCPortInByteArray); }

                            sb.Clear();
                            sb.Append(UTF8Encoding.UTF8.GetChars(ADuCPortInByteArray));
                            Debug.Print(sb.ToString());
                        }

                        Array.Clear(ADuCPortInByteArray, 0, ADuCPortInByteArray.Length);
                        Array.Clear(ADuCPortReadBytes, 0, ADuCPortReadBytes.Length);
                    }
                    else
                    {
                        ADuCPortInByteArrayIndex = ADuCPortInByteArrayIndex + 1;
                    }
                }
            }
        }

        public static double[] parseADuC(byte[] inArray)
        {
            parserJ = -1;
            fieldNum = 0;
            fieldSign = 1;

            for (parserI = 0; parserI < inArray.Length; parserI++)
            {
                //Find the index of the first token
                if (parserJ < 0)
                {
                    if (inArray[parserI] == token)
                    {
                        parserJ = parserJ + 1;
                    }
                }
                else
                {
                    //When the second token shows up, turn the bytes into a number
                    if ((inArray[parserI] == token) || (inArray[parserI] == CR))
                    {
                        accum = 0.0;
                        lastByte = parserJ;

                        //Handle fields that don't have any decimal points
                        if (decimalPtIndex < 0)
                        {
                            decimalPtIndex = lastByte - 1;
                        }

                        //Turn the field into a number
                        for (parserJ = 0; parserJ < lastByte; parserJ++)
                        {
                            accum = accum + ((parserField[parserJ] - 48) * System.Math.Pow(10, (decimalPtIndex - parserJ)));
                        }
                        accum = fieldSign * accum;
                        aducVolts[fieldNum] = accum;
                        //Debug.Print("Voltage " + fieldNum.ToString() + " = " + aducVolts[fieldNum].ToString("F5"));

                        //End the loop at the CR
                        if (inArray[parserI] == CR)
                        {
                            parserI = inArray.Length;
                        }
                        else
                        {
                            fieldSign = 1;
                            parserJ = -1;
                            fieldNum = fieldNum + 1;
                            decimalPtIndex = -1;
                            parserI = parserI - 1;          //Need to do this so we don't skip the start of the next field
                        }
                    }
                    else   // put the number in the field unless it is a decimal point or a negative sign
                    {
                        parserField[parserJ] = inArray[parserI];

                        //Look for the decimal point
                        if (inArray[parserI] == decimalPt)
                        {
                            decimalPtIndex = parserJ - 1;
                        }
                        else if (inArray[parserI] == negativeSign)
                        {
                            fieldSign = -1;
                        }
                        else
                        {
                            parserJ = parserJ + 1;
                        }
                    }
                }
            }
            //double battVolts = aducVolts[1] * configFile.aducCH1Scale;
            //Debug.Print("Parsed volts =                         " + 
            //                            aducVolts[0].ToString("f2") + " " +
            //                            aducVolts[1].ToString("f4") + " " +
            //                            aducVolts[2].ToString("f4") + " " +
            //                            aducVolts[3].ToString("f4") + " " +
            //                            aducVolts[4].ToString("f4") + "   batt voltage = " + 
            //                            battVolts.ToString("F2"));
            return (aducVolts);
        }
    }
}
