using System;
using Microsoft.SPOT;

using Microsoft.SPOT.Hardware;

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

using GHI.Premium.System;

namespace miniCPF
{
    class uBloxGPS
    {
        private static bool stopGPS = false;
        private static bool GPSStarted = false;

        private static byte[] portReadBytes = new byte[10240];
        private static byte[] portInByteArray = new byte[256];
        private static int portInByteArrayIndex = 0;
        private static int portBytesToRead = 0;

        private static byte[] latByteArray = new byte[16];
        private static byte[] lonByteArray = new byte[16];

        private static int latCopyStartIndex;
        private static int latCopyLength;
        private static int lonCopyStartIndex;
        private static int lonCopyLength;

        private static byte[] RMCMessage = new byte[256];

        private static int tokenCounter = 0;
        private static int index = 0;
        private static int hour = 1;
        private static int minute = 0;
        private static int second = 0;
        private static int year = 0;
        private static int month = 0;
        private static int day = 0;

        private static SerialPort GPSPort = new SerialPort("COM1", 9600, Parity.None);

        private static object uBloxLock = new object();

        private static StringBuilder sb = new StringBuilder(256);

        public static void start()
        {
            if (!GPSStarted)
            {
                GPSPort.Open();
                GPSPort.DataReceived += new SerialDataReceivedEventHandler(GPSPortDataReceived);
                GPSStarted = true;
            }
                GPSPort.DiscardInBuffer();
                GPSPort.DiscardOutBuffer();
        }

        public static void stop()
        {
            stopGPS = true;
        }

        public static void close()
        {
            GPSPort.Close();
        }

        private static void GPSPortDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            portBytesToRead = GPSPort.BytesToRead;
            try
            {
                GPSPort.Read(portReadBytes, 0, portBytesToRead);
            }
            catch
            {
                Debug.Print("GPS Read Exception");          //TODO do something smarter here
            }

            for (int i = 0; i < portBytesToRead; i++)
            {
                portInByteArray[portInByteArrayIndex] = portReadBytes[i];
                if (portInByteArrayIndex == 0)    //Check to make sure first character is = $ (NMEA standard start of string)
                {
                    if (portInByteArray[0] == 36)  //only move on to byte index 1 after we get a $
                    {
                        portInByteArrayIndex = portInByteArrayIndex + 1;
                    }
                }
                else
                {
                    if (portInByteArray[portInByteArrayIndex] == 10)
                    {
                        parseNMEA(portInByteArray);
                        portInByteArrayIndex = 0;
                        Array.Clear(portInByteArray, 0, portInByteArray.Length);
                    }
                    else
                    {
                        portInByteArrayIndex = portInByteArrayIndex + 1;
                    }
                }
            }
            if (stopGPS)
            {
                GPSPort.DiscardInBuffer();          //TODO This doesn't seem to work 100% of the time, still get exceptions once in a while
                GPSPort.Close();
            }
        }

        private static void parseNMEA(byte[] NMEAByteArray)
        {

            if (!Program.timeSynched)
            {
                if ((NMEAByteArray[1] == (byte)'G') && (NMEAByteArray[2] == (byte)'P') && (NMEAByteArray[3] == (byte)'R') &&      //TODO Probably need a more robust way to do this
                    (NMEAByteArray[4] == (byte)'M') && (NMEAByteArray[5] == (byte)'C'))
                {
                    uBloxGPS.parseRMC(NMEAByteArray);
                }
            }
        }

        private static void parseRMC(byte[] RMCByteArray)
        {
            tokenCounter = 0;

            for (int i = 0; i < 80; i++)
            {
                if (RMCByteArray[i] == 44)  //The NMEA separator is a comma (',') = ASCII 44
                {
                    tokenCounter = tokenCounter + 1;    //Time is the first field after $GPRMC hhmmss.sss
                    if (tokenCounter == 1)
                    {
                        hour = (RMCByteArray[i + 1] - 48) * 10 + (RMCByteArray[i + 2] - 48);
                        minute = (RMCByteArray[i + 3] - 48) * 10 + (RMCByteArray[i + 4] - 48);
                        second = (RMCByteArray[i + 5] - 48) * 10 + (RMCByteArray[i + 6] - 48);
                    }

                    if (tokenCounter == 3)
                    {
                        latCopyStartIndex = i + 1;
                    }

                    if (tokenCounter == 5)
                    {
                        latCopyLength = i - latCopyStartIndex;
                        lonCopyStartIndex = i + 1;
                    }

                    if (tokenCounter == 7)
                    {
                        lonCopyLength = i - lonCopyStartIndex;
                        Array.Clear(latByteArray, 0, latByteArray.Length);
                        Array.Copy(RMCByteArray, latCopyStartIndex, latByteArray, 0, latCopyLength);
                        Array.Clear(lonByteArray, 0, lonByteArray.Length);
                        Array.Copy(RMCByteArray, lonCopyStartIndex, lonByteArray, 0, lonCopyLength);
                    }

                    if (tokenCounter == 9)      //Date is the 9th field  ddmmyy
                    {
                        day = (RMCByteArray[i + 1] - 48) * 10 + (RMCByteArray[i + 2] - 48);
                        month = (RMCByteArray[i + 3] - 48) * 10 + (RMCByteArray[i + 4] - 48);
                        year = 2000 + (RMCByteArray[i + 5] - 48) * 10 + (RMCByteArray[i + 6] - 48);
                        CPFUtilities.synchTime(year, month, day, hour, minute, second);
                    }
                }
            }

            //CPFUtilities.tick();

            buildRMCLogMessage();

            //CPFUtilities.tock();
        }

        //TODO Decide if we really need this method
        private static byte[] buildRMCLogMessage()
        {
            Array.Clear(RMCMessage, 0, RMCMessage.Length);
            //RMCMessage = Logger.getNewLogArrayHeader(UTF8Encoding.UTF8.GetBytes("RMC message for Time Synch"));
            sb.Clear();
            sb.Append(UTF8Encoding.UTF8.GetChars(RMCMessage));
            Debug.Print(sb.ToString());

            tokenCounter = 0;
            index = Array.IndexOf(RMCMessage, 0);
            for (int i = 0; i < (int)Logger.ColumnNums.lastColumn; i++)
            {
               // RMCMessage[index] = Logger.token;
                index = index + 1;
                if (tokenCounter == (int)Logger.ColumnNums.lat)
                {
                    Array.Copy(latByteArray, 0, RMCMessage, index, (Array.IndexOf(latByteArray, 0) + 1));
                    sb.Clear();
                    sb.Append(UTF8Encoding.UTF8.GetChars(RMCMessage));
                    Debug.Print(sb.ToString());
                    index = index + Array.IndexOf(latByteArray, 0);
                }
                if (tokenCounter == (int)Logger.ColumnNums.lon)
                {
                    Array.Copy(lonByteArray, 0, RMCMessage, index, (Array.IndexOf(lonByteArray, 0) + 1));
                    sb.Clear();
                    sb.Append(UTF8Encoding.UTF8.GetChars(RMCMessage));
                    Debug.Print(sb.ToString());
                    index = index = Array.IndexOf(lonByteArray, 0);
                }
                tokenCounter = tokenCounter + 1;
            }
            return RMCMessage;
        }
    }
}
