using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Linq;
using System.Windows.Forms;
using DevExpress.XtraEditors;

using LFP_Manager.Forms;
using LFP_Manager.Controls;
using LFP_Manager.DataStructure;
using LFP_Manager.Function;
using LFP_Manager.Threads;
using LFP_Manager.Utils;
using DevExpress.Utils.Drawing.Helpers;
using System.Threading.Tasks;
using static LFP_Manager.Threads.csDbThread;

namespace LFP_Manager.Forms
{
    public partial class fmxMain : DevExpress.XtraEditors.XtraForm
    {
        #region VARIABLES

        // Child form
        private fmxInventoryConfig InventoryConfig = null;
        private fmxModuleDetail ModuleDetailForm = null;
        private fmxFwUpdate FwUpdateForm = null;
        private fmxHistory HistoryForm = null;

        // Static Variables
        private CommConfig Config = new CommConfig();
        private CsDeviceData DeviceData = new CsDeviceData();

        // Thread Variables
        private csUartThread uartThread = null;
        private CsUartThreadSB rs485Thread = null;
        private CsSnmpThread snmpThread = null;

        private csDbThread dbThread = null;

        private ucMainStatus MainStatus = null;
        private ucShelfStatus ShelfStatus = null;

        private int ModuleID = 1;

        bool Active = false;

        #endregion

        #region CONSTRUCTORS

        public fmxMain()
        {
            InitializeComponent();

            IniLoad(ref Config);

            uTarget.OnConfig += ConfigButtonClick;
            uTarget.OnStart += StartButtonClick;

            uDataLog.OnUpdate += LogStatusUpdate;

            DisplaySystemFwVerModule();
        }

        private void IniLoad(ref CommConfig aConfig)
        {
            csIniControlFunction.IniLoad(Application.ExecutablePath, ref aConfig);

            uTarget.UpdateConfig(ref aConfig);

            TypeChangedUpdate(ref aConfig);
        }

        #endregion

        #region BUTTON EVENT

        private void ConfigButtonClick(object sender, EventArgs e)
        {
            foreach (Form frm in Application.OpenForms)
            {
                if (frm.Name == "fmxCommConfig")
                {
                    frm.Activate();
                    return;
                }
            }
            fmxCommConfig CommConfigForm = new fmxCommConfig
            {
                Owner = this
            };

            CommConfigForm.OnUpdate += UpdateConfig;
            CommConfigForm.Show();
        }

        private void StartButtonClick(object sender, EventArgs e)
        {
            if (Active)
            {
                DeviceData.Reset();

                switch (Config.CommType)
                {
                    case csConstData.CommType.COMM_UART:
                        if (uartThread != null)
                        {
                            StopUart(); // 내부에서 UnwireUart → Dispose 순서 수행
                            edModuleID.Enabled = true;
                            chbPolling.Enabled = true;
                        }
                        break;
                    case csConstData.CommType.COMM_RS485:
                        // DELTA Protocol
                        if (rs485Thread != null)
                        {
                            StopRS485(); // 내부에서 UnwireRs485 → Dispose 순서 수행
                            edModuleID.Enabled = true;
                            chbPolling.Enabled = true;
                        }
                        break;
                    case csConstData.CommType.COMM_SNMP:
                        break;
                    default:
                        break;
                }

                if (snmpThread != null)
                {
                    snmpThread.disposeThread();
                    snmpThread = null;
                    UpdateSnmpData(this, ref DeviceData.ModuleData[0]);
                }

                if (dbThread != null)
                {
                    //dbThread.Stop();

                    dbThread.OnUpdateAlarmHistory -= UpdateAlarmHistory;
                    dbThread.OnPrint -= ErrorOnPrint;
                    dbThread.Dispose();
                    dbThread = null;
                }

                Active = false;
            }
            else
            {
                try
                {
                    ModuleID = Convert.ToInt16(edModuleID.Text);
                }
                catch (Exception)
                {
                    MessageBox.Show("Wrong Module ID - Please check module ID", "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                switch (Config.CommType)
                {
                    case csConstData.CommType.COMM_UART:
                        // UART MODE - RS-232
                        CommUARTStart();
                        break;
                    case csConstData.CommType.COMM_RS485:
                        // UART MODE - RS-485
                        CommRS485Start();
                        break;
                    case csConstData.CommType.COMM_SNMP:
                        StartProcessSNMP();
                        break;
                }
            }
            uTarget.UpdateStatus(Active);
            uDataLog.UpdateActiveStatus(Active, Config, ref DeviceData);
            uEventLog.UpdateConfig(Config);
            DisplayCommStatus();
        }

        private void UartRecvData(byte[] data)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(delegate ()
                {
                    FwUpdateForm?.RecvData(data);
                    HistoryForm?.RecvUardData(data);
                }));
            }
            else
            {
                FwUpdateForm?.RecvData(data);
                HistoryForm?.RecvUardData(data);
            }
        }

        private void CommUARTStart()
        {
            try
            {
                int mId = string.IsNullOrEmpty(edModuleID.Text) ? 0 : Convert.ToInt32(edModuleID.Text);
                if (mId == 0) { throw new Exception("System ID is zero."); }

                // 혹시 기존 인스턴스가 남아 있다면 먼저 해제
                StopUart();

                if (dbThread != null)
                {
                    dbThread.OnUpdateAlarmHistory -= UpdateAlarmHistory;
                    dbThread.OnPrint -= ErrorOnPrint;
                    dbThread.Dispose();
                    dbThread = null;
                }

                uartThread = new csUartThread(mId, Config, this, DeviceData.ModuleData[0]);
                WireUart(uartThread); // ← 구독은 Start 전에
                uartThread.SetPolling(chbPolling.Checked, mId, ref DeviceData.ModuleData[0]);

                if (uartThread.Start(ref Config, mId, chbPolling.Checked))
                {
                    Active = true;
                    chbPolling.Enabled = true;
                    edModuleID.Enabled = !chbPolling.Checked;
                    ShelfStatus?.UpdateSystemId(mId);

                    dbThread = new csDbThread(Config, this, DeviceData);
                    dbThread.OnUpdateAlarmHistory += UpdateAlarmHistory;
                    dbThread.OnPrint += ErrorOnPrint;
                    dbThread.Start(Config);
                }
                else
                {
                    StopUart();

                    if (dbThread != null)
                    {
                        dbThread.OnUpdateAlarmHistory -= UpdateAlarmHistory;
                        dbThread.OnPrint -= ErrorOnPrint;
                        dbThread.Dispose();
                        dbThread = null;
                    }
                }
            }
            catch (Exception ex)
            {
                // 예외 시에도 반드시 해제 + Dispose
                StopUart();

                dbThread?.Dispose();
                dbThread = null;

                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void StopUart()
        {
            if (uartThread != null)
            {
                UnwireUart();       // 1) 구독 해제
                uartThread.Dispose(); // 2) 스레드/포트 정리(Stop 포함)
                uartThread = null;
            }
        }

        private void CommRS485Start()
        {
            try
            {
                int mId = string.IsNullOrEmpty(edModuleID.Text) ? 0 : Convert.ToInt32(edModuleID.Text);
                if (mId == 0) { throw new Exception("System ID is zero."); }

                StopRS485();

                rs485Thread = new CsUartThreadSB(mId, Config, this, DeviceData.ModuleData);
                WireRs485(rs485Thread); // ← 구독
                rs485Thread.SetPolling(chbPolling.Checked, mId, ref DeviceData.ModuleData);

                if (rs485Thread.Start(ref Config, ModuleID, chbPolling.Checked))
                {
                    Active = true;
                    chbPolling.Enabled = true;
                    edModuleID.Enabled = !chbPolling.Checked;

                    //if (Config.ModuleQty > 1)
                    //    MainStatus?.Start(Config, DeviceData);
                    //else
                    //    ShelfStatus?.UpdateSystemId(ModuleID);

                    dbThread = new csDbThread(Config, this, DeviceData);
                    dbThread.OnUpdateAlarmHistory += UpdateAlarmHistory;
                    dbThread.OnPrint += ErrorOnPrint;
                    dbThread.Start(Config);
                }
                else
                {
                    StopRS485();

                    if (dbThread != null)
                    {
                        dbThread.OnUpdateAlarmHistory -= UpdateAlarmHistory;
                        dbThread.OnPrint -= ErrorOnPrint;
                        dbThread.Dispose();
                        dbThread = null;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

                if (rs485Thread != null)
                {
                    rs485Thread.Dispose();
                    rs485Thread = null;
                }
                if (dbThread != null)
                {
                    dbThread.Dispose();
                    dbThread = null;
                }
            }
        }
        private void StopRS485()
        {
            if (rs485Thread != null)
            {
                UnwireRs485();          // 1) 구독 해제
                rs485Thread.Dispose();  // 2) 스레드/포트 정리(Stop 포함)
                rs485Thread = null;
            }
        }

        private void StartProcessSNMP()
        {
            try
            {
                snmpThread = new CsSnmpThread(Config, ref DeviceData.ModuleData[ModuleID - 1]);
                snmpThread.OnUpdate += UpdateSnmpData;
                snmpThread.OnSetResult += SnmpRecvData;
                snmpThread.Start(Config.SnmpIP, chbPolling.Checked);

                Active = true;

                dbThread = new csDbThread(Config, this, DeviceData);
                dbThread.OnUpdateAlarmHistory += UpdateAlarmHistory;
                dbThread.OnPrint += ErrorOnPrint;
                dbThread.Start(Config);
            }
            catch (Exception)
            {
                if (snmpThread == null)
                {
                    snmpThread.disposeThread();
                    snmpThread = null;
                }

                if (dbThread != null)
                {
                    dbThread.OnUpdateAlarmHistory -= UpdateAlarmHistory;
                    dbThread.OnPrint -= ErrorOnPrint;
                    dbThread.Dispose();
                    dbThread = null;
                }
            }
        }
        #endregion

        #region UPDATE FUNCTIONS

        private void TypeChangeUpdateUart(ref CommConfig aConfig)
        {
            tabStatus.TabPages[0].PageVisible = true;
            for (int i = 1; i < tabStatus.TabPages.Count; i++)
            {
                tabStatus.TabPages[i].PageVisible = false;
            }
            tabStatus.TabPages[0].Text = String.Format("LFP Module [{0}.{1}.{2}.{3}]"
                , 0
                , 0
                , 0
                , 0
                );
            ShelfStatus = new ucShelfStatus();
            ShelfStatus.OnCommand += SetCmdEvent;
            ShelfStatus.Dock = DockStyle.Fill;

            tabStatus.TabPages[0].PageVisible = true;
            tabStatus.TabPages[0].Controls.Add(ShelfStatus);

            ShelfStatus.Start(Convert.ToInt32(edModuleID.Text), ref aConfig, ref DeviceData.ModuleData);

            edModuleID.Enabled = true;
            chbPolling.Enabled = true;
        }
        private void TypeChangeUpdateRS485(ref CommConfig aConfig)
        {
            // Display Screen Delete
            if (MainStatus != null)
            {
                MainStatus.Dispose();
                MainStatus = null;
            }

            if (ShelfStatus != null)
            {
                ShelfStatus.Dispose();
                ShelfStatus = null;
            }

            if (aConfig.ModuleQty > 1)
            {
                // All disable first
                for (int i = 0; i < tabStatus.TabPages.Count; i++)
                {
                    tabStatus.TabPages[i].PageVisible = false;
                }

                MainStatus = new ucMainStatus();
                MainStatus.OnCommand += SetCmdEvent;

                tabStatus.TabPages[0].Text = string.Format("LFP System");
                tabStatus.TabPages[0].PageVisible = true;
                tabStatus.TabPages[0].Controls.Add(MainStatus);
                MainStatus.Dock = DockStyle.Fill;
                MainStatus.UpdateMainConfig(Config, DeviceData);

                MainStatus.Start(aConfig, DeviceData);

                edModuleID.Enabled = false;
                chbPolling.Enabled = false;
            }
            else
            {
                tabStatus.TabPages[0].PageVisible = true;
                for (int i = 1; i < tabStatus.TabPages.Count; i++)
                {
                    tabStatus.TabPages[i].PageVisible = false;
                }
                tabStatus.TabPages[0].Text = String.Format("LFP Module [{0}.{1}.{2}.{3}]"
                    , 0
                    , 0
                    , 0
                    , 0
                    );
                ShelfStatus = new ucShelfStatus();
                ShelfStatus.OnCommand += SetCmdEvent;
                ShelfStatus.OnSendUartData += SetWriteData;
                ShelfStatus.Dock = DockStyle.Fill;

                tabStatus.TabPages[0].PageVisible = true;
                tabStatus.TabPages[0].Controls.Add(ShelfStatus);

                ShelfStatus.Start(ModuleID, ref aConfig, ref DeviceData.ModuleData);

                edModuleID.Enabled = true;
                chbPolling.Enabled = true;
            }
        }
        private void TypeChangeUpdateSnmp(ref CommConfig aConfig)
        {
            tabStatus.TabPages[0].PageVisible = true;
            for (int i = 1; i < tabStatus.TabPages.Count; i++)
            {
                tabStatus.TabPages[i].PageVisible = false;
            }
            tabStatus.TabPages[0].Text = String.Format("LFP Module [{0}.{1}.{2}.{3}]"
                , 0
                , 0
                , 0
                , 0
                );
            ShelfStatus = new ucShelfStatus();
            ShelfStatus.OnCommand += SetCmdEvent;
            ShelfStatus.Dock = DockStyle.Fill;

            tabStatus.TabPages[0].PageVisible = true;
            tabStatus.TabPages[0].Controls.Add(ShelfStatus);

            ShelfStatus.Start(ModuleID, ref aConfig, ref DeviceData.ModuleData);

            edModuleID.Enabled = false;
            chbPolling.Enabled = true;
        }

        private void TypeChangedUpdate(ref CommConfig aConfig)
        {
            tabStatus.TabPages[0].Controls.Clear();

            // Display Screen Deletes
            if (ShelfStatus != null)
            {
                ShelfStatus.Dispose();
                ShelfStatus = null;
            }

            SetCellTempQty(aConfig, ref DeviceData.ModuleData);
            try
            {
                ModuleID = Convert.ToInt16(edModuleID.Text);
            }
            catch (Exception)
            {
                ModuleID = 1;
            }

            switch (aConfig.CommType)
            {
                case csConstData.CommType.COMM_UART:
                    TypeChangeUpdateUart(ref aConfig);
                    historyToolStripMenuItem.Visible = true;
                    inventoryViewToolStripMenuItem.Visible = true;
                    updateToolStripMenuItem.Visible = false;
                    break;
                case csConstData.CommType.COMM_RS485:
                    TypeChangeUpdateRS485(ref aConfig);
                    historyToolStripMenuItem.Visible = true;
                    inventoryViewToolStripMenuItem.Visible = false;
                    updateToolStripMenuItem.Visible = true;
                    break;
                case csConstData.CommType.COMM_SNMP:
                    TypeChangeUpdateSnmp(ref aConfig);
                    historyToolStripMenuItem.Visible = true;
                    inventoryViewToolStripMenuItem.Visible = false;
                    updateToolStripMenuItem.Visible = false;
                    break;
                default:
                    break;
            }
            //DisplayCommStatus();
        }

        private void SetCellTempQty(CommConfig aConfig, ref CsDeviceData.DeviceModuleData[] mData)
        {
            switch (aConfig.CommType)
            {
                case csConstData.CommType.COMM_UART:
                case csConstData.CommType.COMM_RS485:
                    switch (aConfig.UartModelIndex)
                    {
                        case csConstData.UART_MODEL_INDEX.LFPM_48100D:
                        case csConstData.UART_MODEL_INDEX.LFPM_48150D:
                        case csConstData.UART_MODEL_INDEX.LFPM_48200D:
                        case csConstData.UART_MODEL_INDEX.LFPM_48250D:
                        case csConstData.UART_MODEL_INDEX.LFPM_48300D:
                            for (int i = 0; i < mData.Length; i++)
                            {
                                mData[i].cellQty = 15;
                                mData[i].tempQty = 4;
                            }
                            break;
                        case csConstData.UART_MODEL_INDEX.LFPM_124050D:
                            for (int i = 0; i < mData.Length; i++)
                            {
                                mData[i].cellQty = csConstData.SystemInfo.MAX_MODULE_CELL_SIZE;
                                mData[i].tempQty = csConstData.SystemInfo.MAX_MODULE_TEMP_SIZE;
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case csConstData.CommType.COMM_SNMP:
                    switch (aConfig.SnmpModelIndex)
                    {
                        case csConstData.UART_MODEL_INDEX.LFPM_48100D:
                        case csConstData.UART_MODEL_INDEX.LFPM_48150D:
                        case csConstData.UART_MODEL_INDEX.LFPM_48200D:
                        case csConstData.UART_MODEL_INDEX.LFPM_48250D:
                        case csConstData.UART_MODEL_INDEX.LFPM_48300D:
                            for (int i = 0; i < mData.Length; i++)
                            {
                                mData[i].cellQty = 15;
                                mData[i].tempQty = 4;
                            }
                            break;
                        case csConstData.UART_MODEL_INDEX.LFPM_124050D:
                            for (int i = 0; i < mData.Length; i++)
                            {
                                mData[i].cellQty = csConstData.SystemInfo.MAX_MODULE_CELL_SIZE;
                                mData[i].tempQty = csConstData.SystemInfo.MAX_MODULE_TEMP_SIZE;
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    for (int i = 0; i < mData.Length; i++)
                    {
                        mData[i].cellQty = 15;
                        mData[i].tempQty = 4;
                    }
                    break;
            }
        }

        private void DisplaySystemFwVerModule()
        {
            switch (Config.ControlLevel)
            {
                case 0:         // User Level
                    inventoryViewToolStripMenuItem.Visible = false;
                    break;
                case 1:         // Technician Level
                    inventoryViewToolStripMenuItem.Visible = false;
                    break;
                case 2:         // Engineer Level
                    inventoryViewToolStripMenuItem.Visible = false;
                    break;
                case 3:         // Master Level
                    inventoryViewToolStripMenuItem.Visible = true;
                    break;
                default:        // User Level
                    inventoryViewToolStripMenuItem.Visible = false;
                    break;
            }

            switch (Config.ControlLevel)
            {
                case 0:             // User Level
                    this.Text = String.Format("LFP SYSTEM [{0}] -- SB - User Level", Application.ProductVersion);
                    break;
                case 1:             // Technician Level
                    this.Text = String.Format("LFP SYSTEM [{0}] -- SB - Technician Level", Application.ProductVersion);
                    break;
                case 2:             // Engineer Level
                    this.Text = String.Format("LFP SYSTEM [{0}] -- SB - Engineer Level", Application.ProductVersion);
                    break;
                case 3:             // Master Level
                    this.Text = String.Format("LFP SYSTEM [{0}] -- SB - Master Level", Application.ProductVersion);
                    break;
                default:
                    this.Text = String.Format("LFP SYSTEM [{0}] -- SB - User Level", Application.ProductVersion);
                    break;
            }

            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(delegate ()
                {
                    if (DeviceData.ModuleData[0].ValueData.fw_ver[0] != 0x00)
                    {
                        byte[] sn = new byte[33];
                        for (int i = 0; i < 32; i++)
                            sn[i] = DeviceData.ModuleData[0].Information.BMS_SN[i];
                        sn[32] = 0;

                        tabStatus.TabPages[0].Text = String.Format("{0} [{1}] - {2}", "LFP MODULE"
                            , Encoding.UTF8.GetString(DeviceData.ModuleData[0].ValueData.fw_ver)
                            , Encoding.Default.GetString(sn).Trim('\0')
                            );
                    }
                    else
                    {
                        tabStatus.TabPages[0].Text = String.Format("LFP MODULE");
                    }
                }));
            }
            else
            {
                if (DeviceData.ModuleData[0].ValueData.fw_ver[0] != 0x00)
                {
                    byte[] sn = new byte[17];
                    for (int i = 0; i < 16; i++)
                        sn[i] = DeviceData.ModuleData[0].Information.BMS_SN[i];         //22.06.30 Byul 32->16
                    sn[16] = 0;

                    tabStatus.TabPages[0].Text = String.Format("{0} [{1}] - {2}", "LFP MODULE"
                        , Encoding.UTF8.GetString(DeviceData.ModuleData[0].ValueData.fw_ver).Trim('\0')
                        , Encoding.Default.GetString(sn).Trim('\0')
                        );
                }
                else
                {
                    tabStatus.TabPages[0].Text = String.Format("LFP MODULE");
                }
            }
        }

        private void UpdateSnmpData(object sender, ref CsDeviceData.DeviceModuleData mData)
        {
            DeviceData.ModuleData[0] = mData;
            uDataLog.UpdateData(DeviceData);
        }

        private void UpdateConfig(object aConfig)
        {
            Config = (CommConfig)aConfig;

            IniLoad(ref Config);
        }

        #endregion

        #region MENU EVENT

        private void commConfigToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ConfigButtonClick(sender, e);
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void testSendToolStripMenuItem_Click(object sender, EventArgs e)
        {
            csHistoryFunction.DbCreate(System.IO.Path.GetDirectoryName(Application.ExecutablePath));
        }

        private void firmwareUpdateToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (rs485Thread == null) return;

            FwUpdateForm = new fmxFwUpdate(rs485Thread.ModuleID);

            FwUpdateForm.OnAutoTxSet += SetAutoTx;
            FwUpdateForm.OnSendUartData += SetWriteData;
            FwUpdateForm.ShowDialog();
        }

        private void historyViewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (Form frm in Application.OpenForms)
            {
                if (frm.Name == "fmxHistory")
                {
                    frm.Activate();
                    return;
                }
            }
            HistoryForm = new fmxHistory(Config, ModuleID);

            HistoryForm.OnSendUartData += SetWriteData;
            HistoryForm.ShowDialog();
        }

        private void inventoryViewToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (Form frm in Application.OpenForms)
            {
                if (frm.Name == "fmxInventoryConfig")
                {
                    frm.Activate();
                    return;
                }
            }
            InventoryConfig = new fmxInventoryConfig(ModuleID, ref Config, ref DeviceData.ModuleData[ModuleID - 1]);

            InventoryConfig.OnSendUartData += SetWriteData;
            InventoryConfig.Show();
        }

        #endregion

        #region FORM EVENT

        private void fmxMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (uartThread != null)
            {
                uartThread.Dispose();
                uartThread = null;
            }
            if (rs485Thread != null)
            {
                rs485Thread.Dispose();
                rs485Thread = null;
            }
            if (snmpThread != null)
            {
                snmpThread.disposeThread();
                snmpThread = null;
            }
            if (dbThread != null)
            {
                dbThread.Dispose();
                dbThread = null;
            }
        }

        #endregion

        #region STATUS BAR EVENT

        void DisplayCommStatus()
        {
            if (Active == true)
                CommStatusBar.Text = "Comm: On";
            else
                CommStatusBar.Text = "Comm: Off";
        }

        void DisplayCommStatus(string msg)
        {
            if (msg != "")
                CommStatusBar.Text = String.Format("Comm: {0}", msg);
        }

        void LogStatusUpdate(object sender, string msg, bool active, int aLogTime)
        {
            LogStatusBar.Text = msg;

        }

        #endregion

        #region COMMAND AND SET

        private void CommDataPrint(object sender, int id, string msg)
        {
            if (msg != "")
            {
                uEventLog.EventUpdate(id, msg + "\r\n");
            }
        }

        private void SetWriteData(ushort WriteAddr, byte[] WriteData, bool RelyFlag, int type)
        {
            uartThread?.SetWriteReg(WriteAddr, WriteData, RelyFlag, type);
            rs485Thread?.SetWriteReg(WriteAddr, WriteData, RelyFlag, type);
        }

        private void SetCmdEvent(int sId, int cmd, int index, int flag, 
            ref CsDeviceData.DeviceModuleData.DeviceParamData aParam, 
            ref CsDeviceData.DeviceModuleData.DeviceCalibration aCalib)
        {
            if (MainStatus != null)
            {
                switch (cmd)
                {
                    case 1:                 // Module quatity set
                        break;
                    case 2:
                        CreateModuleDetailForm(sId);
                        break;
                }
            }
            uartThread?.SendProcessFromApp(sId, cmd, index, flag, ref aParam, ref aCalib);
        }

        private void CreateModuleDetailForm(int sID)
        {
            foreach (Form frm in Application.OpenForms)
            {
                if (frm.Name == "fmxModuleDetail")
                {
                    ((fmxModuleDetail)frm).SetModuleID(sID);
                    frm.Activate();
                    return;
                }
            }
            ModuleDetailForm = new fmxModuleDetail(sID, ref Config, ref DeviceData.ModuleData);

            //ModuleDetailForm.OnAutoTxCanSet += SetAutoTxCan;
            //ModuleDetailForm.OnSendDataCan += SendDataToCan;
            ModuleDetailForm.Show();
        }

        private void chbPolling_CheckedChanged(object sender, EventArgs e)
        {
            if (edModuleID.Text != "")
            {
                ModuleID = Convert.ToInt32(edModuleID.Text);

                if (uartThread != null)
                {
                    uartThread.SetPolling(chbPolling.Checked, ModuleID, ref DeviceData.ModuleData[ModuleID - 1]);
                    ShelfStatus?.UpdateSystemId(ModuleID);
                }
                if (rs485Thread != null)
                {
                    rs485Thread.SetPolling(chbPolling.Checked, ModuleID, ref DeviceData.ModuleData);
                    ShelfStatus?.UpdateSystemId(ModuleID);
                }
                snmpThread?.SetAutoTx(chbPolling.Checked);

                uDataLog.UpdateActiveStatus(Active, Config, ref DeviceData);

                edModuleID.Enabled = !chbPolling.Checked;
            }
        }

        private void SnmpRecvData(string result, bool error)
        {
            if (result != "")
            {
                uEventLog.EventUpdate(0, result + "\r\n");
            }
        }

        private void SetAutoTx(bool autoTx)
        {
            uartThread?.SetAutoTx(autoTx);
        }
        #endregion

        #region KEYBOARD HOOK
        private void edSystemId_KeyPress(object sender, KeyPressEventArgs e)
        {
            csUtils.TypingOnlyNumber(sender, e, false, false);
        }
        #endregion

        #region MENU COMMAND
        private void inventoryControlToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (Form frm in Application.OpenForms)
            {
                if (frm.Name == "fmxInventoryConfig")
                {
                    frm.Activate();
                    return;
                }
            }
            InventoryConfig = new fmxInventoryConfig(0, ref Config, ref DeviceData.ModuleData[0]);

            InventoryConfig.OnUpdate += UpdateInformation;
            InventoryConfig.OnCommand += InvDataSetCmd;
            //InventoryConfig.OnCommandBySnmp += SetInvEventBySnmp;
            InventoryConfig.Show();
        }

        private void InvDataSetCmd(int sId, int mode, int index, int flag, ref CsDeviceData.DeviceModuleData.DeviceInforation aInvData)
        {
            DeviceData.ModuleData[0].Information = aInvData;
            //OnCommand?.Invoke(sId, mode, index, flag, ref DeviceData.ModuleData[0].ParamData, ref DeviceData.ModuleData[0].CalibriationData);
            //SetCmdToCan(sId, mode, index, flag, ref DeviceData.ModuleData[0].ParamData, ref DeviceData.ModuleData[0].CalibriationData);
        }

        private void UpdateInformation(object sendor)
        {
            ((fmxInventoryConfig)sendor).UpdateData(ref DeviceData.ModuleData[0]);
        }

        private void checkDBToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (Form frm in Application.OpenForms)
            {
                if (frm.Name == "fmxExcelFile")
                {
                    frm.Activate();
                    return;
                }
            }
            fmxExcelFile DbControl = new fmxExcelFile(Config);

            DbControl.Show();
        }

        private void levelConfigToolStripMenuItem_Click(object sender, EventArgs e)
        {
        }

        private void changeLeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (Form frm in Application.OpenForms)
            {
                if (frm.Name == "FmxLevelConfig")
                {
                    frm.Activate();
                    return;
                }
            }
            FmxLevelConfig LevelControl = new FmxLevelConfig(Config);

            LevelControl.ShowDialog();
        }

        private void changePasswordToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (Form frm in Application.OpenForms)
            {
                if (frm.Name == "FmxPasswordChange")
                {
                    frm.Activate();
                    return;
                }
            }
            FmxPasswordChange ChangePassword = new FmxPasswordChange(Config);

            ChangePassword.ShowDialog();
        }
        #endregion

        #region TIMER EVENT
        private void tmrDisplay_Tick(object sender, EventArgs e)
        {
            uTarget.UpdateStatus(Active);
            DisplaySystemFwVerModule();
        }
        #endregion

        #region MAKE TOTAL DATA
        private void MakeTotalDataAndStatus()
        {
            if (Config.CommType == csConstData.CommType.COMM_RS485)
            {
                MakeTotalDataRS485();
                MakeTotalStatusRS485();
                MakeTotalAlarmRS485();
            }
        }

        private void MakeTotalDataRS485()
        {
            int j = 0;
            int vsum, csum, ssum, hsum, tsum;
            int mQty = csUtils.GetModuleQty(Config);

            vsum = csum = ssum = hsum = tsum = 0;
            for (int i = 0; i < mQty; i++)
            {
                if (DeviceData.ModuleData[i].ShelfCommFail == false)
                {
                    vsum += DeviceData.ModuleData[i].ValueData.voltage;
                    csum += DeviceData.ModuleData[i].ValueData.current;
                    ssum += DeviceData.ModuleData[i].ValueData.SOC;
                    hsum += DeviceData.ModuleData[i].ValueData.SOH;
                    tsum += DeviceData.ModuleData[i].AvgData.avgTemp;
                    j++;
                }
            }

            if (j > 0)
            {
                DeviceData.TotalData.ValueData.TotalVoltage = (short)(vsum / j);
                DeviceData.TotalData.ValueData.TotalCurrent = (short)(csum);
                DeviceData.TotalData.ValueData.TotalSOC = (short)(ssum / j);
                DeviceData.TotalData.ValueData.TotalSOH = (short)(hsum / j);
                DeviceData.TotalData.ValueData.TotalTemp = (short)(tsum / j);
            }
            else
            {
                DeviceData.TotalData.ValueData.TotalVoltage = 0;
                DeviceData.TotalData.ValueData.TotalCurrent = 0;
                DeviceData.TotalData.ValueData.TotalSOC = 0;
                DeviceData.TotalData.ValueData.TotalSOH = 0;
                DeviceData.TotalData.ValueData.TotalTemp = 0;
            }
        }

        private void MakeTotalStatusRS485()
        {
            int mQty = csUtils.GetModuleQty(Config);

            switch (DeviceData.TotalData.StatusData.status)
            {
                case 0:         // Standby
                    if (DeviceData.TotalData.ValueData.TotalCurrent > 50)            // Discharging 5.0A
                    {
                        DeviceData.TotalData.StatusData.status = 2;                      // Discharging
                    }
                    else if (DeviceData.TotalData.ValueData.TotalCurrent < -50)      // Charging -5.0A
                    {
                        DeviceData.TotalData.StatusData.status = 1;                      // Charging
                    }
                    else
                    {
                        bool flag = true;
                        for (int i = 0; i < mQty; i++)
                        {
                            if (DeviceData.ModuleData[i].StatusData.status != 3)
                            {
                                flag = false;
                                break;
                            }
                        }
                        if (flag == true)
                        {
                            DeviceData.TotalData.StatusData.status = 3;              // Floating
                        }
                    }
                    break;
                case 1:         // Charging
                    if (DeviceData.TotalData.ValueData.TotalCurrent > 50)                // Discharge -5.0A
                    {
                        DeviceData.TotalData.StatusData.status = 2;                      // Discharging
                    }
                    else if (DeviceData.TotalData.ValueData.TotalCurrent > -30)          // Charging > -3.0A
                    {
                        DeviceData.TotalData.StatusData.status = 0;                      // Standby
                    }
                    else
                    {
                        bool flag = true;
                        for (int i = 0; i < mQty; i++)
                        {
                            if (DeviceData.ModuleData[i].StatusData.status != 3)
                            {
                                flag = false;
                                break;
                            }
                        }
                        if (flag == true)
                        {
                            DeviceData.TotalData.StatusData.status = 3;              // Floating
                        }
                    }
                    break;
                case 2:         // Discharging
                    if (DeviceData.TotalData.ValueData.TotalCurrent < -50)           // Charging -5.0A
                    {
                        DeviceData.TotalData.StatusData.status = 1;                  // Charging
                    }
                    else if (DeviceData.TotalData.ValueData.TotalCurrent < 30)       // Discharge 3.0A
                    {
                        DeviceData.TotalData.StatusData.status = 0;                  // Standby
                    }
                    else
                    {
                        bool flag = true;
                        for (int i = 0; i < mQty; i++)
                        {
                            if (DeviceData.ModuleData[i].StatusData.status != 3)
                            {
                                flag = false;
                                break;
                            }
                        }
                        if (flag == true)
                        {
                            DeviceData.TotalData.StatusData.status = 3;              // Floating
                        }
                    }
                    break;
                case 3:         // Floating
                    if (DeviceData.TotalData.ValueData.TotalCurrent < -50)           // Charging -5.0A
                    {
                        DeviceData.TotalData.StatusData.status = 1;                  // Charging
                    }
                    else if (DeviceData.TotalData.ValueData.TotalCurrent > 50)       // Discharge 5.0A
                    {
                        DeviceData.TotalData.StatusData.status = 2;                  // Discharge
                    }
                    else
                    {
                        bool flag = true;
                        for (int i = 0; i < mQty; i++)
                        {
                            if (DeviceData.ModuleData[i].StatusData.status != 3)
                            {
                                flag = false;
                                break;
                            }
                        }
                        if (flag == false)
                        {
                            DeviceData.TotalData.StatusData.status = 0;              // Standby
                        }
                    }
                    break;
            }
        }

        private void MakeTotalAlarmRS485()
        {
            int mQty = csUtils.GetModuleQty(Config);

            bool aTrip, aWarning;

            aTrip = aWarning = false;

            for (int i = 0; i < mQty; i++)
            {
                if (DeviceData.ModuleData[i].StatusData.batteryStatus == 2)
                {
                    aTrip = true;
                }
                else if ((DeviceData.ModuleData[i].StatusData.batteryStatus == 1) || (DeviceData.ModuleData[i].ShelfCommFail))
                {
                    aWarning = true;
                }
            }
            if (aTrip)
            {
                DeviceData.TotalData.StatusData.batteryStatus = 2;
            }
            else if (aWarning)
            {
                DeviceData.TotalData.StatusData.batteryStatus = 1;
            }
            else
            {
                DeviceData.TotalData.StatusData.batteryStatus = 0;
            }
        }
        #endregion

        #region EVENT CONNECT AND DISCONNECT
        // UART 이벤트 연결/해제
        private void WireUart(csUartThread t)
        {
            if (t == null) return;
            t.OnUpdate += UpdateDataUart;   // EventHandler<ModuleDataEventArgs>
            t.OnDataRecv += UartRecvData;    // EventHandler<DataRecvEventArgs>
            t.OnPrint += CommDataPrint;    // EventHandler<PrintEventArgs>
        }

        private void UnwireUart()
        {
            if (uartThread == null) return;
            uartThread.OnUpdate -= UpdateDataUart;
            uartThread.OnDataRecv -= UartRecvData;
            uartThread.OnPrint -= CommDataPrint;
        }

        // RS485(Delta) 이벤트 연결/해제
        private void WireRs485(CsUartThreadSB t)
        {
            if (t == null) return;
            t.OnUpdate += UpdateDataRS485;  // EventHandler<ModuleDataEventArgs>
            t.OnDataRecv += UartRecvData;    // EventHandler<DataRecvEventArgs>
            t.OnPrint += CommDataPrint;    // EventHandler<PrintEventArgs>
        }

        private void UnwireRs485()
        {
            if (rs485Thread == null) return;
            rs485Thread.OnUpdate -= UpdateDataRS485;
            rs485Thread.OnDataRecv -= UartRecvData;
            rs485Thread.OnPrint -= CommDataPrint;
        }
        #endregion

        #region EVENT HANDLER
        private void UpdateDataUart(object sender, csUartThread.ModuleDataEventArgs e)
        {
            // 필요시 깊은 복사(바인딩 대상이 스레드 모두에서 변경될 여지가 있으면)
            // var snapshot = e.Modules.Select(m => m.DeepCopy()).ToArray();
            // DeviceData.ModuleData = snapshot;

            DeviceData.ModuleData[0] = e.Module; // 참조 재할당만으로 충분하면 이대로

            // ref 제거: UpdateData 시그니처도 ref 없이 받도록 변경 권장
            ShelfStatus?.UpdateData(DeviceData.ModuleData);
            uDataLog.UpdateData(DeviceData);
        }

        private void UpdateDataRS485(object sender, CsUartThreadSB.ModuleDataEventArgs e)
        {
            // 필요시 깊은 복사(바인딩 대상이 스레드 모두에서 변경될 여지가 있으면)
            // var snapshot = e.Modules.Select(m => m.DeepCopy()).ToArray();
            // DeviceData.ModuleData = snapshot;

            DeviceData.ModuleData = e.Modules; // 참조 재할당만으로 충분하면 이대로

            // ref 제거: UpdateData 시그니처도 ref 없이 받도록 변경 권장
            ShelfStatus?.UpdateData(DeviceData.ModuleData);

            MakeTotalDataAndStatus();
            MainStatus?.UpdateData(DeviceData);
            uDataLog.UpdateData(DeviceData);
        }

        // 핸들러 (대응되는 형태)
        private void UartRecvData(object sender, DataRecvEventArgs e)
        {
            // e.Data 사용
            FwUpdateForm?.RecvData(e.Data);
            //HistoryForm?.RecvUartData(e.Data); // 메서드명 확인 (오타 주의)
        }

        // 핸들러 (대응되는 형태)
        private void CommDataPrint(object sender, PrintEventArgs e)
        {
            var id = e.Id;
            var msg = e.Message;
            if (!string.IsNullOrEmpty(msg))
                uEventLog.EventUpdate(id, msg + "\r\n");
        }

        private void ErrorOnPrint(object sender, PrintEventArgs e)
        {
            var id = e.Id;
            var msg = e.Message;
            if (!string.IsNullOrEmpty(e.Message))
                uEventLog.ErrorUpdate(id, msg + "\r\n");
        }
        #endregion

        #region EVENT DB COMMAND TO THREAD
        private void UpdateAlarmHistory(DataTable aAlarmHistory)
        {
            uEventLog.AlarmHistoryUpdate1(aAlarmHistory);
        }
        #endregion
    }
}