using System;
using System.IO.Ports;
using System.Text;
using System.Threading;
using SensorModules;
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 FLBB1 : WetlabsFLBB2K
    {
        private FLBB1()
            : base("FLBB1", "COM9", 19200, Parity.None, 8, StopBits.One, 5000, 20, true, (byte)'/')
        { }

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

    }

    public class WetlabsFLBB2K
    {
        HWModules.MotherBoard motherboard = MotherBoard.Instance;

        private StringBuilder sbTemp = new StringBuilder(256);

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

        private ExarUart IOPort;

        public WetlabsFLBB2K (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.FLBB, processMessage, portName, baudRate, parity, dataBits, stopBits, readTimeout, writeTimeout, useSTX, STX);
        }

        public void init()
        {
            if (!IOPort.IsOpen)
                IOPort.Open();

            
            sendStopCmd();
            Thread.Sleep(1500);
            sendPktCmd(flbbPkt);
            Thread.Sleep(1000);
            sendAveCmd(flbbAve);
            Thread.Sleep(1000);

            sbTemp.Clear();
            sbTemp.Append("FLBB setup: Pkt = ");
            sbTemp.Append(flbbPkt);
            sbTemp.Append(" Ave = ");
            sbTemp.Append(flbbAve);
            SciLogger.appendToMsgHeader(sbTemp);
            EngrLogger.writeToColumns(1, sbTemp);
        }

        public void powerUp()
        {
            EngrLogger.writeToColumns("Powering up FLBB");

            motherboard.EnableChannelPower(MotherBoard.ChannelNames.FLBBChannel);
            Thread.Sleep(1500);
            
            init();

            //TODO P2 eventually need to test for proper power up and send flbbmode(1) if success or flbbmode(0) if not
            bool success = true;
            sbTemp.Clear();
            if (success)
                sbTemp.Append("flbbmode(1)");
            else
                sbTemp.Append("flbbmode(0)");

            SciLogger.appendToMsgHeader(sbTemp);
        }

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

        /// <summary>
        /// Indicates if the serial port associated with the WetlabsFLBB2K is open.
        /// </summary>
        public bool Initialized
        {
            get { return IOPort.IsOpen; }
        }

        private static byte[] txBytes = new byte[64];
        private static StringBuilder pktCmd = new StringBuilder("$pkt ");
        private const string pktCmdHeader = "Sending FLBB pkt command";

        private void sendPktCmd(int numPkt)
        {
            //Debug.Print("Sending FLBB pkt command");

            sbTemp.Clear();
            sbTemp.Append(pktCmdHeader);
            sbTemp.Append(": ");
            sbTemp.Append(pktCmd);
            sbTemp.AppendLine(numPkt.ToString());
            //Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
            //Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
            //qS.state = SV.CurrentState;
            //qS.qRecordType = StructQueue.QRecordType.FLBB;
            //qS.timeStamp = DateTime.Now;
            //Program.sQueue.Enqueue(qS);

            EngrLogger.writeToColumns(sbTemp);

            sbTemp.Clear();
            sbTemp.Append(pktCmd);
            sbTemp.AppendLine(numPkt.ToString());
            Array.Clear(txBytes, 0, txBytes.Length);
            Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), txBytes, sbTemp.Length);
            IOPort.Write(txBytes, 0, sbTemp.Length);
        }

        private static StringBuilder aveCmd = new StringBuilder("$ave ");
        private const string aveCmdHeader = "Sending FLBB ave command";

        private void sendAveCmd(int numAve)
        {
            //Debug.Print("Sending FLBB ave command");

            sbTemp.Clear();
            sbTemp.Append(aveCmdHeader);
            sbTemp.Append(": ");
            sbTemp.Append(aveCmd);
            sbTemp.AppendLine(numAve.ToString());

            //Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
            //Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qS.byteArray, sbTemp.Length);
            //qS.state = SV.CurrentState;
            //qS.qRecordType = StructQueue.QRecordType.FLBB;
            //qS.timeStamp = DateTime.Now;
            //Program.sQueue.Enqueue(qS);

            EngrLogger.writeToColumns(sbTemp);

            sbTemp.Clear();
            sbTemp.Append(aveCmd);
            sbTemp.AppendLine(numAve.ToString());
            Array.Clear(txBytes, 0, txBytes.Length);
            Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), txBytes, sbTemp.Length);
            IOPort.Write(txBytes, 0, sbTemp.Length);
        }

        private static readonly byte[] stopCmd = UTF8Encoding.UTF8.GetBytes("!!!!!!!!!!!!!!!");
        private const string stopCmdHeader = "Sending FLBB stop command";

        public void sendStopCmd()
        {
            //Debug.Print("Sending FLBB stop command");
            //Array.Clear(qS.byteArray, 0, qS.byteArray.Length);
            //Array.Copy(UTF8Encoding.UTF8.GetBytes(stopCmdHeader), qS.byteArray, stopCmdHeader.Length);
            //qS.state = SV.CurrentState;
            //qS.qRecordType = StructQueue.QRecordType.FLBB;
            //qS.timeStamp = DateTime.Now;
            //Program.sQueue.Enqueue(qS);

            EngrLogger.writeToColumns(stopCmdHeader);

            IOPort.Write(stopCmd, 0, stopCmd.Length);
        }

        private static readonly byte[] runCmd = UTF8Encoding.UTF8.GetBytes("$run\r\n");
        private const string runCmdHeader = "Sending FLBB run command";

        public void sendRunCmd()
        {
            EngrLogger.writeToColumns(runCmdHeader);

            IOPort.Write(runCmd, 0, runCmd.Length);
        }

        private static StringBuilder sbTemp2 = new StringBuilder(128);
        private static readonly StringBuilder FLBBHeader = new StringBuilder("FLBB Message");
        private static DateTime timeNow = new DateTime();

        public void processMessage(QRecord qRecord)
        {
            timeNow = DateTime.Now;
            if (Array.IndexOf(qRecord.byteArray, (byte)'\t') > -1)
            {
                sbTemp.Clear();
                sbTemp.Append(SafeEncoding.GetChars(qRecord.byteArray));
                //TODO P2 get rid of try catch block; maybe check for tabs then get rid of them
                try
                {
                    sbTemp.Replace('\t', ',');
                }
                catch
                {
                    sbTemp2.Clear();
                    sbTemp2.Append("Program /t .Replace error location 1 string: ");
                    sbTemp2.Append(sbTemp);
                    EngrLogger.writeToColumns(sbTemp2);
                }
                Array.Clear(qRecord.byteArray, 0, qRecord.byteArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbTemp.ToString()), qRecord.byteArray, sbTemp.Length);
            }

            if (SV.Pressure == double.NaN)
                EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, timeNow,
                                          EngrLiterals.ColumnNums.state, qRecord.state,
                                          EngrLiterals.ColumnNums.comment, FLBBHeader,
                                          EngrLiterals.ColumnNums.FLBB, qRecord.byteArray);
            else
                EngrLogger.writeToColumns(EngrLiterals.ColumnNums.dateTime, timeNow,
                                          EngrLiterals.ColumnNums.state, qRecord.state,
                                          EngrLiterals.ColumnNums.comment, FLBBHeader,
                                          EngrLiterals.ColumnNums.pressure, SV.Pressure,
                                          EngrLiterals.ColumnNums.FLBB, qRecord.byteArray);

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