using DevExpress.Utils;
using DevExpress.XtraEditors.Repository;
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Columns;
using DevExpress.XtraGrid.Views.Grid;

using LFP_Manager.DataStructure;
using LFP_Manager.Function;
using LFP_Manager.Utils;

using System;
using System.Data;
using System.Windows.Forms;

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

        private int SystemId = 0;
        private CommConfig Config;

        private byte[] rData = null;
        private ushort curr_addr = 0;
        private int recv_data_len = 0;

        private int hist_no = 0;
        private bool stopReq = true;

        private int cell_qty = 16;
        private int temp_qty = 4;

        private int req_timeoutCount = 0;

        private DateTime ReqDateTime;

        private DataTable BmsHistoryTable = null;

        public event SendDataUartEvent OnSendUartData = null;

        #endregion

        #region CONTRUCTORS

        public fmxHistory(CommConfig aConfig, int systemId)
        {
            InitializeComponent();

            Config = aConfig;

            switch (Config.UartModelIndex)
            {
                case csConstData.MODEL_INDEX.LFPM_124050D:
                    recv_data_len = 112;
                    cell_qty = 39;
                    temp_qty = 8;
                    break;
                default:
                    recv_data_len = 58;
                    cell_qty = 16;
                    temp_qty = 4;
                    break;
            }

            ucHistroy1.SetCommCofig(Config);
            SystemId = systemId;

            CreateHistoryColumn(ref BmsHistoryTable);
            BuildHistoryGridColumns(gcBmsHistory, gvBmsHistory, BmsHistoryTable);

            gvBmsHistory.Appearance.HeaderPanel.TextOptions.HAlignment = HorzAlignment.Center;
            gvBmsHistory.Appearance.HeaderPanel.TextOptions.VAlignment = VertAlignment.Center;

            switch (Config.CommType)
            {
                case csConstData.CommType.COMM_UART:
                    pgBmsHistory.PageEnabled = true;
                    //tabHistory.TabPages.Remove(pgBmsHistory);
                    break;
                case csConstData.CommType.COMM_RS485:
                    pgBmsHistory.PageEnabled = false;
                    tabHistory.TabPages.Remove(pgBmsHistory);
                    break;
                case csConstData.CommType.COMM_SNMP:
                    pgBmsHistory.PageEnabled = false;
                    tabHistory.TabPages.Remove(pgBmsHistory);
                    break;
                default:
                    break;
            }
        }

        #endregion

        #region INITIALIZE COMPONENT

        private void CreateHistoryColumn(ref DataTable hDT)
        {
            hDT = new DataTable();

            hDT.Columns.Add("no", typeof(int));
            hDT.Columns.Add("datetime", typeof(DateTime));
            hDT.Columns.Add("status", typeof(int));
            hDT.Columns.Add("warning", typeof(string));
            hDT.Columns.Add("protect", typeof(string));
            hDT.Columns.Add("error", typeof(string));

            hDT.Columns.Add("voltage", typeof(decimal));
            hDT.Columns.Add("current", typeof(decimal));
            hDT.Columns.Add("SOC", typeof(decimal));

            hDT.Columns.Add("pcb_t", typeof(decimal));
            hDT.Columns.Add("amb_t", typeof(decimal));

            for (int i = 0; i < temp_qty; i++)
            {
                hDT.Columns.Add(string.Format("temp_{0:00}", i + 1), typeof(decimal));
            }
            for (int i = 0; i < cell_qty; i++)
            {
                hDT.Columns.Add(string.Format("cell_v_{0:00}", i + 1), typeof(decimal));
            }
            gcBmsHistory.DataSource = hDT;
        }

        private void BuildHistoryGridColumns(GridControl grid, GridView view, DataTable dt)
        {
            grid.BeginUpdate();
            view.BeginUpdate();

            try
            {
                // 1️⃣ DataSource 바인딩
                grid.DataSource = dt;

                // 2️⃣ 기존 컬럼 제거
                view.Columns.Clear();

                // 3️⃣ RepositoryItems 준비

                RepositoryItemTextEdit repoText = new RepositoryItemTextEdit
                {
                    ReadOnly = true
                };

                RepositoryItemSpinEdit repoInt = new RepositoryItemSpinEdit
                {
                    IsFloatValue = false,
                    Mask = { EditMask = "N0", UseMaskAsDisplayFormat = true }
                };

                RepositoryItemSpinEdit repoF1 = new RepositoryItemSpinEdit
                {
                    IsFloatValue = true,
                    Mask = { EditMask = "F1", UseMaskAsDisplayFormat = true }
                };

                RepositoryItemSpinEdit repoF2 = new RepositoryItemSpinEdit
                {
                    IsFloatValue = true,
                    Mask = { EditMask = "F2", UseMaskAsDisplayFormat = true }
                };

                RepositoryItemSpinEdit repoF3 = new RepositoryItemSpinEdit
                {
                    IsFloatValue = true,
                    Mask = { EditMask = "F3", UseMaskAsDisplayFormat = true }
                };

                RepositoryItemDateEdit repoDate = new RepositoryItemDateEdit
                {
                    Mask = { EditMask = "yyyy-MM-dd HH:mm:ss", UseMaskAsDisplayFormat = true }
                };

                grid.RepositoryItems.AddRange(new RepositoryItem[]
                {
                    repoText, repoInt, repoF1, repoF2, repoF3, repoDate
                });

                int visibleIndex = 0;

                // 4️⃣ DataTable 컬럼 기준 GridColumn 생성
                foreach (DataColumn dc in dt.Columns)
                {
                    GridColumn col = new GridColumn
                    {
                        FieldName = dc.ColumnName,
                        Name = "gc_" + dc.ColumnName,
                        Caption = GetHistoryColumnCaption(dc.ColumnName), // ⭐ 헤더 변경
                        Visible = true,
                        VisibleIndex = visibleIndex++
                    };

                    string name = dc.ColumnName.ToLower();

                    // ───── 타입/이름 기준 포맷 규칙 ─────

                    if (name == "no")
                    {
                        col.ColumnEdit = repoInt;
                        col.DisplayFormat.FormatType = FormatType.Numeric;
                        col.DisplayFormat.FormatString = "N0";
                        col.AppearanceCell.TextOptions.HAlignment = HorzAlignment.Center;
                    }
                    else if (name == "datetime")
                    {
                        col.ColumnEdit = repoDate;
                        col.DisplayFormat.FormatType = FormatType.DateTime;
                        col.DisplayFormat.FormatString = "yyyy-MM-dd HH:mm:ss";
                    }
                    else if (name == "status")
                    {
                        col.ColumnEdit = repoText;
                        // ✅ status는 표시가 문자열이므로 가운데 정렬 강제
                        col.AppearanceCell.Options.UseTextOptions = true;   // ⭐⭐⭐ 핵심 ⭐⭐⭐
                        col.AppearanceCell.TextOptions.HAlignment = HorzAlignment.Center;

                        // (선택) 숫자 표시 포맷/에디터 굳이 필요 없으면 빼도 됨
                        // col.ColumnEdit = repoInt;
                    }
                    else if (name == "voltage" || name == "current" || name == "soc")
                    {
                        col.ColumnEdit = repoF2;
                        col.DisplayFormat.FormatType = FormatType.Numeric;
                        col.DisplayFormat.FormatString = "0.00";
                    }
                    else if (name == "pcb_t" || name == "amb_t" || name.StartsWith("temp_"))
                    {
                        col.ColumnEdit = repoF1;
                        col.DisplayFormat.FormatType = FormatType.Numeric;
                        col.DisplayFormat.FormatString = "0.0";
                    }
                    else if (name.StartsWith("cell_v_"))
                    {
                        col.ColumnEdit = repoF3;
                        col.DisplayFormat.FormatType = FormatType.Numeric;
                        col.DisplayFormat.FormatString = "0.000";
                    }
                    else
                    {
                        // warning / protect / error (string)
                        col.ColumnEdit = repoText;
                        col.AppearanceCell.Options.UseTextOptions = true;   // ⭐⭐⭐ 핵심 ⭐⭐⭐
                        col.AppearanceCell.TextOptions.HAlignment = HorzAlignment.Center;
                    }

                    view.Columns.Add(col);
                }

                // 5️⃣ 전체 헤더 가운데 정렬 (요청하신 설정)
                view.Appearance.HeaderPanel.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Center;
                view.Appearance.HeaderPanel.TextOptions.VAlignment = DevExpress.Utils.VertAlignment.Center;

                // 6️⃣ Grid 기본 옵션
                view.OptionsView.ColumnAutoWidth = false;
                view.OptionsView.ShowGroupPanel = false;
                view.OptionsBehavior.Editable = false; // History Grid → 읽기 전용 권장
            }
            finally
            {
                view.EndUpdate();
                grid.EndUpdate();
            }
        }

        private string GetHistoryColumnCaption(string fieldName)
        {
            string name = fieldName.ToLower();

            if (name == "no") return "No";
            if (name == "datetime") return "Date / Time";
            if (name == "status") return "Status";

            if (name == "warning") return "Warning Flags";
            if (name == "protect") return "Protect Flags";
            if (name == "error") return "Error Flags";

            if (name == "voltage") return "Pack Voltage (V)";
            if (name == "current") return "Pack Current (A)";
            if (name == "soc") return "SOC (%)";

            if (name == "pcb_t") return "PCB Temp (°C)";
            if (name == "amb_t") return "Ambient Temp (°C)";

            if (name.StartsWith("temp_"))
            {
                int idx = int.Parse(name.Substring(5));
                return $"Temp {idx} (°C)";
            }

            if (name.StartsWith("cell_v_"))
            {
                int idx = int.Parse(name.Substring(7));
                return $"Cell {idx} Voltage (V)";
            }

            // 기본: 컬럼명 그대로 (Fallback)
            return fieldName;
        }

        #endregion

        #region PUBLIC FUNCTIONS
        public void RecvUartData(byte[] data)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new Action(() => RecvUartData(data)));
                return;
            }

            rData = data;

            LcGrResult.Text = $"Result ({rData.Length})";

            if ((rData != null) && (rData.Length == recv_data_len))
            {
                try
                {
                    req_timeoutCount = 0;

                    DataRow hRow = BmsHistoryTable.NewRow();

                    short[] shorts = csUtils.ByteArrTo2ShortSafe(data);

                    int pos = 0;
                    int yy, MM, dd, HH, mm, ss;
                    Int32 iDateTime = (rData[3 + 2] << 24) | (rData[3 + 3] << 16) | (rData[3 + 0] << 8) | (rData[3 + 1] << 0);
                    //Int32 iDateTime = (Int32)((shorts[pos + 1] << 16) | (shorts[pos + 0] << 0));
                    yy = (iDateTime >> 26) & 0x003F;
                    MM = (iDateTime >> 22) & 0x000F;
                    dd = (iDateTime >> 17) & 0x001F;
                    HH = (iDateTime >> 12) & 0x001F;
                    mm = (iDateTime >> 6) & 0x003F;
                    ss = (iDateTime >> 0) & 0x003F;

                    hRow["no"] = hist_no;
                    string stDateTime = string.Format("{0}-{1:00}-{2:00} {3:00}:{4:00}:{5:00}"
                        , yy
                        , MM
                        , dd
                        , HH
                        , mm
                        , ss
                        );

                    DateTime aDateTime;

                    if (MM < 1 || MM > 12 || dd < 1 || dd > 31 || HH > 23 || mm > 59 || ss > 59)
                        aDateTime = DateTime.MinValue; // 또는 DateTime.MinValue 넣기
                    else
                        aDateTime = Convert.ToDateTime(stDateTime);

                    hRow["datetime"] = aDateTime;

                    pos = 2;
                    hRow["status"] = shorts[pos++];
                    hRow["warning"] = string.Format("{0:X4}", shorts[pos++]);
                    hRow["protect"] = string.Format("{0:X4}", shorts[pos++]);
                    hRow["error"] = string.Format("{0:X4}", shorts[pos++]);
                    hRow["voltage"] = shorts[pos++] / 100m;
                    hRow["current"] = shorts[pos++] / 100m;
                    hRow["SOC"] = shorts[pos++] / 1m;

                    hRow["pcb_t"] = (byte)(shorts[pos] >> 8) / 1m;
                    hRow["amb_t"] = (byte)(shorts[pos] >> 0) / 1m;
                    pos++;

                    // Temperature
                    for (int i = 0; i < (temp_qty / 2); i++)
                    {
                        hRow[string.Format("temp_{0:00}", (i * 2) + 1)] = Convert.ToDouble((byte)(shorts[pos] >> 8));
                        hRow[string.Format("temp_{0:00}", (i * 2) + 2)] = Convert.ToDouble((byte)(shorts[pos++] >> 0));
                    }
                    // Cell Voltage
                    for (int i = 0; i < cell_qty; i++)
                    {
                        hRow[string.Format("cell_v_{0:00}", i + 1)] = Convert.ToDecimal(shorts[pos++]) / 1000m;
                    }
                    //hRow["SOH"] = shorts[pos++] / 1m;

                    BmsHistoryTable.Rows.Add(hRow);
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message);
                }
            }

            hist_no++;
            curr_addr += 0x20;
            RequestHistoryData(curr_addr);
        }

        #endregion

        #region BUTTON EVENT

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

        private void btnBmsHistoryReq_Click(object sender, EventArgs e)
        {
            if (stopReq)
            {
                BmsHistoryTable.Rows.Clear();

                stopReq = false;
                hist_no = 1;
                curr_addr = 0x0200;
                TmrCheckReq.Enabled = true;

                RequestHistoryData(curr_addr);

                btnBmsHistoryReq.Text = "STOP";
            }
            else
            {
                StopAndFinish("User Stop Request");
            }
        }

        private void RequestHistoryData(ushort haddr)
        {
            byte[] crc;
            ushort addr = haddr;

            // Normal mode --> Bootloader mode
            byte[] sdata = new byte[8];
            sdata[0] = (byte)SystemId;         // Dev Address
            sdata[1] = 0x03;         // Function Code
            sdata[2] = (byte)(addr >> 8);         // Address H
            sdata[3] = (byte)(addr >> 0);         // Address L
            sdata[4] = 0x00;         // Number of Register H
            sdata[5] = 0x35;         // Number of Register L

            crc = csSerialCommFunction.GetCRC(sdata, 6);

            sdata[6] = crc[1];         // CRCH
            sdata[7] = crc[0];         // CRCL

            if (stopReq == false)
            {
                OnSendUartData?.Invoke(addr, sdata, false, 1);
                ReqDateTime = DateTime.Now;
            }
        }

        private void StopAndFinish(string reason = null)
        {
            stopReq = true;
            TmrCheckReq.Enabled = false;
            btnBmsHistoryReq.Text = "Request";

            req_timeoutCount = 0;

            gvBmsHistory.BestFitColumns();
            gvBmsHistory.ClearSelection();

            if (!string.IsNullOrEmpty(reason))
                MessageBox.Show($"Finished: {reason}");
            else
                MessageBox.Show("Finished BMS History Read");
        }

        private void btnExportExcel_Click(object sender, EventArgs e)
        {
            SaveFileDialog sDialog = new SaveFileDialog();
            sDialog.Title = "Select save file";
            sDialog.DefaultExt = "xlsx";
            sDialog.Filter = "Excel files (*.xlsx)|*.xlsx|All files (*.*)|*.*";

            if (sDialog.ShowDialog() == DialogResult.OK)
            {
                string filename = sDialog.FileName;

                try
                {
                    //csExcelExport.ExportToExcelExt(BmsHistoryTable, filename);
                    DataTable[] export = new DataTable[1];
                    export[0] = BmsHistoryTable;
                    csExcelFunction.ExportDgvWithClosedXml(filename, export);

                    MessageBox.Show("Complete Export Excel File", "Result", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
        #endregion

        #region GRID DISPLAY
        private void gvBmsHistory_CustomColumnDisplayText(object sender, DevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventArgs e)
        {
            if (e.Column.FieldName == "status")
            {
                int status = Convert.ToInt32(e.Value);
                switch (status)
                {
                    case 0:
                        e.DisplayText = "Standby";
                        break;
                    case 1:
                        e.DisplayText = "Charging";
                        break;
                    case 2:
                        e.DisplayText = "Discharging";
                        break;
                    case 4:
                        e.DisplayText = "Protect";
                        break;
                    case 8:
                        e.DisplayText = "Charging-Lmt";
                        break;
                    case 0x32:
                        e.DisplayText = "Power-On";
                        break;
                    default:
                        e.DisplayText = string.Format("Unknown({0:X4})", status);
                        break;
                }
            }
        }
        #endregion

        #region TIMER EVENT
        private void TmrCheckReq_Tick(object sender, EventArgs e)
        {
            DateTime nDateTime = ReqDateTime.AddSeconds(1);

            if (DateTime.Now - ReqDateTime > TimeSpan.FromSeconds(1))
            {
                req_timeoutCount++;

                if (req_timeoutCount >= 3)
                {
                    StopAndFinish("Read Finished");
                }
            }
        }
        #endregion

        #region FORM EVENT
        private void fmxHistory_Load(object sender, EventArgs e)
        {
            //SetGridColumnFormat();
        }
        #endregion
    }
}
