using System;
using Microsoft.SPOT;

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

namespace CPF
{
    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 static I2CDevice pthI2C = new I2CDevice(null);

        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 object pthLock = new object();

        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);

            //pthMessage.Clear();
            //pthMessage.Append(lps331P);
            //pthMessage.Append(pthData.LPS331Pressure);
            //pthMessage.Append(lps331T);
            //pthMessage.Append(pthData.LPS331Temperature);
            //pthMessage.Append(sht21H);
            //pthMessage.Append(pthData.SHT21Humidity);
            //pthMessage.Append(sht21T);
            //pthMessage.Append(pthData.SHT21Temperature);

            //Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
            //Array.Copy(UTF8Encoding.UTF8.GetBytes(pthMessage.ToString()), qS.byteArray, pthMessage.Length);
            //qS.qRecordType = StructQueue.QRecordType.PTH;
            //lock (pthLock) { Program.sQueue.Enqueue(qS); }
        }

        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

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

            pthI2C.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
            pthI2C.Execute(new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(setResolutionBytes) }, 1000);

            //Set device active
            pthI2C.Execute(new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(setActiveBytes) }, 1000);

            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;

            pthI2C.Config = configP;

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

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

            pthI2C.Execute(new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(setOneShot) }, 1000);

            //wait for measurement complete
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readCtrlReg2);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            pthI2C.Execute(xActions2Bytes, 1000);
            //tick();
            while (I2CReturnByte1[0] != 0)
            {
                pthI2C.Execute(xActions2Bytes, 1000);
            }
            //tock();

            Array.Clear(xActions2Bytes, 0, xActions2Bytes.Length);
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readIPXL);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            pthI2C.Execute(xActions2Bytes, 1000);
            pressure = I2CReturnByte1[0];

            Array.Clear(xActions2Bytes, 0, xActions2Bytes.Length);
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readIPL);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            pthI2C.Execute(xActions2Bytes, 1000);
            pressure = pressure + (I2CReturnByte1[0] << 8);

            Array.Clear(xActions2Bytes, 0, xActions2Bytes.Length);
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readIPH);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            pthI2C.Execute(xActions2Bytes, 1000);
            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);
            pthI2C.Execute(xActions2Bytes, 1000);
            temperature = I2CReturnByte1[0];

            Array.Clear(xActions2Bytes, 0, xActions2Bytes.Length);
            xActions2Bytes[0] = I2CDevice.CreateWriteTransaction(readTempH);
            xActions2Bytes[1] = I2CDevice.CreateReadTransaction(I2CReturnByte1);
            pthI2C.Execute(xActions2Bytes, 1000);
            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 double GetHumidity()
        {
            pthI2C.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
            pthI2C.Execute(xActionsTH, 1000);

            //RHBytes[0] is MSB
            //RHBytes[1] is LSB
            //RHBytes[2] is checksum

            double dec = ConvertMSBLSBToDouble(RHBytes[0], RHBytes[1]);

            double humidity = (125 * (dec / 65536)) - 6;

            HumidityReadCount++;

            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()
        {
            pthI2C.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
            pthI2C.Execute(xActionsTH, 1000);

            //SHT21TemperatureValueBytes[0] is MSB
            //SHT21TemperatureValueBytes[1] is LSB
            //SHT21TemperatureValueBytes[2] is checksum

            double dec = ConvertMSBLSBToDouble(SHT21TemperatureValueBytes[0], SHT21TemperatureValueBytes[1]);
            double SHT21Temperature = (175.72 * (dec / 65536)) - 46.85;

            TempReadCount++;

            return (SHT21Temperature);
        }

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

            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;

            Debug.Print("Elapsed time (mSec) = " + elapsedMilliseconds.ToString());
        }

    }
}
