using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

using LFP_Manager.DataStructure;

using log4net;
using log4net.Appender;
using log4net.Layout;
using log4net.Repository.Hierarchy;

namespace LFP_Manager.Utils
{
    public class csL4Logger
    {
        private static csL4Logger LoggerInstance;

        public ILog log;
        public RollingFileAppender rollingAppender;
        public PatternLayout layout;
        public log4net.Filter.LoggerMatchFilter lmf;

        public static csL4Logger GetInstance()
        {
            if (LoggerInstance == null)
            {
                LoggerInstance = new csL4Logger();
            }
            return LoggerInstance;
        }

        public csL4Logger()
        {
            string FilePath = AppDomain.CurrentDomain.BaseDirectory + "\\Log\\App.log"; // 실행폴더 아래에 Log폴더

            Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository();
            hierarchy.Configured = true;

            RollingFileAppender rollingAppender = new RollingFileAppender();
            rollingAppender.Name = "logger";
            rollingAppender.File = FilePath;    // 로그파일 이름
            rollingAppender.AppendToFile = true;

            rollingAppender.StaticLogFileName = true;
            rollingAppender.CountDirection = 1;
            rollingAppender.RollingStyle = RollingFileAppender.RollingMode.Date;
            rollingAppender.DatePattern = "_yyyyMMdd\".log\"";          // 날짜가 변경되면 이전 로그에 붙은 이름

            PatternLayout layout = new PatternLayout("%date [%-5level] %message%newline");  // 로그출력 포맷

            rollingAppender.Layout = layout;

            hierarchy.Root.AddAppender(rollingAppender);
            rollingAppender.ActivateOptions();
            hierarchy.Root.Level = log4net.Core.Level.All;

            log = LogManager.GetLogger("logger");
            Logger l = (Logger)log.Logger;
        }

        public void AddDebug(string LogMsg)
        {
            log.Debug(LogMsg);
        }

        public void AddError(string LogMsg)
        {
            log.Error(LogMsg);
        }

        public void Close()
        {
            LogManager.Shutdown();
        }
    }

    class csLog
    {
        private static string SYSTEMLOG_FILE_DIR_M = "\\log\\systemlog";
        private static string SYSTEMLOG_FILE_DIR = "\\log\\systemlog";

        private static string DB_FILE_DIR = @"\db";

        public static string GetLogFolder(string AppPath)
        {
            string path = System.IO.Path.GetDirectoryName(AppPath);

            if (Directory.Exists(path + SYSTEMLOG_FILE_DIR_M) == false)
                Directory.CreateDirectory(path + SYSTEMLOG_FILE_DIR_M);

            return path + SYSTEMLOG_FILE_DIR_M;
        }

        public static string GetDbFolder(string AppPath)
        {
            string path = System.IO.Path.GetDirectoryName(AppPath);

            if (Directory.Exists(path + DB_FILE_DIR) == false)
                Directory.CreateDirectory(path + DB_FILE_DIR);

            return path + DB_FILE_DIR;
        }

        public static void SystemDataLog(int RackID, CommConfig sConfig, CsDeviceData.DeviceModuleData mData, DateTime aTime, string AppPath)
        {
            SYSTEMLOG_FILE_DIR = String.Format("{0}\\SHELF{1}\\{2}", "\\log\\systemlog", RackID, String.Format("{0:yyyyMM}", aTime));

            FileStream logFile = null;
            string path = System.IO.Path.GetDirectoryName(AppPath);
            string FileName = String.Format(path + SYSTEMLOG_FILE_DIR + "\\SHELF{0}_LOG_{1}.csv", RackID, String.Format("{0:yyMMdd}", aTime));
            byte[] logData;

            if (Directory.Exists(path + SYSTEMLOG_FILE_DIR) == false)
                Directory.CreateDirectory(path + SYSTEMLOG_FILE_DIR);

            if (File.Exists(FileName) == false)
            {
                logFile = new FileStream(FileName, FileMode.CreateNew, FileAccess.ReadWrite);
                logData = WriteDataHeader(aTime, mData);
                logFile.Write(logData, 0, logData.Length);
                logFile.Close();
            }

            logFile = null;
            //logFile = new FileStream(FileName, FileMode.Open, FileAccess.ReadWrite);
            logFile = new FileStream(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);

            if (logFile != null)
            {
                logFile.Seek(0, SeekOrigin.End);
                logData = WriteData(aTime, sConfig, mData);
                logFile.Write(logData, 0, logData.Length);
                logFile.Close();
            }
        }

        public static void SystemTotalDataLog(CommConfig sConfig, CsDeviceData.DeviceSystemTotalData tData, DateTime aTime, string AppPath, string LogFileName)
        {
            SYSTEMLOG_FILE_DIR = String.Format("{0}\\MAIN\\{1}", "\\log\\systemlog", String.Format("{0:yyyyMM}", aTime));

            FileStream logFile = null;
            string path = System.IO.Path.GetDirectoryName(AppPath);
            //string FileName = String.Format(path + SYSTEMLOG_FILE_DIR + "\\SHELF{0}_LOG_{1}.csv", RackID, String.Format("{0:yyMMdd}", aTime));
            string FileName = String.Format(path + SYSTEMLOG_FILE_DIR + "\\MAIN_LOG_{0}.csv", LogFileName);
            byte[] logData;

            if (Directory.Exists(path + SYSTEMLOG_FILE_DIR) == false)
                Directory.CreateDirectory(path + SYSTEMLOG_FILE_DIR);

            if (File.Exists(FileName) == false)
            {
                logFile = new FileStream(FileName, FileMode.CreateNew, FileAccess.ReadWrite);
                logData = WriteTotalDataHeader(aTime);
                logFile.Write(logData, 0, logData.Length);
                logFile.Close();
            }

            logFile = null;
            //logFile = new FileStream(FileName, FileMode.Open, FileAccess.ReadWrite);
            logFile = new FileStream(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);

            if (logFile != null)
            {
                logFile.Seek(0, SeekOrigin.End);
                logData = WriteTotalData(aTime, sConfig, tData);
                logFile.Write(logData, 0, logData.Length);
                logFile.Close();
            }
        }

        public static void SystemDataLog(int mID, CommConfig sConfig, CsDeviceData.DeviceModuleData mData, DateTime aTime, string AppPath, string LogFileName)
        {
            SYSTEMLOG_FILE_DIR = String.Format("{0}\\SHELF{1}\\{2}", "\\log\\systemlog", mID, String.Format("{0:yyyyMM}", aTime));

            FileStream logFile = null;
            string path = Path.GetDirectoryName(AppPath);
            //string FileName = String.Format(path + SYSTEMLOG_FILE_DIR + "\\SHELF{0}_LOG_{1}.csv", RackID, String.Format("{0:yyMMdd}", aTime));
            string FileName = String.Format(path + SYSTEMLOG_FILE_DIR + "\\SHELF{0}_LOG_{1}.csv", mID, LogFileName);
            byte[] logData;

            if (Directory.Exists(path + SYSTEMLOG_FILE_DIR) == false)
                Directory.CreateDirectory(path + SYSTEMLOG_FILE_DIR);

            if (File.Exists(FileName) == false)
            {
                logFile = new FileStream(FileName, FileMode.CreateNew, FileAccess.ReadWrite);
                logData = WriteDataHeader(aTime, mData);
                logFile.Write(logData, 0, logData.Length);
                logFile.Close();
            }

            logFile = null;
            //logFile = new FileStream(FileName, FileMode.Open, FileAccess.ReadWrite);
            logFile = new FileStream(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);

            if (logFile != null)
            {
                logFile.Seek(0, SeekOrigin.End);
                logData = WriteData(aTime, sConfig, mData);
                logFile.Write(logData, 0, logData.Length);
                logFile.Close();
            }
        }

        #region SYSTEM DATE LOGGING

        private static byte[] WriteTotalDataHeader(DateTime aDateTime)
        {
            string tt;
            string sdata;

            tt = aDateTime.ToString("yyyy-MM-dd HH:mm:ss");
            sdata = String.Format(
                "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},"
                , "DATETIME"                    // 0
                , "INTERFACE"                   // 1
                , "MODEL"                       // 2
                , "COMM STATUS"                 // 3
                , "STATUS"                      // 4
                , "ALARM"                       // 5
                , "VOLTAGE"                     // 6
                , "CURRENT"                     // 7
                , "SOC"                         // 8
                , "MAX. TEMP"                   // 9
               );
            sdata += "\r\n";

            Byte[] info =
                   new UTF8Encoding(true).GetBytes(sdata);

            return info;
        }

        private static byte[] WriteDataHeader(DateTime aDateTime, CsDeviceData.DeviceModuleData mData)
        {
            string tt;
            string sdata;

            tt = aDateTime.ToString("yyyy-MM-dd HH:mm:ss");
            sdata = String.Format(
                "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},"
                , "DATETIME"                    // 0
                , "INTERFACE"                   // 1
                , "MODEL"                       // 2
                , "FW_VER"                      // 3
                , "MODULE_SN"                   // 4
                , "COMM STATUS"                 // 5
                , "STATUS"                      // 6
                , "ALARM"                       // 7
                , "VOLTAGE"                     // 8
                , "CURRENT"                     // 9
                , "SOC"                         // 10
                , "SOH"                         // 11
                , "CYCLES"                      // 12
               );
            sdata += "CV_DIFF,";
            for (int i = 0; i < mData.cellQty; i++)
            {
                sdata += String.Format("{0},", String.Format("Cell_{0}", i + 1));
            }

            sdata += "TP_DIFF,";
            for (int i = 0; i < mData.tempQty; i++)
            {
                sdata += String.Format("{0},", String.Format("Temp_{0}", i + 1));
            }

            sdata += String.Format("{0},", String.Format("EXT1_Temp"));
            sdata += String.Format("{0},", String.Format("EXT2_Temp"));

            sdata += String.Format(
                "{0},{1},{2},"
                , "Warning"                     // 0
                , "Fault"                       // 1
                , "CB"                          // 2
               );
            sdata += "\r\n";

            Byte[] info =
                   new UTF8Encoding(true).GetBytes(sdata);

            return info;
        }

        private static string GetStatusString(ushort aStatus)
        {
            string result = "";

            if (aStatus == 0)
            {
                result = "STANDBY";
            }
            else if (aStatus == 1)
            {
                result = "CHARGING";
            }
            else if (aStatus == 2)
            {
                result = "DISCHARGING";
            }
            else if (aStatus == 3)
            {
                result = "FLOATING";
            }
            else
            {
                result = "UNKNOWN";
            }
            return result;
        }

        private static string GetAlarmString(ushort aAlarm)
        {
            string result = "";
            bool[] rackStatus = csUtils.UInt16ToBitArray(aAlarm);

            if (aAlarm == 0)      result = "NORMAL";
            else if (aAlarm == 1) result = "WARNING";
            else if (aAlarm == 2) result = "FAULT";
            else if (aAlarm == 3) result = "WARMING UP";
            else                  result = "UNKNOWN";

            return result;
        }

        private static string GetInterface(int sCommType)
        {
            string result = "";

            result = "UART";

            return result;
        }

        private static string GetModelName(CommConfig sConfig)
        {
            string result = "";

            result = csConstData.UART_MODEL[sConfig.UartModelIndex];

            return result;
        }

        private static string GetCommStatus(CommConfig sConfig, CsDeviceData.DeviceModuleData mData)
        {
            string result = "";

            switch (sConfig.CommType)
            {
                case csConstData.CommType.COMM_UART:
                    result = mData.CommFail == false ? "NORM" : "FAIL";
                    break;
                case csConstData.CommType.COMM_RS485:
                    result = mData.CommFail == false ? "NORM" : "FAIL";
                    break;
                case csConstData.CommType.COMM_SNMP:
                    if (mData.CommFail == false)
                    {
                        if (mData.ShelfCommFail == false) result = "NORM";
                        else result = "FAIL";
                    }
                    else
                    {
                        result = "OFF-LINE";
                    }
                    break;
                default:
                    break;
            }
            return result;
        }

        private static string GetCommStatus(CsDeviceData.DeviceSystemTotalData aTotalData)
        {
            string result = "";

            if (aTotalData.CommFail)
                result = "OFF-LINE";
            else
                result = "NORM";

            return result;
        }

        private static byte[] WriteTotalData(DateTime aLog, CommConfig sConfig, CsDeviceData.DeviceSystemTotalData tData)
        {
            string tt;
            string sdata;

            tt = aLog.ToString("yyyy-MM-dd HH:mm:ss");
            sdata = String.Format(
                "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},"
                , tt                                                                                    //  0    DATETIME 
                , GetInterface(sConfig.CommType)                                                        //  1    INTERFACE
                , GetModelName(sConfig)                                                                 //  2    MODEL 
                , GetCommStatus(tData)                                                                  //  3    COMM STATUS 
                , GetStatusString(tData.StatusData.status)                                              //  4    STATUS 
                , GetAlarmString(tData.StatusData.batteryStatus)                                        //  5    ALARM
                , String.Format("{0:#0.0}", Convert.ToDouble(tData.ValueData.TotalVoltage) / 10)        //  6    voltageOfPack
                , String.Format("{0:#0.0}", Convert.ToDouble(tData.ValueData.TotalCurrent) / 10)        //  7    current
                , String.Format("{0:#0.0}", Convert.ToDouble(tData.ValueData.TotalSOC) / 10)            //  8    SOC
                , String.Format("{0:#0.0}", Convert.ToDouble(tData.ValueData.TotalTemp) / 10)           //  9    Max Temperature
                );
            sdata += "\r\n";

            Byte[] info =
                   new UTF8Encoding(true).GetBytes(sdata);

            return info;
        }

        private static byte[] WriteData(DateTime aLog, CommConfig sConfig, CsDeviceData.DeviceModuleData mData)
        {
            string tt;
            string sdata;

            tt = string.Format("{0:yyyy-MM-dd HH:mm:ss}", aLog);
            sdata = String.Format(
                "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},"
                , tt                                                                                    //  0    DATETIME 
                , GetInterface(sConfig.CommType)                                                        //  1    INTERFACE
                , mData.Information.ModelName                                                           //  2    MODEL 
                , mData.Information.SwProductRev                                                        //  3    FW Ver 
                , mData.Information.HwSerialNumber                                                      //  4    SN
                , GetCommStatus(sConfig, mData)                                                         //  5    COMM STATUS 
                , GetStatusString(mData.StatusData.status)                                              //  6    STATUS 
                , GetAlarmString(mData.StatusData.batteryStatus)                                        //  7    ALARM
                , String.Format("{0:#0.0}", Convert.ToDouble(mData.ValueData.voltage) / 10)       //  8    voltageOfPack
                , String.Format("{0:#0.0}", Convert.ToDouble(mData.ValueData.current) / 10)             //  9    current
                , String.Format("{0:#0.0}", Convert.ToDouble(mData.ValueData.SOC) / 10)                //  10    SOC
                , String.Format("{0:#0.0}", Convert.ToDouble(mData.ValueData.SOH) / 10)       //  11    SOH
                , String.Format("{0}", Convert.ToDouble(mData.ValueData.cycleCount) / 1)                //  12    Cycle Count
                );

            sdata += String.Format("{0:#0.000},", Convert.ToDouble(mData.AvgData.diffCellVoltage) / 1000);                  // 13       Cell Voltage Diff
            for (int i = 0; i < mData.cellQty; i++)
            {
                sdata += String.Format("{0:#0.000},", Convert.ToDouble(mData.ValueData.CellVoltage[i]) / 1000);                  // 14 - 28    Cell Voltage n
            }

            sdata += String.Format("{0:#0.0},", Convert.ToDouble(mData.AvgData.diffTemp) / 10);                  // 29   Temperature Diff
            for (int i = 0; i < mData.tempQty; i++)
            {
                sdata += String.Format("{0:#0.0},", Convert.ToDouble(mData.ValueData.CellTemperature[i]) / 10);                  // 30 - 33    Cell Temperature n
            }

            sdata += String.Format("{0:#0.0},", Convert.ToDouble(mData.ValueData.MosTemperature) / 10);                  // 34    Ext1 Temperature n
            sdata += String.Format("{0:#0.0},", Convert.ToDouble(mData.ValueData.AmbTemperature) / 10);                  // 35    Ext2 Temperature n

            sdata += String.Format(
                "{0},{1},{2},"
                , String.Format("0x{0}", mData.StatusData.warning.ToString("X4"))                                       // 0    Warning
                , String.Format("0x{0}", mData.StatusData.protect.ToString("X4"))                                    // 1    Protection
                , String.Format("0x{0}", mData.StatusData.cellBallanceStatus.ToString("X4"))                            // 2    cellBallanceStatus
                );
            sdata += "\r\n";

            Byte[] info =
                   new UTF8Encoding(true).GetBytes(sdata);

            return info;
        }
        #endregion

        #region DATA PRINT

        public static string data_print(byte[] data, int len)
        {
            byte[] ASC;
            int i, j;
            string result = "";

            ASC = new byte[20];

            if (len > 2)
            {
                if (ASC != null)
                {
                    for (i = 0; i < (len / 16 + 1); i++)
                    {
                        result += String.Format("0x{0:X8}  ", (int)(i * 16));
                        for (j = 0; j < 16; j++)
                        {
                            if ((i * 16 + j) >= data.Length)
                            {
                                result += "   ";
                                ASC[j] = (byte)' ';
                            }
                            else
                            {
                                result += String.Format("{0:X2} ", data[(i * 16) + j]);
                                if (data[i * 16 + j] < ' ')
                                    ASC[j] = (byte)'.';
                                else
                                {
                                    if ((j == 15) && (data[i * 16 + j] > 128))
                                        ASC[j] = (byte)'.';
                                    else
                                        ASC[j] = data[i * 16 + j];
                                }
                            }
                        }
                        ASC[16] = 0x00;
                        result += "\r\n";
                        //result += String.Format("  {0}\r\n", ASC.ToArray<byte>().ToString());
                    }
                }
                result += "\r\n";
                //result += String.Format("ID: {0:X2}\r\n", data[6]);
                //result += String.Format("CMD: {0:X2}\r\n", data[7]);
                //result += String.Format("S-A: {0:X4}({0:d})\r\n", (data[8] << 8) | data[9]);
                //result += String.Format("S-L: {0:X4}({0:d})\r\n", (data[10] << 8) | data[11]);
            }

            return result;
        }
        public static string trx_data_print(byte[] data, int len, int flag)
        {
            int i, j;
            string result = "";

            if (len > 2)
            {
                for (i = 0; i < (len / 16 + 1); i++)
                {
                    if (flag == 0)
                    { result += string.Format("[{0:yyyy-MM-dd HH:mm:ss.fff}] TX({1:0000}): ", DateTime.Now, len); }
                    else
                    { result += string.Format("[{0:yyyy-MM-dd HH:mm:ss.fff}] RX({1:0000}): ", DateTime.Now, len); }

                    for (j = 0; j < 16; j++)
                    {
                        if ((i * 16 + j) >= len)
                        {
                            result += "   ";
                        }
                        else
                        {
                            result += string.Format("{0:X2} ", data[(i * 16) + j]);
                        }
                    }
                    result += "\r\n";
                }
            }
            return result;
        }
        public static string trx_msg_print(string msg, int flag)
        {
            string result = "";

            if (flag == 0)
            { 
                result += string.Format("[{0:yyyy-MM-dd HH:mm:ss.fff}] TX({1}) ", DateTime.Now, msg); 
            }
            else
            { 
                result += string.Format("[{0:yyyy-MM-dd HH:mm:ss.fff}] RX({1}) ", DateTime.Now, msg); 
            }
            return result;
        }

        #endregion

        #region DB ERROR LOG
        public static void SystemDbErrorLog(CommConfig sConfig, string sEvent, DateTime aTime, string AppPath)
        {
            SYSTEMLOG_FILE_DIR = string.Format(@"{0}\SystemErr\{1}", @"\log\systemlog", string.Format("{0:yyyyMM}", aTime));
            string path = Path.GetDirectoryName(AppPath);
            string FileName = string.Format(path + SYSTEMLOG_FILE_DIR + @"\SYS_ERROR_LOG_{0}.txt", string.Format("{0:yyMMdd}", aTime));
            byte[] logData;

            if (Directory.Exists(path + SYSTEMLOG_FILE_DIR) == false)
            {
                Directory.CreateDirectory(path + SYSTEMLOG_FILE_DIR);
            }

            FileStream logFile;
            if (File.Exists(FileName) == false)
            {
                logFile = new FileStream(FileName, FileMode.CreateNew, FileAccess.ReadWrite);
                logFile.Close();
            }

            logFile = new FileStream(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);

            if (logFile != null)
            {
                logFile.Seek(0, SeekOrigin.End);
                logData = WriteDbEventData(aTime, sEvent);
                logFile.Write(logData, 0, logData.Length);
                logFile.Close();
            }
        }
        private static byte[] WriteDbEventData(DateTime aLog, String sEvent)
        {
            string tt;
            string sdata;

            tt = aLog.ToString("yyyy-MM-dd HH:mm:ss");
            sdata = String.Format(
                "{0}    {1}\r\n"
                , tt                                                                                    // 0    DATETIME 
                , sEvent                                                                                // 1    Event Log Data 
                );

            byte[] info = new UTF8Encoding(true).GetBytes(sdata);

            return info;
        }
        #endregion

    }
}
