using DevExpress.XtraLayout;
using LFP_Manager.DataStructure;
using LFP_Manager.Utils;
using System;
using System.Globalization;
using System.Windows.Forms;

namespace LFP_Manager.Controls
{
    public partial class ucCalibration : DevExpress.XtraEditors.XtraUserControl
    {
        #region DELEGATE

        public delegate void setCalibUpdate(object sender);
        public delegate void setCalibCommand(int mode, int index, int flag, ref DeviceParamData aParm, ref DeviceCalibration aCalib);
        public delegate Int32 getBattData(int item, int cno);

        #endregion

        #region ENUMS

        private enum ControlLevel
        {
            User = 0,
            Technician = 1,
            Engineer = 2,
            Master = 3
        }

        private enum CommandMode
        {
            SetCapacity = 15,
            SetChargeMode = 16,
            SetDateTime = 17,
            SetAntiTheft = 18,
            ClearAntiTheft = 19,
            SetRS485Timeout = 20,
            SetBalanceVoltage = 21,
            SetBalanceDiff = 22
        }

        #endregion

        #region VARIABLES

        const int CALC_INDEX = 6;

        CommConfig Config;

        DeviceSystemData SystemData;
        DeviceCalibration rCalib;
        DeviceCalibration wCalib;

        DeviceParamData rParam;

        public event setCalibCommand OnCommand = null;

        int wFlag = 0;

        int dFlag;

        int PasswordResult;

        #endregion

        #region CONSTRUCTORS

        // Battery No:
        //     0: No 1,3,5,7,9,11,13,15 ODD
        //     1: No 2,4,6,8,10,12,14 Even
        public ucCalibration()
        {
            InitializeComponent();

            ComponentLoad();

            Config = new CommConfig();

            dFlag = 0;
        }

        private void ComponentLoad()
        {
            CbNewChaMode.Clear();
            for (int i = 0; i < csConstData.SystemInfo.CHG_MODE.Length; i++)
            {
                CbNewChaMode.Properties.Items.Add(csConstData.SystemInfo.CHG_MODE[i]);
            }

            teNewCapacity.KeyPress += TextBox_KeyPress;
            teNewSoc.KeyPress += TextBox_KeyPress;
            TeCommTimeoutNew.KeyPress += TextBox_KeyPress;
            TeChgModeValueNew.KeyPress += TextBox_KeyPress2;
        }
        #endregion

        #region EXT EVENT FUNCTION

        private void OnCommnadEvent(int mode, int index, int flag, ref DeviceParamData aParam, ref DeviceCalibration aCalib)
        {
            OnCommand?.Invoke(mode, index, flag, ref aParam, ref aCalib);
        }

        #endregion

        #region PUBLIC FUCTIONS

        public void UpdateConfig(CommConfig aConfig)
        {
            Config = aConfig;
        }
        public void UpdateData(DeviceSystemData aSystemData, DeviceCalibration aCalib)
        {
            SystemData = aSystemData;
            rCalib = aCalib;
            DisplayCalib();
            UpdateNewCalib();
        }

        private void UpdateNewCalib()
        {
            if (SystemData != null)
            {
                if (wFlag == 0)
                {
                    teNewCapacity.Text = teCurrCapacity.Text;
                    teNewSoc.Text = teCurrSoc.Text;

                    wFlag = 1;
                }
            }
        }
        #endregion

        #region DISPLAY DATA

        private void DisplayCalib()
        {
            if (SystemData != null)
            {
                teCurrCapacity.Text = string.Format("{0}", SystemData.ValueData.designedCapacity / 10);
                teCurrSoc.Text = string.Format("{0}", SystemData.ValueData.rSOC / 10);

                int mode = SystemData.CalibrationData.ChaMode.Mode;
                int idx = MapChaModeToComboIndex(mode);
                if (idx >= 0 && idx < csConstData.SystemInfo.CHG_MODE.Length)
                    TeChaMode.Text = csConstData.SystemInfo.CHG_MODE[idx];
                else
                    TeChaMode.Text = string.Format(CultureInfo.InvariantCulture, "UNKNOWN({0})", mode);

                TeChgModeValueCurr.Text = string.Format("{0:0.00}", Convert.ToDouble(SystemData.CalibrationData.ChaMode.Value) / 100);

                TeCurrDateTime.Text = SystemData.BmsDateTime.DateTimeStr;

                // Anti-Theft Data Display
                if (SystemData.CalibrationData.AntiTheft.GyroScope == 0)
                {
                    TeAntiTheftGyroCurr.Text = "DISABLE";
                }
                else
                {
                    TeAntiTheftGyroCurr.Text = "ENABLE";
                }
                TeSensitive.Text = string.Format("{0}", SystemData.CalibrationData.AntiTheft.GyroScope);

                // Anti-Theft - RS-485 Comm. Timeout
                TeCommTimeout.Text = string.Format("{0}", SystemData.CalibrationData.AntiTheft.Comm);

                if (dFlag == 0)
                {
                    CbNewChaMode.SelectedIndex = 1;
                    CbNewChaMode.SelectedIndex = SystemData.CalibrationData.ChaMode.Mode;

                    TeChgModeValueNew.Text = TeChgModeValueCurr.Text;

                    dFlag = 1;
                }
            }
        }

        #endregion

        #region BUTTON EVENT

        private void TextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            csUtils.TypingOnlyNumber(sender, e, false, false);
        }
        private void TextBox_KeyPress2(object sender, KeyPressEventArgs e)
        {
            csUtils.TypingOnlyNumber(sender, e, true, false);
        }

        private void btnCapacitySet_Click(object sender, EventArgs e)
        {
            try
            {
                if (!ValidateNumericInput(teNewCapacity.Text, "Capacity") ||
                    !ValidateNumericInput(teNewSoc.Text, "SOC"))
                {
                    return;
                }

                wCalib = rCalib.DeepCopy();
                wCalib.CapCalib.DesignCapacity = Convert.ToInt32(teNewCapacity.Text) * 10;
                wCalib.CapCalib.SocValue = Convert.ToInt32(teNewSoc.Text);

                OnCommnadEvent((int)CommandMode.SetCapacity, CALC_INDEX, 1, ref rParam, ref wCalib);
            }
            catch (Exception ex)
            {
                ShowError($"Failed to set capacity: {ex.Message}");
            }
        }

        private void BtnChaModeSet_Click(object sender, EventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(CbNewChaMode.Text))
                {
                    ShowError("Please select a charge mode");
                    return;
                }

                if (CbNewChaMode.SelectedIndex > 1 &&
                    !ValidateNumericInput(TeChgModeValueNew.Text, "Charge Mode Value"))
                {
                    return;
                }

                wCalib = rCalib.DeepCopy();
                wCalib.ChaMode.Mode = CbNewChaMode.SelectedIndex;
                wCalib.ChaMode.Value = (int)(Convert.ToDouble(TeChgModeValueNew.Text) * 100);

                OnCommnadEvent((int)CommandMode.SetChargeMode, CALC_INDEX, 1, ref rParam, ref wCalib);
            }
            catch (Exception ex)
            {
                ShowError($"Failed to set charge mode: {ex.Message}");
            }
        }
        private void BtnBmsDateTimeSet_Click(object sender, EventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(TeNewDateTime.Text))
                {
                    ShowError("Please enter a date/time value");
                    return;
                }

                DateTime dateTime = Convert.ToDateTime(TeNewDateTime.Text);
                int iDateTime = CalculateDateTimeValue(dateTime);

                wCalib = rCalib.DeepCopy();
                wCalib.BmsDateTime.sValue[0] = (short)(iDateTime >> 16);
                wCalib.BmsDateTime.sValue[1] = (short)(iDateTime >> 0);

                OnCommnadEvent((int)CommandMode.SetDateTime, CALC_INDEX, 1, ref rParam, ref wCalib);
            }
            catch (Exception ex)
            {
                ShowError($"Failed to set date/time: {ex.Message}");
            }
        }
        private int CalculateDateTimeValue(DateTime dateTime)
        {
            return ((dateTime.Year - 2000) << 26)
                | (dateTime.Month << 22)
                | (dateTime.Day << 17)
                | (dateTime.Hour << 12)
                | (dateTime.Minute << 6)
                | (dateTime.Second << 0);
        }

        private void BtnGetCurrTime_Click(object sender, EventArgs e)
        {
            TeNewDateTime.Text = string.Format("{0:yyyy-MM-dd HH:mm:ss}", DateTime.Now);
        }

        private void BtnClearAntiTheftProtect_Click(object sender, EventArgs e)
        {
            wCalib = rCalib.DeepCopy();
            wCalib.AntiTheft.GyroScope = Config.GyroSensitive;

            OnCommnadEvent(19, CALC_INDEX, 1, ref rParam, ref wCalib);
        }

        private void PassWordResultProc(int result)
        {
            PasswordResult = result;
        }

        private void BtnAntiTheftEnable_Click(object sender, EventArgs e)
        {
            wCalib = rCalib.DeepCopy();
            wCalib.AntiTheft.GyroScope = Config.GyroSensitive;

            OnCommnadEvent(18, CALC_INDEX, 1, ref rParam, ref wCalib);
        }

        private void BtnAntiTheftDisable_Click(object sender, EventArgs e)
        {
            wCalib = rCalib.DeepCopy();
            wCalib.AntiTheft.GyroScope = 0;

            OnCommnadEvent(18, CALC_INDEX, 1, ref rParam, ref wCalib);
        }

        private void BtnRS485TimeoutSet_Click(object sender, EventArgs e)
        {
            string stTimeout = TeCommTimeoutNew.Text;
            int Timeout = 0;

            try
            {
                Timeout = Convert.ToInt32(stTimeout);
            }
            catch (Exception)
            {
                MessageBox.Show("Timeout data Format Error", "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            wCalib = rCalib.DeepCopy();
            wCalib.AntiTheft.Comm = Timeout;

            OnCommnadEvent(20, CALC_INDEX, 1, ref rParam, ref wCalib);
        }
        #endregion

        #region COMPONENT EVENT
        private void CbNewChaMode_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (CbNewChaMode.SelectedIndex)
            {
                case 0:
                    TeChgModeValueNew.Enabled = false;
                    break;
                case 1:
                    TeChgModeValueNew.Enabled = false;
                    break;
                case 2:
                    TeChgModeValueNew.Enabled = true;
                    break;
                case 3:
                    TeChgModeValueNew.Enabled = true;
                    break;
                case 4:
                    TeChgModeValueNew.Enabled = true;
                    break;
                default:
                    TeChgModeValueNew.Enabled = false;
                    break;
            }
        }
        #endregion

        #region FORM EVENT
        private void ucCalibration_Load(object sender, EventArgs e)
        {
            CalibConfig_ReLoad();
        }

        private void CalibConfig_ReLoad()
        {
            var level = (ControlLevel)Config.ControlLevel;

            btnCapacitySet.Enabled = level == ControlLevel.Master;
            BtnChaModeSet.Enabled = level >= ControlLevel.Engineer;
            BtnBmsDateTimeSet.Enabled = level >= ControlLevel.Engineer;
            BtnClearAntiTheftProtect.Enabled = level >= ControlLevel.Technician;
            BtnAntiTheftEnable.Enabled = level >= ControlLevel.Engineer;
            BtnAntiTheftDisable.Enabled = level >= ControlLevel.Engineer;
            BtnRS485TimeoutSet.Enabled = level >= ControlLevel.Technician;
        }

        #endregion

        #region VALIDATION METHODS

        private bool ValidateNumericInput(string input, string fieldName)
        {
            if (string.IsNullOrEmpty(input))
            {
                ShowError($"Please enter a value for {fieldName}");
                return false;
            }

            if (!int.TryParse(input, out _))
            {
                ShowError($"Invalid numeric value for {fieldName}");
                return false;
            }

            return true;
        }

        private void ShowError(string message)
        {
            MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        #endregion

        #region HELPER FUNCTIONS
        // Charging Mode 매핑: 콤보 인덱스 <-> ChaMode.Mode
        private static bool TryMapIndexToChaMode(int comboIndex, out int mode)
        {
            // UI: 0=CC,1=CV,2=CC-CV(x),3=Time(x),4=Power(x) (예시)
            // 장비: 0,1,2,4,8 로 매핑
            switch (comboIndex)
            {
                case 0: mode = 0; return true;
                case 1: mode = 1; return true;
                case 2: mode = 2; return true;
                case 3: mode = 4; return true;
                case 4: mode = 8; return true;
                default: mode = -1; return false;
            }
        }
        private static int MapChaModeToComboIndex(int mode)
        {
            switch (mode)
            {
                case 0: return 0;
                case 1: return 1;
                case 2: return 2;
                case 4: return 3;
                case 8: return 4;
                default: return -1;
            }
        }
        private static bool TryParseDouble(string text, out double value)
        {
            return double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
        }

        private static string ToInvStr(double v, string fmt) =>
            v.ToString(fmt, CultureInfo.InvariantCulture);

        // BMS DateTime 비트 인코딩 (검증 포함)
        private static bool TryEncodeBmsDateTime(DateTime dt, out short hi, out short lo, out string error)
        {
            error = null;
            int year = dt.Year;
            if (year < 2000 || year > 2063) { error = "Year must be 2000~2063"; hi = lo = 0; return false; }
            int y = year - 2000;
            int m = dt.Month; if (m < 1 || m > 12) { error = "Month out of range"; hi = lo = 0; return false; }
            int d = dt.Day; if (d < 1 || d > 31) { error = "Day out of range"; hi = lo = 0; return false; }
            int hh = dt.Hour; if (hh < 0 || hh > 23) { error = "Hour out of range"; hi = lo = 0; return false; }
            int mm = dt.Minute; if (mm < 0 || mm > 59) { error = "Minute out of range"; hi = lo = 0; return false; }
            int ss = dt.Second; if (ss < 0 || ss > 59) { error = "Second out of range"; hi = lo = 0; return false; }

            int packed = (y << 26) | (m << 22) | (d << 17) | (hh << 12) | (mm << 6) | ss;
            hi = (short)(packed >> 16);
            lo = (short)(packed & 0xFFFF);
            return true;
        }
        #endregion
    }
}
