using System;
using Microsoft.SPOT;

using Microsoft.SPOT.Hardware;
using System.Threading;
using System.Text;

namespace CPF
{
    public static class PTH
    {

        private static I2CDevice.Configuration configTH = new I2CDevice.Configuration(0x40, 400);  //device address is 0x40 or 1000000 (7 bit address)
        private static I2CDevice.Configuration configP = new I2CDevice.Configuration(0x5D, 400);

        private struct LPS331Data
        {
            public double pressure;
            public double temperature;
        }
        private static LPS331Data lps331Data;

        public struct PTHData
        {
            public double LPS331Pressure;
            public double LPS331Temperature;
            public double SHT21Humidity;
            public double SHT21Temperature;
        }
        private static PTHData pthData;

        private static StructQueue.qStruct qS = new StructQueue.qStruct();
        private static StringBuilder pthMessage = new StringBuilder(128);
        private static StringBuilder lps331P = new StringBuilder("LPS331 P,");
        private static StringBuilder lps331T = new StringBuilder("LPS331 T,");
        private static StringBuilder sht21H = new StringBuilder("SHT21 H,");
        private static StringBuilder sht21T = new StringBuilder("SHT21 T");
        private static StringBuilder sbTemp = new StringBuilder(128);

        public static PTHData getPTH()
        {
            //qS.timeStamp = DateTime.Now;
            //qS.state = Program.CPFState;

            pthData.SHT21Humidity = GetHumidity();
            pthData.SHT21Temperature = GetSHT21Temperature();
            lps331Data = ReadLPS331();
            pthData.LPS331Temperature = lps331Data.temperature;
            pthData.LPS331Pressure = lps331Data.pressure;

            return (pthData);
        }

        private static byte[] ipSetResolution = new byte[2] { 0x10, 0x6A }; //0xE5 or 11100101 is the "read relative humidity" command
        private static byte[] ipSetActive = new byte[2] { 0x20, 0x80 };     //0xE5 or 11100101 is the "read relative humidity" command

        static PTH()
        {
            overHumidityError.criticalErrorHandler += new ErrorHandler.CriticalErrorHandler(ErrorHandler.setRecoveryMode);
        }

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

            Program.g400I2C.Config = configP;

            byte[] setActiveBytes = new byte[] { 0x20, 0x84 };
            //byte[] setResolutionBytes = new byte[] { 0x10, 0x7A }; //highest resolution
            byte[] setResolutionBytes = new byte[] { 0x10, 0x00 }; //lowest resolution

            //Set resolution
            if (Program.g400I2C.Execute(new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(setResolutionBytes) }, 1000) == 0)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C set resolution transaction");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS); 
            }

            //Set device active
            if (Program.g400I2C.Execute(new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(setActiveBytes) }, 1000) == 0)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C set active bytes transaction");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS); 
            }

            pthData.LPS331Pressure = double.NaN;
            pthData.LPS331Temperature = double.NaN;
            pthData.SHT21Humidity = double.NaN;
            pthData.SHT21Temperature = double.NaN;
        }

        private static byte[] readIPXL = new byte[1] { 0x28 };//read the least significant byte = pressure_out_xl register
        private static byte[] readIPL = new byte[1] { 0x29 };//read the middle significant byte = pressure_out_l register
        private static byte[] readIPH = new byte[1] { 0x2A };//read the most significant byte = pressure_out_xl register
        private static byte[] readTempL = new byte[1] { 0x2B };
        private static byte[] readTempH = new byte[1] { 0x2C };
        private static byte[] readCtrlReg2 = new byte[1] { 0x21 };
        private static byte[] I2CReturnByte1 = new byte[1];
        private static I2CDevice.I2CTransaction[] xActions2Bytes = new I2CDevice.I2CTransaction[2];
        private static int InternalPressureReadCount = 0;

        private static LPS331Data ReadLPS331()
        {
            System.Int32 pressure = 0;
            System.Int16 temperature = 0;

            LPS331Data data;

            Program.g400I2C.Config = configP;

            data.pressure = double.NaN;
            data.temperature = double.NaN;

            byte[] setOneShot = new byte[] { 0x21, 0x01 };

            if (Program.g400I2C.Execute(new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(setOneShot) }, 1000) == 0)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C set one shot transaction");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS); 
            }

            //wait for measurement complete
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readCtrlReg2);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            //tick();

            int j = 0;
            Program.g400I2C.Execute(xActions2Bytes, 1000);
            for (j = 0; j < 1000; j++)
            {
                if (Program.g400I2C.Execute(xActions2Bytes, 1000) != 0)
                    break;
                else
                    Thread.Sleep(1);
            }
            if (j >= 1000)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C pressure measurement transaction timed out");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS); 
                pressure = 0;
            }
            //tock();

            Array.Clear(xActions2Bytes, 0, xActions2Bytes.Length);
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readIPXL);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            if (Program.g400I2C.Execute(xActions2Bytes, 1000) == 0)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C pressure IPXL transaction");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS); 
                pressure = 0;
            }
            else
                pressure = I2CReturnByte1[0];

            Array.Clear(xActions2Bytes, 0, xActions2Bytes.Length);
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readIPL);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            if (Program.g400I2C.Execute(xActions2Bytes, 1000) == 0)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C pressure IPL transaction");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS); 
                pressure = 0;
            }
            else
                pressure = pressure + (I2CReturnByte1[0] << 8);

            Array.Clear(xActions2Bytes, 0, xActions2Bytes.Length);
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readIPH);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            if (Program.g400I2C.Execute(xActions2Bytes, 1000) == 0)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C pressure IPH transaction");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS); 
                pressure = 0;
            }
            else
            {
                pressure = pressure + (I2CReturnByte1[0] << 16);
                data.pressure = pressure / 4096.0;
            }

            Array.Clear(xActions2Bytes, 0, xActions2Bytes.Length);
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readTempL);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            if (Program.g400I2C.Execute(xActions2Bytes, 1000) == 0)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C tempL transaction");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS); 
                temperature = 0;
            }
            temperature = I2CReturnByte1[0];

            Array.Clear(xActions2Bytes, 0, xActions2Bytes.Length);
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readTempH);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            if (Program.g400I2C.Execute(xActions2Bytes, 1000) == 0)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C tempH transaction");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS); 
                temperature = 0;
            }
            else
            {
                temperature = (System.Int16)(temperature + (I2CReturnByte1[0]) * 256);
                data.temperature = 42.5 + (temperature / 480.0);
            }

            InternalPressureReadCount++;

            return (data);
        }

        private static byte[] readRH = new byte[1] { 0xE5 };//0xE5 or 11100101 is the "read relative humidity" command
        private static byte[] RHBytes = new byte[3];
        private static int HumidityReadCount = 0;

        // setup the transactions
        private static I2CDevice.I2CTransaction[] xActionsTH = new I2CDevice.I2CTransaction[2];

        private static TimeSpan overHumidityErrorTS = new TimeSpan(1, 0, 0);
        private static StringBuilder overHumidityLabel = new StringBuilder("Over Humidity Error");
        private static ErrorHandler overHumidityError = new ErrorHandler(3, overHumidityErrorTS, overHumidityLabel, ErrorHandler.ErrorType.critical);
        private const double maxHumidity = 65.0;

        private static double GetHumidity()
        {
            double dec = double.NaN;
            double humidity = double.NaN;

            Program.g400I2C.Config = configTH;
            // create write buffer, send "read humidity" command
            xActionsTH[0] = I2CDevice.CreateWriteTransaction(readRH);

            // create read buffer to read the returned data
            xActionsTH[1] = I2CDevice.CreateReadTransaction(RHBytes);

            //run the write/ read
            if (Program.g400I2C.Execute(xActionsTH, 1000) == 0)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C get humidity TH transaction");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS);
                humidity = double.NaN;
            }
            else
            {
                //RHBytes[0] is MSB
                //RHBytes[1] is LSB
                //RHBytes[2] is checksum

                dec = ConvertMSBLSBToDouble(RHBytes[0], RHBytes[1]);
                humidity = (125 * (dec / 65536)) - 6;
                HumidityReadCount++;
                if (humidity > maxHumidity)
                    overHumidityError.logError();
            }
            return (humidity);
        }

        private static byte[] CommandReadSHT21Temperature = new byte[1] { 0xE3 };//0xE3 or 11100011 is the "read temperature" command
        private static byte[] SHT21TemperatureValueBytes = new byte[3];
        private static int TempReadCount = 0;

        private static double GetSHT21Temperature()
        {
            double dec = double.NaN;
            double SHT21Temperature = double.NaN;

            Program.g400I2C.Config = configTH;
            // create write buffer (we need one byte)
            xActionsTH[0] = I2CDevice.CreateWriteTransaction(CommandReadSHT21Temperature);

            // create read buffer to read the returned data
            xActionsTH[1] = I2CDevice.CreateReadTransaction(SHT21TemperatureValueBytes);

            //run the write/ read
            if (Program.g400I2C.Execute(xActionsTH, 1000) == 0)
            {
                sbTemp.Clear();
                sbTemp.Append("ERROR: PTH I2C SHT21 Temperature TH transaction");
                Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
                qS.qRecordType = StructQueue.QRecordType.PTH;
                qS.errorCode = -1;
                Program.sQueue.Enqueue(qS); 
                SHT21Temperature = double.NaN;
            }
            else
            {
                //SHT21TemperatureValueBytes[0] is MSB
                //SHT21TemperatureValueBytes[1] is LSB
                //SHT21TemperatureValueBytes[2] is checksum
                dec = ConvertMSBLSBToDouble(SHT21TemperatureValueBytes[0], SHT21TemperatureValueBytes[1]);
                SHT21Temperature = (175.72 * (dec / 65536)) - 46.85;

                TempReadCount++;
            }

            return (SHT21Temperature);
        }

        private static double ConvertMSBLSBToDouble(byte MSB, byte LSB)
        {
            double dbl = ((double)MSB + ((double)LSB / 256.0)) * 256.0;

            return (dbl);
        }

        private static TimeSpan tic = new TimeSpan();

        public static void tick()
        {
            tic = Utility.GetMachineTime(); ;
        }

        private static TimeSpan toc = new TimeSpan();
        private static TimeSpan timeDiff = new TimeSpan();
        private static int elapsedMilliseconds;

        public static void tock()
        {
            toc = Utility.GetMachineTime();

            timeDiff = toc - tic;
            elapsedMilliseconds = (timeDiff.Days * 24 * 3600 * 1000) + (timeDiff.Hours * 3600 * 1000) + (timeDiff.Seconds * 1000) + timeDiff.Milliseconds;

            sbTemp.Clear();
            sbTemp.Append("Elapsed time (mSec) = ");
            sbTemp.Append(elapsedMilliseconds.ToString());
            Debug.Print(sbTemp.ToString());
        }

    }
}
