using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.CodeDom;

namespace arDev
{
    public class BMS : arRS232
    {
        public BMS()
        {

            MinRecvLength = 34;
        }

        /// <summary>
        /// 시리얼포트의 핀 상태값이 변경될 때 발생합니다.
        /// </summary>
        public event EventHandler<BMSInformationEventArgs> BMSDataReceive;
        public event EventHandler<BMSCelvoltageEventArgs> BMSCellDataReceive;

        protected override bool CustomParser(byte[] buf, out byte[] remainBuffer)
        {
            List<byte> remain = new List<byte>();

            //데이터는 총 34byt 이며 , DD~77로 구성됨
            Boolean bComplete = false;
            for (int i = 0; i < buf.Length; i++)
            {
                var incomByte = buf[i];
                if (bComplete == true)
                {
                    remain.Add(incomByte);
                }
                else
                {
                    if (findSTX == false)
                    {
                        if (incomByte != 0xDD)
                        {
                            //버리는데이터
                        }
                        else
                        {
                            findSTX = true;
                            tempBuffer.Clear();
                            tempBuffer.Add(incomByte);
                        }
                    }
                    else
                    {
                        tempBuffer.Add(incomByte);

                        var queylen = QueryIndex == 0 ? 34 : 23;
                        if (tempBuffer.Count == queylen)
                        {
                            if (incomByte != 0x77)
                            {
                                //종단기호가 맞지 않다. 이자료는 폐기한다.
                                var hexstr = string.Join(" ", tempBuffer.Select(t => t.ToString("X2")));
                                RaiseMessage(MessageType.Error, $"discard : {hexstr}");
                                tempBuffer.Clear();
                            }
                            else
                            {
                                //데이터가 맞게 수신됨
                                LastReceiveBuffer = tempBuffer.ToArray();
                                bComplete = true;
                            }
                            findSTX = false;
                        }
                        else
                        {
                            //아직 모자르므로 대기한다
                        }
                    }
                }
            }

            remainBuffer = remain.ToArray();
            return bComplete;
        }

        bool Recv0 = false;
        bool Recv1 = false;
        public event EventHandler<ChargetDetectArgs> ChargeDetect;
        public override bool ProcessRecvData(byte[] data)
        {
            //LastReceiveBuffer
            if (data == null)
            {
                RaiseMessage(MessageType.Error, "수신 데이터가 없습니다");
                return false;
            }

            var datalne = QueryIndex == 0 ? 34 : 23;
            if (data.Length != datalne)
            {
                RaiseMessage(MessageType.Error, $"데이터의 길이가 {MinRecvLength}가 아닙니다 길이={data.Length}");
                return false;
            }
            else
            {
                var rxstr = string.Join(" ", data.Select(t => t.ToString("X2")));
                RaiseMessage(MessageType.Recv, $"Querh:{QueryIndex},Data:{rxstr}" );
            }

            if (QueryIndex == 0)
            {
                return ParseBMSInfo();
            }
            else
            {
                return ParseBMSCellVoltage();
            }


        }
        bool ParseBMSCellVoltage()
        {
            //var i = 0;
            var BatteryCell_Checksum = 0xffff;// - (LastReceiveBuffer[i + 3] + LastReceiveBuffer[i + 4] + LastReceiveBuffer[i + 5] + LastReceiveBuffer[i + 6] + LastReceiveBuffer[i + 7] + LastReceiveBuffer[i + 8] + LastReceiveBuffer[i + 9] + LastReceiveBuffer[i + 10] + LastReceiveBuffer[i + 11] + LastReceiveBuffer[i + 12] + LastReceiveBuffer[i + 13] + LastReceiveBuffer[i + 14] + LastReceiveBuffer[i + 15] + LastReceiveBuffer[i + 16] + LastReceiveBuffer[i + 17] + LastReceiveBuffer[i + 18] + LastReceiveBuffer[i + 19])) + 1;
            for (int i = 3; i < 20; i++)
            {
                BatteryCell_Checksum -= LastReceiveBuffer[i];// + LastReceiveBuffer[i + 4] + LastReceiveBuffer[i + 5] + LastReceiveBuffer[i + 6] + LastReceiveBuffer[i + 7] + LastReceiveBuffer[i + 8] + LastReceiveBuffer[i + 9] + LastReceiveBuffer[i + 10] + LastReceiveBuffer[i + 11] + LastReceiveBuffer[i + 12] + LastReceiveBuffer[i + 13] + LastReceiveBuffer[i + 14] + LastReceiveBuffer[i + 15] + LastReceiveBuffer[i + 16] + LastReceiveBuffer[i + 17] + LastReceiveBuffer[i + 18] + LastReceiveBuffer[i + 19])) + 1;
            }

            BatteryCell_Checksum += 1;
            var recvchecksum = BitConverter.ToUInt16(LastReceiveBuffer.Skip(20).Take(2).Reverse().ToArray(), 0);
            if (recvchecksum == BatteryCell_Checksum)
            {
                var v1 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(4).Take(2).Reverse().ToArray(), 0) / 1000.0;
                var v2 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(6).Take(2).Reverse().ToArray(), 0) / 1000.0;
                var v3 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(8).Take(2).Reverse().ToArray(), 0) / 1000.0;
                var v4 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(10).Take(2).Reverse().ToArray(), 0) / 1000.0;
                var v5 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(12).Take(2).Reverse().ToArray(), 0) / 1000.0;
                var v6 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(14).Take(2).Reverse().ToArray(), 0) / 1000.0;
                var v7 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(16).Take(2).Reverse().ToArray(), 0) / 1000.0;
                var v8 = BitConverter.ToUInt16(LastReceiveBuffer.Skip(18).Take(2).Reverse().ToArray(), 0) / 1000.0;

                var idx = 0;
                CellVoltage[idx++] = v1;
                CellVoltage[idx++] = v2;
                CellVoltage[idx++] = v3;
                CellVoltage[idx++] = v4;
                CellVoltage[idx++] = v5;
                CellVoltage[idx++] = v6;
                CellVoltage[idx++] = v7;
                CellVoltage[idx++] = v8;

                Recv1 = true;

                try
                {
                    BMSCellDataReceive?.Invoke(this, new BMSCelvoltageEventArgs(v1, v2, v3, v4, v5, v6, v7, v8));
                    Current_CellTime = DateTime.Now;
                    return true;
                }
                catch (Exception ex)
                {
                    RaiseMessage(MessageType.Error, ex.Message);
                    return false;
                }
            }
            else return false;


        }
        bool ParseBMSInfo()
        {
            //전압확인
            UInt16 batH = (UInt16)LastReceiveBuffer[4];
            UInt16 batL = (UInt16)LastReceiveBuffer[5];
            batH = (UInt16)(batH << 8);
            batH = (UInt16)(batH | batL);
            Current_Volt = (float)(batH / 100.0);

            //잔량확인
            batH = (UInt16)LastReceiveBuffer[8];
            batL = (UInt16)LastReceiveBuffer[9];
            batH = (UInt16)(batH << 8);
            batH = (UInt16)(batH | batL);
            var newamp = (int)batH;
            var Changed = newamp != Current_Amp;
            Current_Amp = newamp;

            //총량확인
            batH = (UInt16)LastReceiveBuffer[10];
            batL = (UInt16)LastReceiveBuffer[11];
            batH = (UInt16)(batH << 8);
            batH = (UInt16)(batH | batL);
            Current_MaxAmp = (int)batH;

            //남은 % 계산
            float levraw = (float)(Current_Amp / (Current_MaxAmp * 1.0));
            Current_Level = (float)(levraw * 100.0);//  (float)(map(remain, 0, total, 0, 100));            
            Current_LevelA = LastReceiveBuffer[23]; //<- 23번자료는 byte이므로 소수점이잇는 데이터로 직접 계산한다


            //250620 jwlee 추가
            int temp1 = (LastReceiveBuffer[27] << 8) | LastReceiveBuffer[28];
            int temp2 = (LastReceiveBuffer[29] << 8) | LastReceiveBuffer[30];

            Current_temp1 = (temp1 - 2731) / 10f;
            Current_temp2 = (temp2 - 2731) / 10f;

            CheckManualCharge();

            Recv0 = true;

            try
            {
                BMSDataReceive?.Invoke(this, new BMSInformationEventArgs(Current_Volt, Current_Amp, Current_MaxAmp, Current_Level, Changed));
                Current_DataTime = DateTime.Now;
                return true;
            }
            catch (Exception ex)
            {
                RaiseMessage(MessageType.Error, ex.Message);
                return false;
            }
        }

        private bool _autocharge = false;
        public Boolean AutoCharge
        {
            get { return _autocharge; }
            set { _autocharge = false; }
        }

        //public void ClearManualChargeCheckValue()
        //{
        //    chk_timee = new DateTime(1982, 11, 23);
        //    chk_times = new DateTime(1982, 11, 23);
        //    chk_valuee = 0f;
        //    chk_values = 0f;
        //}

        void CheckManualCharge()
        {
            if (AutoCharge)
            {
                if (chk_timee.Year != 1982)
                {
                    chk_timee = new DateTime(1982, 11, 23);
                    chk_valuee = 999f;
                }
                if (chk_times.Year != 1982)
                {
                    chk_times = new DateTime(1982, 11, 23);
                    chk_values = 999f;
                }
            }
            if (chk_times.Year == 1982)
            {
                chk_times = DateTime.Now;
                chk_values = Current_Level;
            }
            else
            {
                if (chk_timee.Year == 1982)
                {
                    if ((Current_Level - chk_values) >= 0.1)
                    {
                        //충전중이다
                        chk_timee = DateTime.Now;
                        chk_valuee = Current_Level;
                        try
                        {
                            ChargeDetect?.Invoke(this, new ChargetDetectArgs(chk_times, chk_values, chk_timee, chk_valuee));
                        }
                        catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }

                    }
                    else if ((Current_Level - chk_values) <= -0.1)
                    {
                        //방전중이다
                        if (chk_times.Year != 1982) chk_times = new DateTime(1982, 11, 23);
                        if (chk_timee.Year != 1982) chk_timee = new DateTime(1982, 11, 23);
                    }
                    else
                    {
                        //아직 변화가 없으니 종료일을 기록하지 않는다
                    }
                }
                else
                {
                    //이미 종료일이 셋팅된 상태이다
                    if ((Current_Level - chk_valuee) >= 0.1)
                    {
                        //종료시간을 시작값에 넣는다
                        chk_times = chk_timee;
                        chk_values = chk_valuee;

                        chk_timee = DateTime.Now;
                        chk_valuee = Current_Level;
                        try
                        {
                            ChargeDetect?.Invoke(this, new ChargetDetectArgs(chk_times, chk_values, chk_timee, chk_valuee));
                        }
                        catch (Exception ex) { RaiseMessage(MessageType.Error, ex.Message); }
                    }
                    else if ((Current_Level - chk_valuee) <= -0.1)
                    {
                        //방전중이다
                        if (chk_times.Year != 1982) chk_times = new DateTime(1982, 11, 23);
                        if (chk_timee.Year != 1982) chk_timee = new DateTime(1982, 11, 23);
                    }
                    else
                    {
                        //아직 변화가 없으니 종료일을 기록하지 않는다
                    }
                }

            }
        }


        public DateTime chk_times { get; set; } = new DateTime(1982, 11, 23);
        public DateTime chk_timee { get; set; } = new DateTime(1982, 11, 23);
        public float chk_values { get; set; } = 0f;
        public float chk_valuee { get; set; } = 0f;

        /// <summary>
        /// 전압
        /// </summary>
        public float Current_Volt { get; set; }
        /// <summary>
        /// 남은 잔량(%)
        /// </summary>
        public float Current_Level { get; set; }
        public double[] CellVoltage = new double[] { 0, 0, 0, 0, 0, 0, 0, 0 };

        public float Current_LevelA { get; set; }

        /// <summary>
        /// 남은 전류량
        /// </summary>
        public int Current_Amp { get; set; }

        /// <summary>
        /// BMS 온도값1
        /// </summary>
        public double Current_temp1 { get; set; }

        /// <summary>
        /// BMS 온도값2
        /// </summary>
        public double Current_temp2 { get; set; }

        /// <summary>
        /// 총 전류량
        /// </summary>
        public int Current_MaxAmp { get; set; }

        public DateTime Current_DataTime = DateTime.Parse("1982-11-23");
        public DateTime Current_CellTime = DateTime.Parse("1982-11-23");

        public int QueryIndex { get; set; } = 0;

        /// <summary>
        ///  상태읽기와 전압읽기명령을 반복합니다
        /// </summary>
        /// <returns></returns>
        public Boolean SendQuery()
        {
            if (QueryIndex == 0)
            {
                if (Recv0 == true)
                {
                    QueryIndex = 1;
                    Recv1 = false;
                    return true;
                }
                else
                {
                    return SendQuery_ReadStatue();
                }
            }
            else
            {
                if (Recv1 == true)
                {
                    QueryIndex = 0;
                    Recv0 = false;
                    return true;
                }
                else
                {
                    return SendQuery_ReadCellvoltage();
                }
            }
        }

        public Boolean SendQuery_ReadStatue()
        {
            Recv0 = false;
            var cmd = new List<byte>();
            cmd.Add(0xDD);
            cmd.Add(0xA5);
            cmd.Add(0x03);
            cmd.Add(0x00);
            cmd.Add(0xFF);
            cmd.Add(0xFD);
            cmd.Add(0x77);
            cmd.Add(0x0D);
            return WriteData(cmd.ToArray());
        }


        public Boolean SendQuery_ReadCellvoltage()
        {
            Recv1 = false;
            var cmd = new List<byte>();
            cmd.Add(0xDD);
            cmd.Add(0xA5);
            cmd.Add(0x04);
            cmd.Add(0x00);
            cmd.Add(0xFF);
            cmd.Add(0xFC);
            cmd.Add(0x77);
            cmd.Add(0x0D);
            return WriteData(cmd.ToArray());
        }

    }
}
