using System;
using Microsoft.SPOT;

//using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.IO;

using System.Text;
using System.IO;

using GHI.Premium.IO;

namespace CPF
{
    class EngrLogger
    {

        private static StringBuilder columnNames = new StringBuilder();

        private static PersistentStorage sdCard;
        private static VolumeInfo sdVol;
        private static FileStream logFile;

        private static StringBuilder sbTemp = new StringBuilder(4096);
        private static StringBuilder sbWrite = new StringBuilder(4096);
        private static StringBuilder sbPrefix = new StringBuilder(4096);

        private static object loggerLock = new object();

        private static byte[] writeArray = new byte[4096];

        private static DateTime timeNow;
        private static char token = '\t';

        private static bool fileOpen = false;

        public enum ColumnNums : int
        {
            //After the date and time and current state goes the comment
            comment = 0,

            //Then the verbosity level 0 stuff
            pressure,
            bellowsPosition,
            batteryBusVolts,

            //Science data
            temp, salinity,
            TOpt, TPhase, RPhase,
            NO3,
            pH,
            FSig,
            BbSig,
            TSig,

            lat, lon,

            //CTD stuff
            pressureTimestamp, platformLPFVel, platformDiffVel, platformLPFAcc, pressureAverage, pressureVariance,

            //PID stuff
            PID_setPoint, PID_PV, PID_y1, PID_y2, PID_I, PID_v, PID_u, PID_uCPS,

            //ADC Stuff
            batteryBusAmps, Amps12V, Amps33V,

            //PTHStuff
            LPS331Pressure, LPS331Temperature, SHT21Humidity, SHT21Temperature,

            lastColumn,
        }

        public static void init()
        {
            //Column names need to be appended in the same order as ColumnNum enum above
            columnNames.Clear();
            columnNames.Append("Date\tTime\tState\t");
            columnNames.Append("Comment\t");
            columnNames.Append("Pressure\tBel Pos\tBatt Volt\t");

            columnNames.Append("Temp\tSalinity\t");
            columnNames.Append("Topt\tTPhase\tC1Rphase\t");
            columnNames.Append("NO3\tpH\t");
            columnNames.Append("FSig\tBbSig\tTsig\t");

            columnNames.Append("Lat\tLon\t");

            columnNames.Append("Pres Timestamp\tPlat LPF Vel\tPlat Diff Vel\tPlat LPF Acc\tPress Avg\tPress Var\t");

            columnNames.Append("PID SP\tPID PV\ty1\ty2\tI\tv\tu\tuCPS\t");
            columnNames.Append("Batt Cur\t12V Cur\t3.3V Cur\t");

            columnNames.Append("LPS331Press\tLPS331Temp\tSHT31Humid\tSHT21Temp");

            sdCard = new PersistentStorage("SD");
            sdCard.MountFileSystem();
            sdVol = new VolumeInfo("SD");

            openFile();

            lock (loggerLock) { logFile.Write(UTF8Encoding.UTF8.GetBytes(columnNames.ToString()), 0, columnNames.Length); }
            BTConsole.SendLine(columnNames);
            Debug.Print(columnNames.ToString());

            sbTemp.Clear();
            sbTemp.Append("File Name = ");
            sbTemp.Append(fileName.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("Volume Total Size:     ");
            sbTemp.Append(sdVol.TotalSize.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("Volume Free Space:     ");
            sbTemp.Append(sdVol.TotalFreeSpace.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("PlatVel/Depth PID K = ");
            sbTemp.Append(configFile.KPlatVel.ToString());
            sbTemp.Append("  ");
            sbTemp.Append(configFile.KDepth.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("PlatVel/Depth PID Ti = ");
            sbTemp.Append(configFile.TiPlatVel.ToString());
            sbTemp.Append("  ");
            sbTemp.Append(configFile.TiDepth.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("PlatVel/Depth PID Td = ");
            sbTemp.Append(configFile.TdPlatVel.ToString());
            sbTemp.Append("  ");
            sbTemp.Append(configFile.TdDepth.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("PlatVel/Depth PID Tf = ");
            sbTemp.Append(configFile.TfPlatVel.ToString());
            sbTemp.Append("  ");
            sbTemp.Append(configFile.TfDepth.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("PlatVel/Depth PID Tt = ");
            sbTemp.Append(configFile.TtPlatVel.ToString());
            sbTemp.Append("  ");
            sbTemp.Append(configFile.TtDepth.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("PlatVel/Depth PID B = ");
            sbTemp.Append(configFile.BPlatVel.ToString());
            sbTemp.Append("  ");
            sbTemp.Append(configFile.BDepth.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("PlatVel/Depth PID uLow = ");
            sbTemp.Append(configFile.uLowPlatVel.ToString());
            sbTemp.Append("  ");
            sbTemp.Append(configFile.uLowDepth.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("PlatVel/Depth PID uHigh = ");
            sbTemp.Append(configFile.uHighPlatVel.ToString());
            sbTemp.Append("  ");
            sbTemp.Append(configFile.uHighDepth.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("PlatVel/Depth PID h = ");
            sbTemp.Append(configFile.hPlatVel.ToString());
            sbTemp.Append("  ");
            sbTemp.Append(configFile.hDepth.ToString());
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }

            sbTemp.Clear();
            sbTemp.Append("Target anchor depth = ");
            sbTemp.Append(MissionConfigFile.descendTable[MissionConfigFile.descendTable.Length - 1].depth.ToString());
            sbTemp.Append("  ");
            lock (loggerLock) { write(ColumnNums.comment, getPrefix(sbTemp)); }
        }

        private static int lastValueIndex = 0;
        private static int columnNum = 0;
        private static int valueNum = 0;

        public static void write(params object[] values)
        {
            lock (loggerLock)
            {
                lastValueIndex = values.Length - 1;
                sbWrite.Clear();

                for (columnNum = 0; columnNum < (int)ColumnNums.lastColumn; columnNum++)
                {
                    for (valueNum = 0; valueNum < lastValueIndex; valueNum = valueNum + 2)
                    {
                        if ((int)values[valueNum] == columnNum)
                        {
                            //TODO need to do a better job formatting values to minimize length
                            if (columnNum == (int)ColumnNums.comment)
                                sbWrite.Append(values[valueNum + 1].ToString());
                            else
                                sbWrite.Append(((double)values[valueNum + 1]).ToString("f6"));
                            break;
                        }
                    }
                    sbWrite.Append(token);
                }

                Debug.Print(sbWrite.ToString());
                sbWrite.AppendLine();
                Array.Clear(writeArray, 0, writeArray.Length);
                Array.Copy(UTF8Encoding.UTF8.GetBytes(sbWrite.ToString()), writeArray, sbWrite.Length);
                logFile.Write(writeArray, 0, Array.IndexOf(writeArray, 0));
                BTConsole.SendLine(sbWrite);
            }
        }

        private static StringBuilder sbRecord = new StringBuilder(StructQueue.qRecordWidth);

        public static StringBuilder formatRecord(params object[] values)
        {
            lock (loggerLock)
            {
                lastValueIndex = values.Length - 1;
                sbRecord.Clear();

                for (columnNum = 0; columnNum < (int)ColumnNums.lastColumn; columnNum++)
                {
                    for (valueNum = 0; valueNum < lastValueIndex; valueNum = valueNum + 2)
                    {
                        if ((int)values[valueNum] == columnNum)
                        {
                            //TODO need to do a better job formatting values to minimize length
                            if ( columnNum == (int)ColumnNums.comment )
                                sbRecord.Append(values[valueNum + 1].ToString());
                            else if (columnNum == (int)ColumnNums.TOpt)
                            {
                                sbTemp.Clear();
                                sbTemp.Append(UTF8Encoding.UTF8.GetChars((byte[])values[valueNum + 1]));
                                sbRecord.Append(sbTemp);
                            }
                            else
                                sbRecord.Append(((double)values[valueNum + 1]).ToString("f6"));
                            break;
                        }
                    }
                    sbRecord.Append(token);
                }

                //Debug.Print(sbRecord.ToString());
                sbRecord.AppendLine();
                return sbRecord;
            }
        }

        private static void printVolInfo()
        {
            sbTemp.Clear();
            sbTemp.Append("Volume Total Size:     ");
            sbTemp.Append(sdVol.TotalSize.ToString());
            lock (loggerLock) { write(ColumnNums.comment, sbTemp); }

            sbTemp.Clear();
            sbTemp.Append("Volume Free Space:     ");
            sbTemp.Append(sdVol.TotalFreeSpace.ToString());
            lock (loggerLock) { write(ColumnNums.comment, sbTemp); }

            //String[] fileNames = Directory.GetFiles(sdVol.RootDirectory);
            //for (int i = 0; i < fileNames.Length; i++)
            //{
            //    FileInfo fileInfo = new FileInfo(fileNames[i]);
            //    Debug.Print(i.ToString() + "  File name: " + fileNames[i]
            //                + "   Creation Date = " + fileInfo.CreationTime.ToString()
            //                + "  Length = " + fileInfo.Length.ToString());
            //}
        }

        public static void closeFile()
        {
            if (fileOpen)
                logFile.Close();

            fileOpen = false;
        }

        public static StringBuilder fileName = new StringBuilder(128);

        public static void openFile()
        {
            if (fileOpen)
                closeFile();

            timeNow = DateTime.Now;

            fileName.Clear();
            fileName.Append(sdVol.RootDirectory);
            fileName.Append("\\");
            fileName.Append(timeNow.Year.ToString());
            fileName.Append("-");
            fileName.Append(timeNow.Month.ToString());
            fileName.Append("-");
            fileName.Append(timeNow.Day.ToString());
            fileName.Append("T");
            fileName.Append(timeNow.Hour.ToString());
            fileName.Append("-");
            fileName.Append(timeNow.Minute.ToString());
            fileName.Append("-");
            fileName.Append(timeNow.Second.ToString());

            logFile = new FileStream(fileName.ToString(), FileMode.OpenOrCreate, FileAccess.Write);
            fileOpen = true;
        }

        public static void unMount()
        {
            sdCard.UnmountFileSystem();
        }

        public static StringBuilder getPrefix(StringBuilder comment)
        {
            timeNow = DateTime.Now;

            sbPrefix.Clear();
            sbPrefix.Append(timeNow.ToString("MMM dd, yyyy"));
            sbPrefix.Append(token);
            sbPrefix.Append(timeNow.ToString("HH:mm:ss.fff"));
            sbPrefix.Append(token);
            sbPrefix.Append(Program.sbStateNames[(int)Program.CPFState]);
            sbPrefix.Append(token);
            sbPrefix.Append(comment);

            return (sbPrefix);
        }

        public static StringBuilder getPrefix(DateTime timeStamp, Program.CPFStates state, StringBuilder comment)
        {
            sbPrefix.Clear();
            sbPrefix.Append(timeStamp.ToString("MMM dd, yyyy"));
            sbPrefix.Append(token);
            sbPrefix.Append(timeStamp.ToString("HH:mm:ss.fff"));
            sbPrefix.Append(token);
            sbPrefix.Append(Program.sbStateNames[(int)state]);
            sbPrefix.Append(token);
            sbPrefix.Append(comment);

            return (sbPrefix);
        }
 
        public static StringBuilder getPrefix(DateTime timeStamp, Program.CPFStates state, byte[] byteArrayComment)
        {
            sbPrefix.Clear();
            sbPrefix.Append(timeStamp.ToString("MMM dd, yyyy"));
            sbPrefix.Append(token);
            sbPrefix.Append(timeStamp.ToString("HH:mm:ss.fff"));
            sbPrefix.Append(token);
            sbPrefix.Append(Program.sbStateNames[(int)state]);
            sbPrefix.Append(token);
            sbPrefix.Append(UTF8Encoding.UTF8.GetChars(byteArrayComment));

            return (sbPrefix);
        }

        private static byte[] inArray = new byte[StructQueue.qRecordWidth];
        private static StringBuilder qWriterSB = new StringBuilder(StructQueue.qRecordWidth);

        public static void qWriter(byte[] inArray)
        {
            logFile.Write(inArray, 0, Array.IndexOf(inArray, 0) + 1);
            BTConsole.SendLine(sbWrite);
            qWriterSB.Clear();
            qWriterSB.Append(UTF8Encoding.UTF8.GetChars(inArray));
            qWriterSB.Replace("\r\n", "");
            Debug.Print(qWriterSB.ToString());
        }
    }
}
