using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ENIG
{
    // 장비 타입 정의
    public enum DeviceType
    {
        ACS = 0,
        AGV1 = 10,
        AGV2 = 11,
        BUFFER1 = 20,
        BUFFER2 = 21,
        BUFFER3 = 22,
        BUFFER4 = 23,
        BUFFER5 = 24,
        DOOR = 30,
    }

    public partial class EEProtocol
    {
        // 패킷 수신 이벤트 정의
        // 데이터 수신 이벤트 정의
        public event EventHandler<DataEventArgs> OnDataReceived;
        public event EventHandler<MessageEventArgs> OnMessage;

        // CRC16 계산을 위한 테이블
        private static readonly ushort[] CRC16_TABLE = new ushort[256];

        // CRC16 테이블 초기화
        public EEProtocol()
        {
            const ushort polynomial = 0x7979;
            for (ushort i = 0; i < CRC16_TABLE.Length; i++)
            {
                ushort value = 0;
                ushort temp = i;
                for (byte j = 0; j < 8; j++)
                {
                    if (((value ^ temp) & 0x0001) != 0)
                    {
                        value = (ushort)((value >> 1) ^ polynomial);
                    }
                    else
                    {
                        value >>= 1;
                    }
                    temp >>= 1;
                }
                CRC16_TABLE[i] = value;
            }

            //// CRC 테이블 출력
            //Console.WriteLine("CRC16 테이블 값:");
            //for (int i = 0; i < CRC16_TABLE.Length; i++)
            //{
            //    if (i % 8 == 0)
            //    {
            //        Console.WriteLine();
            //    }
            //    Console.Write($"0x{CRC16_TABLE[i]:X4}, ");
            //}
            //Console.WriteLine();


        }

        // CRC16 계산 메서드
        public ushort CalculateCRC16(byte[] data)
        {
            ushort crc = 0xFFFF;
            for (int i = 0; i < data.Length; i++)
            {
                byte index = (byte)(crc ^ data[i]);
                crc = (ushort)((crc >> 8) ^ CRC16_TABLE[index]);
            }
            return crc;
        }

        // 패킷 생성 메서드
        public byte[] CreatePacket(byte id, byte command, byte[] data)
        {
            var packet = new Packet
            {
                ID = id,
                Command = command,
                Data = data ?? new byte[0],
                Length = (byte)(1 + 1 + (data?.Length ?? 0)) // ID + Command + Data 길이
            };

            // 패킷 조립
            List<byte> packetData = new List<byte>();
            packetData.Add(Packet.STX);
            packetData.Add(packet.Length);
            packetData.Add(packet.ID);
            packetData.Add(packet.Command);
            if (packet.Data != null)
                packetData.AddRange(packet.Data);

            // CRC16 계산
            packet.CRC16 = CalculateCRC16(packetData.Skip(1).ToArray()); // STX 제외하고 계산
            packetData.AddRange(BitConverter.GetBytes(packet.CRC16));
            packetData.Add(Packet.ETX);

            return packetData.ToArray();
        }

        //패킷테스트
        public void PacketTest(byte[] rawData)
        {
            var hexstr = string.Join(" ", rawData.Select(t => t.ToString("X2")));
            RaiseMessage( $"TestPacket : {hexstr}");
            ParsePacket(rawData);
        }

        //메세지 발생
        public void RaiseMessage(string message, bool isError = false)
        {
            OnMessage?.Invoke(this, new MessageEventArgs { IsError = isError, Message = message });
        }

        // 패킷 파싱 메서드
        public bool ParsePacket(byte[] rawData)
        {
            try
            {
                if (rawData.Length < 7) // 최소 패킷 크기
                {
                    var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2")));
                    RaiseMessage($"Too Short Data:{hexstring}");
                    return false;
                }
                    

                if (rawData[0] != Packet.STX || rawData[rawData.Length - 1] != Packet.ETX)
                {
                    var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2")));
                    RaiseMessage($"STX/ETX Error Data:{hexstring}");
                    return false;
                }
                    

                byte length = rawData[1];
                if (length + 5 != rawData.Length) // STX + Length + CRC16(2) + ETX = 5
                {
                    var hexstring = string.Join(" ", rawData.Select(t => t.ToString("X2")));
                    RaiseMessage($"Length Error ({length+5} != {rawData.Length}) Data:{hexstring}");
                    return false;
                }
                    

                // CRC16 검증
                byte[] dataForCrc = rawData.Skip(1).Take(length + 1).ToArray();
                ushort calculatedCrc = CalculateCRC16(dataForCrc);
                ushort receivedCrc = BitConverter.ToUInt16(rawData, rawData.Length - 3);

                if (receivedCrc != 0xFFFF && calculatedCrc != receivedCrc)    //FF 무시
                {
                    RaiseMessage($"CRC Error ID:{rawData[2]:X2},CMD:{rawData[3]:X2}", true);
                    return false;
                }
                    

                // 패킷 생성
                var packet = new Packet
                {
                    Length = length,
                    ID = rawData[2],
                    Command = rawData[3],
                    Data = rawData.Skip(4).Take(length - 2).ToArray(), // ID와 Command 길이(2) 제외
                    CRC16 = receivedCrc,
                    RawData = rawData,
                };

                // 이벤트 발생
                OnDataReceived?.Invoke(this, new DataEventArgs { ReceivedPacket = packet });
                return true;
            }
            catch(Exception ex)
            {
                RaiseMessage(ex.Message, true);
                return false;
            }
        }

        // 데이터 수신 처리 메서드 (시리얼 포트에서 데이터를 받았을 때 호출)
        private List<byte> buffer = new List<byte>();
        private int ProtocolParseError = 0;
        public void ProcessReceivedData(byte[] data)
        {
            buffer.AddRange(data);

            while (buffer.Count > 0)
            {
                // STX 찾기
                int stxIndex = buffer.FindIndex(b => b == Packet.STX);
                if (stxIndex == -1)
                {
                    buffer.Clear();
                    break;
                }

                // 불필요한 데이터 제거
                if (stxIndex > 0)
                    buffer.RemoveRange(0, stxIndex);

                // 패킷 길이 확인을 위한 최소 데이터 확인
                if (buffer.Count < 2)
                    break;

                int expectedLength = buffer[1] + 5; // 전체 패킷 길이
                if (buffer.Count < expectedLength)
                    break;

                // 패킷 추출 및 처리
                byte[] packetData = buffer.Take(expectedLength).ToArray();
                buffer.RemoveRange(0, expectedLength);

                var parseOK = ParsePacket(packetData);
                if(parseOK==false)  //분석이 실패되었다면 해당 데이터는 삭제한다.
                {
                    ProtocolParseError += 1;
                    if (ProtocolParseError > 3) buffer.Clear();
                } else ProtocolParseError = 0;

                if(buffer.Any())
                {
                    System.Threading.Thread.Sleep(1);
                }
            }
        }
    }
}