using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LFP_Manager.DataStructure;
using LFP_Manager.Utils;
using LFP_Manager.Controls;

namespace LFP_Manager.Function
{
    /// <summary>
    /// BMS 장치와의 시리얼 통신을 위한 개선된 클래스
    /// Modbus RTU 프로토콜 및 커스텀 프로토콜 지원
    /// </summary>
    public class CsRs232CommFunction124050
    {
        #region Constants - Modbus Function Codes
        public const byte READ_COIL_STATUS = 0x01;
        public const byte READ_HOLDING_REG = 0x03;
        public const byte READ_INPUT_REG = 0x04;
        public const byte FORCE_SINGLE_COIL = 0x05;
        public const byte PRESET_SINGLE_REG = 0x06;
        public const byte WRITE_COIL_REG = 0x0F;
        public const byte PRESET_MULTI_REG = 0x10;
        public const byte ERROR_REG = 0x90;
        public const byte FW_FLASH_ERASE_CMD = 0x43;
        public const byte FW_FLASH_WRITE_CMD = 0x31;
        public const byte NO_CMD = 0xFF;
        #endregion

        #region Constants - Register Addresses
        private static class RegisterAddress
        {
            // Basic Data
            public const int PACK_VOLTAGE = 0;
            public const int PACK_CURRENT = 1;
            public const int CELL_VOLTAGE_START = 2;
            public const int CELL_VOLTAGE_END = 17;
            public const int EXT1_TEMPERATURE = 18;
            public const int EXT2_TEMPERATURE = 19;
            public const int REMAINING_CAPACITY = 21;
            public const int MAX_CHARGE_CURRENT = 22;
            public const int STATE_OF_HEALTH = 23;
            public const int STATE_OF_CHARGE = 24;

            // Status Registers
            public const int OPERATING_STATUS = 25;
            public const int WARNING_STATUS = 26;
            public const int PROTECTION_STATUS = 27;
            public const int ERROR_CODE = 28;
            public const int CYCLE_COUNT_MSB = 29;
            public const int CYCLE_COUNT_LSB = 30;

            // Temperature Registers (Packed)
            public const int CELL_TEMP_START = 32;
            public const int CELL_TEMP_END = 35;

            // Device Info
            public const int CELL_QTY = 36;
            public const int DESIGNED_CAPACITY = 37;
            public const int CELL_BALANCE_STATUS = 38;
            public const int DATETIME_MSB = 45;
            public const int DATETIME_LSB = 46;
            public const int SPECIAL_ALARM = 49;

            // Protection Parameters
            public const int LOW_SOC_WARNING = 58;
            public const int CELL_UV_WARNING = 61;
            public const int CELL_UV_TRIP = 62;
            public const int CELL_UV_RELEASE = 63;
            public const int SYS_UV_WARNING = 64;
            public const int SYS_UV_TRIP = 65;
            public const int SYS_UV_RELEASE = 66;
            public const int CELL_OV_WARNING = 67;
            public const int CELL_OV_TRIP = 68;
            public const int CELL_OV_RELEASE = 69;
            public const int SYS_OV_WARNING = 70;
            public const int SYS_OV_TRIP = 71;
            public const int SYS_OV_RELEASE = 72;

            // Current Protection
            public const int CHA_OC_TIMES = 76;
            public const int DCH_OC_TIMES = 77;
            public const int CHA_OC_RELEASE_TIME = 78;
            public const int DCH_OC_RELEASE_TIME = 79;
            public const int CHA_OC_TRIP1 = 80;
            public const int DCH_OC_TRIP1 = 81;
            public const int SHORT_CIRCUIT = 82;
            public const int CHA_OC_TRIP2 = 83;
            public const int DCH_OC_TRIP2 = 84;
            public const int CHA_OC_DELAY1 = 85;
            public const int CHA_OC_DELAY2 = 86;
            public const int DCH_OC_DELAY1 = 87;
            public const int DCH_OC_DELAY2 = 88;

            // Temperature Protection
            public const int CHA_LOW_TEMP_WARNING = 90;
            public const int CHA_LOW_TEMP_TRIP = 91;
            public const int CHA_LOW_TEMP_RELEASE = 92;
            public const int CHA_HIGH_TEMP_WARNING = 93;
            public const int CHA_HIGH_TEMP_TRIP = 94;
            public const int CHA_HIGH_TEMP_RELEASE = 95;
            public const int DCH_LOW_TEMP_WARNING = 96;
            public const int DCH_LOW_TEMP_TRIP = 97;
            public const int DCH_LOW_TEMP_RELEASE = 98;
            public const int DCH_HIGH_TEMP_WARNING = 99;
            public const int DCH_HIGH_TEMP_TRIP = 100;
            public const int DCH_HIGH_TEMP_RELEASE = 101;

            // Device Information
            public const int MODEL_NAME_START = 105;
            public const int MODEL_NAME_END = 116;
            public const int FW_VERSION_START = 117;
            public const int FW_VERSION_END = 119;
            public const int SERIAL_NUMBER_START = 120;
            public const int SERIAL_NUMBER_END = 127;

            // Extended Cell Voltages
            public const int EXT_CELL_VOLTAGE_START = 138;
            public const int EXT_CELL_VOLTAGE_END = 160;

            // Manufacturing Date
            public const int MANU_DATE_START = 163;
            public const int MANU_DATE_END = 166;
        }
        #endregion

        #region Delegates and Events
        public delegate void LogEventHandler(string message, LogLevel level);
        public static event LogEventHandler OnLog;

        public enum LogLevel
        {
            Info,
            Warning,
            Error
        }
        #endregion

        #region Modbus Frame Construction
        /// <summary>
        /// Modbus 읽기 요청 프레임 생성
        /// </summary>
        public static byte[] CreateReadRegisterFrame(byte deviceId, byte functionCode, ushort startAddress, ushort quantity)
        {
            if (quantity == 0 || quantity > 125)
                throw new ArgumentException("Invalid register quantity");

            var frame = new byte[8];
            frame[0] = deviceId;
            frame[1] = functionCode;
            frame[2] = (byte)(startAddress >> 8);
            frame[3] = (byte)(startAddress & 0xFF);
            frame[4] = (byte)(quantity >> 8);
            frame[5] = (byte)(quantity & 0xFF);

            var crc = csUtils.CalculateCRC(frame, 6);
            frame[6] = crc[0];
            frame[7] = crc[1];

            return frame;
        }

        /// <summary>
        /// Modbus 다중 레지스터 쓰기 요청 프레임 생성
        /// </summary>
        public static byte[] CreateWriteMultipleRegistersFrame(byte deviceId, ushort startAddress, short[] values)
        {
            if (values == null || values.Length == 0 || values.Length > 123)
                throw new ArgumentException("Invalid values array");

            var frame = new byte[9 + (values.Length * 2)];
            int index = 0;

            frame[index++] = deviceId;
            frame[index++] = PRESET_MULTI_REG;
            frame[index++] = (byte)(startAddress >> 8);
            frame[index++] = (byte)(startAddress & 0xFF);
            frame[index++] = (byte)(values.Length >> 8);
            frame[index++] = (byte)(values.Length & 0xFF);
            frame[index++] = (byte)(values.Length * 2);

            foreach (var value in values)
            {
                frame[index++] = (byte)(value >> 8);
                frame[index++] = (byte)(value & 0xFF);
            }

            var crc = csUtils.CalculateCRC(frame, index);
            frame[index++] = crc[0];
            frame[index++] = crc[1];

            return frame;
        }

        /// <summary>
        /// 단일 코일 쓰기 요청 프레임 생성
        /// </summary>
        public static byte[] CreateWriteCoilFrame(byte deviceId, ushort coilAddress, bool value)
        {
            var frame = new byte[8];
            frame[0] = deviceId;
            frame[1] = FORCE_SINGLE_COIL;
            frame[2] = (byte)(coilAddress >> 8);
            frame[3] = (byte)(coilAddress & 0xFF);
            frame[4] = value ? (byte)0xFF : (byte)0x00;
            frame[5] = 0x00;

            var crc = csUtils.CalculateCRC(frame, 6);
            frame[6] = crc[0];
            frame[7] = crc[1];

            return frame;
        }
        #endregion

        #region Frame Validation
        /// <summary>
        /// Modbus 응답 프레임 검증
        /// </summary>
        public static ValidationResult ValidateModbusResponse(byte[] data, int length)
        {
            if (data == null || length < 3)
                return new ValidationResult(false, "Insufficient data length");

            try
            {
                byte functionCode = data[1];
                int expectedLength = CalculateExpectedLength(functionCode, data, length);

                if (length < expectedLength)
                    return new ValidationResult(false, "Incomplete frame");

                var calculatedCrc = csUtils.CalculateCRC(data, length - 2);
                var receivedCrc = new byte[] { data[length - 2], data[length - 1] };

                if (calculatedCrc[0] != receivedCrc[0] || calculatedCrc[1] != receivedCrc[1])
                    return new ValidationResult(false, "CRC mismatch");

                return new ValidationResult(true, "Valid frame");
            }
            catch (Exception ex)
            {
                OnLog?.Invoke($"Frame validation error: {ex.Message}", LogLevel.Error);
                return new ValidationResult(false, ex.Message);
            }
        }

        private static int CalculateExpectedLength(byte functionCode, byte[] data, int length)
        {
            switch (functionCode)
            {
                case READ_COIL_STATUS:
                case READ_HOLDING_REG:
                case READ_INPUT_REG:
                    return length >= 3 ? data[2] + 5 : 5;
                case PRESET_MULTI_REG:
                case FORCE_SINGLE_COIL:
                case PRESET_SINGLE_REG:
                    return 8;
                case ERROR_REG:
                    return 5;
                case FW_FLASH_ERASE_CMD:
                case FW_FLASH_WRITE_CMD:
                    return 5;
                default:
                    return length;
            }
        }
        #endregion

        #region Data Processing
        /// <summary>
        /// Modbus 응답 데이터를 시스템 데이터로 변환
        /// </summary>
        public static ProcessingResult ProcessModbusResponse(byte[] data, ushort startAddress, ushort length, ref DeviceSystemData systemData)
        {
            if (data == null || systemData == null)
                return new ProcessingResult(false, "Invalid input parameters");

            try
            {
                byte functionCode = data[1];

                switch (functionCode)
                {
                    case READ_COIL_STATUS:
                        return ProcessCoilResponse(data, startAddress, length, ref systemData);
                    case READ_HOLDING_REG:
                        return ProcessHoldingRegisterResponse(data, startAddress, length, ref systemData);
                    case READ_INPUT_REG:
                        return ProcessInputRegisterResponse(data, startAddress, length, ref systemData);
                    case ERROR_REG:
                        return ProcessErrorResponse(data, ref systemData);
                    default:
                        return new ProcessingResult(false, $"Unsupported function code: {functionCode:X2}");
                }
            }
            catch (Exception ex)
            {
                OnLog?.Invoke($"Data processing error: {ex.Message}", LogLevel.Error);
                return new ProcessingResult(false, ex.Message);
            }
        }

        private static ProcessingResult ProcessHoldingRegisterResponse(byte[] data, ushort startAddress, ushort length, ref DeviceSystemData systemData)
        {
            int byteCount = data[2];
            int registerCount = byteCount / 2;
            int dataIndex = 3;

            var processors = GetRegisterProcessors();

            for (int i = 0; i < registerCount; i++)
            {
                ushort registerAddress = (ushort)(startAddress + i);
                short registerValue = (short)((data[dataIndex] << 8) | data[dataIndex + 1]);

                if (processors.TryGetValue(registerAddress, out var processor))
                {
                    processor.Invoke(registerValue, systemData);
                }

                dataIndex += 2;
            }

            // 계산된 값들 업데이트
            UpdateCalculatedValues(ref systemData);

            return new ProcessingResult(true, "Processing completed successfully");
        }

        private static ProcessingResult ProcessCoilResponse(byte[] data, ushort startAddress, ushort length, ref DeviceSystemData systemData)
        {
            // 코일 데이터 처리 로직
            return new ProcessingResult(true, "Coil processing completed");
        }

        private static ProcessingResult ProcessInputRegisterResponse(byte[] data, ushort startAddress, ushort length, ref DeviceSystemData systemData)
        {
            // 입력 레지스터 데이터 처리 로직
            return new ProcessingResult(true, "Input register processing completed");
        }

        private static ProcessingResult ProcessErrorResponse(byte[] data, ref DeviceSystemData systemData)
        {
            return new ProcessingResult(false, $"Device error: {data[2]:X2}");
        }
        #endregion

        #region Register Processing
        private static Dictionary<ushort, Action<short, DeviceSystemData>> GetRegisterProcessors()
        {
            return new Dictionary<ushort, Action<short, DeviceSystemData>>
            {
                [RegisterAddress.PACK_VOLTAGE] = (value, data) => data.ValueData.voltageOfPack = (short)(value / 10),
                [RegisterAddress.PACK_CURRENT] = (value, data) => data.ValueData.current = (short)(value / 10),
                [RegisterAddress.REMAINING_CAPACITY] = (value, data) => data.ValueData.remainingCapacity = value,
                [RegisterAddress.STATE_OF_HEALTH] = (value, data) => data.ValueData.stateOfHealth = (short)(value * 10),
                [RegisterAddress.STATE_OF_CHARGE] = (value, data) => data.ValueData.rSOC = (short)(value * 10),
                [RegisterAddress.OPERATING_STATUS] = (value, data) => data.StatusData.status = value,
                [RegisterAddress.WARNING_STATUS] = (value, data) => data.StatusData.warning = ConvertWarningData(value),
                [RegisterAddress.PROTECTION_STATUS] = (value, data) => data.StatusData.protection = ConvertProtectionData(value),
                [RegisterAddress.CELL_QTY] = (value, data) => data.recv_cellQty = value,
                [RegisterAddress.DESIGNED_CAPACITY] = (value, data) => data.ValueData.designedCapacity = value,
                // 추가 레지스터 처리기들...
            };
        }

        private static void ProcessCellVoltages(ushort address, short value, ref DeviceSystemData systemData)
        {
            if (address >= RegisterAddress.CELL_VOLTAGE_START && address <= RegisterAddress.CELL_VOLTAGE_END)
            {
                int cellIndex = address - RegisterAddress.CELL_VOLTAGE_START;
                if (cellIndex < systemData.ValueData.CellVoltage.Length)
                {
                    systemData.ValueData.CellVoltage[cellIndex] = (ushort)value;
                }
            }
            else if (address >= RegisterAddress.EXT_CELL_VOLTAGE_START && address <= RegisterAddress.EXT_CELL_VOLTAGE_END)
            {
                int cellIndex = address - RegisterAddress.EXT_CELL_VOLTAGE_START + 16;
                if (cellIndex < systemData.ValueData.CellVoltage.Length)
                {
                    systemData.ValueData.CellVoltage[cellIndex] = (ushort)value;
                }
            }
        }

        private static void ProcessCellTemperatures(ushort address, short value, ref DeviceSystemData systemData)
        {
            if (address >= RegisterAddress.CELL_TEMP_START && address <= RegisterAddress.CELL_TEMP_END)
            {
                int tempIndex = (address - RegisterAddress.CELL_TEMP_START) * 2;
                if (tempIndex + 1 < systemData.ValueData.CellTemperature.Length)
                {
                    systemData.ValueData.CellTemperature[tempIndex] = (short)(((value >> 8) & 0xFF) * 10);
                    systemData.ValueData.CellTemperature[tempIndex + 1] = (short)((value & 0xFF) * 10);
                }
            }
        }
        #endregion

        #region Data Conversion
        private static short ConvertWarningData(short rawData)
        {
            short result = 0;
            bool[] alarmBits = ConvertToBitArray(rawData);

            if (alarmBits[0]) result |= (1 << 2);   // Pack OV
            if (alarmBits[1]) result |= (1 << 4);   // Cell OV
            if (alarmBits[2]) result |= (1 << 3);   // Pack UV
            if (alarmBits[3]) result |= (1 << 5);   // Cell UV
            if (alarmBits[4]) result |= (1 << 6);   // Charging OC
            if (alarmBits[5]) result |= (1 << 7);   // Discharging OC
            if (alarmBits[8]) result |= (1 << 0);   // Charging Over Temperature
            if (alarmBits[9]) result |= (1 << 0);   // Discharging Over Temperature
            if (alarmBits[10]) result |= (1 << 1);  // Charging Under Temperature
            if (alarmBits[11]) result |= (1 << 1);  // Discharging Under Temperature
            if (alarmBits[12]) result |= (1 << 11); // SOC Low

            return result;
        }

        private static short ConvertProtectionData(short rawData)
        {
            short result = 0;
            bool[] alarmBits = ConvertToBitArray(rawData);

            if (alarmBits[0]) result |= (1 << 2);   // Pack OV
            if (alarmBits[1]) result |= (1 << 4);   // Cell OV
            if (alarmBits[2]) result |= (1 << 3);   // Pack UV
            if (alarmBits[3]) result |= (1 << 5);   // Cell UV
            if (alarmBits[4]) result |= (1 << 6);   // Charging OC
            if (alarmBits[5]) result |= (1 << 7);   // Discharging OC
            if (alarmBits[8]) result |= (1 << 0);   // Charging Over Temperature
            if (alarmBits[9]) result |= (1 << 0);   // Discharging Over Temperature
            if (alarmBits[10]) result |= (1 << 1);  // Charging Under Temperature
            if (alarmBits[11]) result |= (1 << 1);  // Discharging Under Temperature
            if (alarmBits[13]) result |= (1 << 9);  // Short Circuit Protection

            return result;
        }

        private static bool[] ConvertToBitArray(short value)
        {
            var bits = new bool[16];
            for (int i = 0; i < 16; i++)
            {
                bits[i] = ((value >> i) & 1) == 1;
            }
            return bits;
        }
        #endregion

        #region Calculated Values
        private static void UpdateCalculatedValues(ref DeviceSystemData systemData)
        {
            CalculateCellVoltageStatistics(ref systemData);
            CalculateTemperatureStatistics(ref systemData);
            csMakeDataFunction.MakeAlarm(ref systemData);
        }

        private static void CalculateCellVoltageStatistics(ref DeviceSystemData systemData)
        {
            if (systemData.cellQty <= 0) return;

            int max = 0, min = int.MaxValue, sum = 0;
            int maxIndex = 0, minIndex = 0;

            for (int i = 0; i < systemData.cellQty; i++)
            {
                if (i >= systemData.ValueData.CellVoltage.Length) break;

                int cellVoltage = systemData.ValueData.CellVoltage[i];
                sum += cellVoltage;

                if (cellVoltage > max)
                {
                    max = cellVoltage;
                    maxIndex = i;
                }

                if (cellVoltage < min)
                {
                    min = cellVoltage;
                    minIndex = i;
                }
            }

            systemData.AvgData.avgCellVoltage = (short)(sum / systemData.cellQty);
            systemData.AvgData.maxCellVoltage = (short)max;
            systemData.AvgData.maxCellNum = (short)(maxIndex + 1);
            systemData.AvgData.minCellVoltage = (short)min;
            systemData.AvgData.minCellNum = (short)(minIndex + 1);
            systemData.AvgData.diffCellVoltage = (short)(max - min);
        }

        private static void CalculateTemperatureStatistics(ref DeviceSystemData systemData)
        {
            if (systemData.tempQty <= 0) return;

            int max = int.MinValue, min = int.MaxValue, sum = 0;
            int maxIndex = 0, minIndex = 0;

            for (int i = 0; i < systemData.tempQty; i++)
            {
                if (i >= systemData.ValueData.CellTemperature.Length) break;

                int temperature = systemData.ValueData.CellTemperature[i];
                sum += temperature;

                if (temperature > max)
                {
                    max = temperature;
                    maxIndex = i;
                }

                if (temperature < min)
                {
                    min = temperature;
                    minIndex = i;
                }
            }

            systemData.AvgData.avgTemp = (short)(sum / systemData.tempQty);
            systemData.AvgData.maxTemp = (short)max;
            systemData.AvgData.maxTempNum = (short)(maxIndex + 1);
            systemData.AvgData.minTemp = (short)min;
            systemData.AvgData.minTempNum = (short)(minIndex + 1);
            systemData.AvgData.diffTemp = (short)(max - min);
        }
        #endregion

        #region Result Classes
        public class ValidationResult
        {
            public bool IsValid { get; }
            public string Message { get; }

            public ValidationResult(bool isValid, string message)
            {
                IsValid = isValid;
                Message = message;
            }
        }

        public class ProcessingResult
        {
            public bool Success { get; }
            public string Message { get; }

            public ProcessingResult(bool success, string message)
            {
                Success = success;
                Message = message;
            }
        }
        #endregion
    }
}