using System;
using Microsoft.SPOT;

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

namespace SensorModules
{
    // This is a Singleton instantiation based on
    // C# in Depth, Third Edition Jon Skeet
    // http://csharpindepth.com/Articles/General/Singleton.aspx
    // "a singleton is a class which only allows a single instance of itself to be created, and usually gives simple access to that instance"
    public sealed class Radiometer1 : SatlanticOCR504
    {
        private Radiometer1()
            : base("Radiometer 1", "COM8", 9600, Parity.None, 8, StopBits.One, 5000, 20, false, 0)
        { }

        private static Radiometer1 instance;
        public static Radiometer1 Instance
        {
            get { return instance ?? (instance = new Radiometer1()); }
        }

    }

    public class SatlanticOCR504
    {
        private static StringBuilder sbTemp = new StringBuilder(256);

        private const int flbbPkt = 1;
        private const int flbbAve = 20;

        private ExarUart IOPort;

        private MotherBoard motherboard = MotherBoard.Instance;

        MessageQueue messageQ = MessageQueue.Instance;

        //TODO P1: Need to harmonize NativeUART and I2CSerialPort constructors

        public SatlanticOCR504(string id, string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int readTimeout, int writeTimeout, bool useSTX, byte STX)
        {
            IOPort = new ExarUart(id, QRecordType.OCR, processMessage, portName, baudRate, parity, dataBits, stopBits, readTimeout, writeTimeout, false, 0);
        }

        public void init()
        {
            if (!IOPort.IsOpen) IOPort.Open();
            
            //TODO P2 Do we really need both these 5 second sleeps?
            EngrLogger.writeToColumns("Powering down OCR504");
            motherboard.DisableChannelPower(MotherBoard.ChannelNames.RadiometerChannel);
            Thread.Sleep(5000);

            EngrLogger.writeToColumns("Powering up OCR504");
            motherboard.EnableChannelPower(MotherBoard.ChannelNames.RadiometerChannel);
            Thread.Sleep(5000);
            for (int i = 0; i < 5; i++)
            {
                messageQ.processQ();
                Thread.Sleep(500);
            }

            EngrLogger.Comment("Sending OCR504 ctrl-c");
            sendCtrlC();
            Thread.Sleep(1000);
            for (int i = 0; i < 5; i++)
            {
                messageQ.processQ();
                Thread.Sleep(500);
            }

            EngrLogger.Comment("Sending OCR504 frametype short");
            for (int i = 0; i < frametypeShort.Length; i++)
            {
                IOPort.WriteByte(frametypeShort[i]);
                Thread.Sleep(100);
            }
            for (int i = 0; i < 5; i++)
            {
                messageQ.processQ();
                Thread.Sleep(500);
            }

            EngrLogger.Comment("Sending OCR504 cal on");
            for (int i = 0; i < calOn.Length; i++)
            {
                IOPort.WriteByte(calOn[i]);
                Thread.Sleep(100);
            }
            for (int i = 0; i < 5; i++)
            {
                messageQ.processQ();
                Thread.Sleep(500);
            }

            EngrLogger.Comment("Sending OCR504 show all");
            for (int i = 0; i < showAll.Length; i++)
            {
                IOPort.WriteByte(showAll[i]);
                Thread.Sleep(100);
            }
            for (int i = 0; i < 5; i++)
            {
                messageQ.processQ();
                Thread.Sleep(500);
            }

            EngrLogger.Comment("Sending OCR504 exit");
            for (int i = 0; i < exit.Length; i++)
            {
                IOPort.WriteByte(exit[i]);
                Thread.Sleep(100);
            }
            for (int i = 0; i < 5; i++)
            {
                messageQ.processQ();
                Thread.Sleep(500);
            }

            byte[] yes = UTF8Encoding.UTF8.GetBytes("y\r\n");
            EngrLogger.Comment("Sending OCR504 y to save");
            for (int i = 0; i < yes.Length; i++)
            {
                IOPort.WriteByte(yes[i]);
                Thread.Sleep(100);
            }
            for (int i = 0; i < 5; i++)
            {
                messageQ.processQ();
                Thread.Sleep(500);
            }

            EngrLogger.Comment("Sending OCR504 poll sample");
            sendPollSample();
            Thread.Sleep(1000);
            for (int i = 0; i < 5; i++)
            {
                messageQ.processQ();
                Thread.Sleep(500);
            }
        }

        public void powerUp()
        {
            EngrLogger.writeToColumns("Powering up OCR504");
            motherboard.EnableChannelPower(MotherBoard.ChannelNames.RadiometerChannel);
        }

        public void powerDown()
        {
            EngrLogger.writeToColumns("Powering down OCR504");
            motherboard.DisableChannelPower(MotherBoard.ChannelNames.RadiometerChannel);
        }

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

        private static byte[] stopFreeRun = { 0x13 };
        private void sendStopFreeRun()
        {
            EngrLogger.writeToColumns("Sending radiometer stopFreeRun");
            IOPort.Write(stopFreeRun, 0, stopFreeRun.Length);
        }

        private static byte[] startCommandMode = { 0x03 };
        public void sendStartCommandMode()
        {
            EngrLogger.writeToColumns("Sending start command mode");
            IOPort.Write(startCommandMode, 0, startCommandMode.Length);
        }

        public static byte[] pollSample = { 0x20 };
        public void sendPollSample()
        {
            EngrLogger.writeToColumns("Sending radiometer pollSample");
            IOPort.Write(pollSample, 0, pollSample.Length);
        }

        private static byte[] autoTelemetryOff = UTF8Encoding.UTF8.GetBytes("set initat off\r\n");
        private void sendAutoTelemetryOff()
        {
            EngrLogger.writeToColumns("Sending radiometer autoTelemetryOff");
            IOPort.Write(autoTelemetryOff, 0, autoTelemetryOff.Length);
        }

        private static byte[] networkModeOff = UTF8Encoding.UTF8.GetBytes("set netmode off\r\n");
        private void sendNetworkModeOff()
        {
            EngrLogger.writeToColumns("Sending radiometer networkModeOff");
            IOPort.Write(networkModeOff, 0, networkModeOff.Length);
        }

        private static byte[] averageOff = UTF8Encoding.UTF8.GetBytes("set avg off\r\n");
        private void sendAverageOff()
        {
            EngrLogger.writeToColumns("Sending radiometer averageOff");
            IOPort.Write(averageOff, 0, averageOff.Length);
        }

        private static byte[] averageOn = UTF8Encoding.UTF8.GetBytes("set avg on\r\n");
        private void sendAverageOn()
        {
            EngrLogger.writeToColumns("Sending radiometer averageOn");
            IOPort.Write(averageOn, 0, averageOn.Length);
        }

        private static byte[] help = UTF8Encoding.UTF8.GetBytes("help\r\n");
        private void sendHelp()
        {
            EngrLogger.writeToColumns("Sending radiometer help");
            IOPort.Write(help, 0, help.Length);
        }

        private static byte[] frameRate1 = UTF8Encoding.UTF8.GetBytes("set framerate 1\r\n");
        private void sendFrameRate1()
        {
            EngrLogger.writeToColumns("Sending framerate 1");
            IOPort.Write(frameRate1, 0, frameRate1.Length);
        }

        private static byte[] frametypeShort = UTF8Encoding.UTF8.GetBytes("set frametype short\r\n");
        private void sendFrametypeShort()
        {
            EngrLogger.writeToColumns("Sending radiometer frametype short");
            IOPort.Write(frametypeShort, 0, frametypeShort.Length);
        }

        private static byte[] calOn = UTF8Encoding.UTF8.GetBytes("set usecal on\r\n");
        private void sendCalOn()
        {
            EngrLogger.writeToColumns("Sending radiometer use cal on");
            IOPort.Write(calOn, 0, calOn.Length);
        }

        private static byte[] calOff = UTF8Encoding.UTF8.GetBytes("set usecal off\r\n");
        private void sendCalOff()
        {
            EngrLogger.writeToColumns("Sending radiometer calOff");
            IOPort.Write(calOff, 0, calOff.Length);
        }

        int delay = 25;
        private static byte[] showAll = UTF8Encoding.UTF8.GetBytes("show all\r\n");
        public void sendShowAll()
        {
            EngrLogger.writeToColumns("Sending show all");
            IOPort.Write(showAll, 0, showAll.Length);
        }

        private static byte[] exit = UTF8Encoding.UTF8.GetBytes("exit\r\n");
        private void sendExit()
        {
            EngrLogger.writeToColumns("Sending exit");
            for (int i = 0; i <exit.Length; i++)
            {
                Thread.Sleep(delay);
                IOPort.WriteByte(showAll[i]);
            }
        }

        private static byte[] ctrlC = { 0x03 };
        private void sendCtrlC()
        {
            EngrLogger.writeToColumns("Sending ctrl-C character");
            IOPort.Write(ctrlC, 0, ctrlC.Length);
        }

        private static byte[] enter = { 0x0D };
        public void sendEnter()
        {
            EngrLogger.writeToColumns("Sending enter character");
            IOPort.Write(enter, 0, enter.Length);
        }

        private static StringBuilder sbTemp2 = new StringBuilder(128);
        private static readonly StringBuilder radiometerHeader = new StringBuilder("Radiometer Message");
        private static DateTime timeNow = new DateTime();

        public void processMessage(QRecord qRecord)
        {
            timeNow = DateTime.Now;
       
            if(SV.Pressure == double.NaN)
                EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, timeNow,
                                            EngrLiterals.ColumnNums.state, qRecord.state,
                                            EngrLiterals.ColumnNums.comment, radiometerHeader,
                                            EngrLiterals.ColumnNums.OCRText, qRecord.byteArray);
            else
                EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, timeNow,
                                            EngrLiterals.ColumnNums.state, qRecord.state,
                                            EngrLiterals.ColumnNums.comment, radiometerHeader,
                                            EngrLiterals.ColumnNums.CTDSalinity, SV.Pressure,
                                            EngrLiterals.ColumnNums.OCRText, qRecord.byteArray);

            
            if (SciLogger.sampling(SciLogger.Instruments.Radiometer) && !SciLogger.posted(SciLogger.Instruments.Radiometer))
            {
                SciLogger.postSample(SciLogger.Instruments.Radiometer, qRecord.byteArray);
                sbTemp.Clear();
                sbTemp.Append("Radiometer post: ");
                sbTemp.Append(SafeEncoding.GetChars(qRecord.byteArray));
                EngrLogger.writeToColumns(sbTemp);
            }

        }

    }
}
