using ProjectManagementSystem.Device.Command;
using ProjectManagementSystem.Device.CommandCallback;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ProjectManagementSystem.Device.Core
{
    public class CasunCommunication
    {
        public event Action<InfoRoller> OnInfoRoller;
        public event Action<InfoIO> OnInfoIO;
        public event Action<InfoBox> OnInfoBox;
        public event Action<InfoBoxRelease> OnInfoBoxRelease;
        public event Action<InfoAgv> OnInfoAgv;
        public event Action<InfoSkq> OnInfoSkq;

        /// <summary>
        /// 通讯对象集合
        /// </summary>
        public List<ICommunication> Communications { get; set; } = new List<ICommunication>();

        public ConcurrentDictionary<int, InfoRoller> InfoRollerDictionary { get; set; } = new ConcurrentDictionary<int, InfoRoller>();
        public ConcurrentDictionary<int, InfoIO> InfoIODictionary { get; set; } = new ConcurrentDictionary<int, InfoIO>();
        public ConcurrentDictionary<int, InfoAlarmLight> InfoAlarmLightDictionary { get; set; } = new ConcurrentDictionary<int, InfoAlarmLight>();
        public ConcurrentDictionary<int, InfoBox> InfoBoxDictionary { get; set; } = new ConcurrentDictionary<int, InfoBox>();
        public ConcurrentDictionary<int, InfoBoxRelease> InfoBoxReleaseDictionary { get; set; } = new ConcurrentDictionary<int, InfoBoxRelease>();

        public ConcurrentDictionary<int, InfoAgv> InfoAgvDictionary { get; set; } = new ConcurrentDictionary<int, InfoAgv>();
        public ConcurrentDictionary<int, InfoVcu> InfoVcuDictionary { get; set; } = new ConcurrentDictionary<int, InfoVcu>();

        public void SendCommand(byte[] data, string address)
        {
            for (int i = 0; i < Communications.Count; i++)
            {
                Communications[i]?.SendCommand(data, address);
            }
        }

        public void Handling(string address, byte[] data)
        {
            if (HandlingBytes(address, data, 0x50, 0x00, 0x53, 40, HandlingRoller)) return;
            if (HandlingBytes(address, data, 0x20, 0x00, 0x00, 16, HandlingAlarmLight)) return;
            if (HandlingBytes(address, data, 0x10, 0x00, 0x03, 14, HandlingCallBox, HandlingReleaseBox, HandlingIOBox)) return;
            if (HandlingBytes(address, data, 0x10, 0x64, 0x03, 12, HandlingRailAgv1064)) return;
            if (HandlingBytes(address, data, 0x10, 0x62, 0x03, 10, HandlingRailAgv1062)) return;

            if (HandlingPackLine(address, data)) return;

            HandlingVcu(address, data);
        }

        private bool HandlingBytes(string address, byte[] data, byte first, byte second, byte end, int len, params Action<string, byte[]>[] actions)
        {
            if (data[0] == first
                && (second > 0 ? data[1] == second : true)
                && (end > 0 ? data[data.Length - 1] == end : true)
                && data.Length % len == 0)
            {
                int count = data.Length / len;
                if (count == 1)
                {
                    for (int index = 0; index < actions.Length; index++)
                    {
                        actions[index]?.Invoke(address, data);
                    }

                }
                else
                {
                    byte[] tempData = new byte[len];
                    for (int i = 0; i < count; i++)
                    {
                        Buffer.BlockCopy(data, i * len, tempData, 0, tempData.Length);

                        for (int index = 0; index < actions.Length; index++)
                        {
                            actions[index]?.Invoke(address, tempData);
                        }
                    }
                }

                return true;
            }
            return false;
        }

        private void HandlingRailAgv1064(string address, byte[] data)
        {
            if (!(data[0] == 0x10 && data[1] == 0x64 && data[data.Length - 1] == 0x03)) return;

            int deviceId = data[2];

            bool isContainsKey = InfoAgvDictionary.ContainsKey(deviceId);
            var info = isContainsKey ? InfoAgvDictionary[deviceId] : new InfoAgv1064();

            if (isContainsKey && !(info is InfoAgv1064))
            {
                info = new InfoAgv1064();
            }

            info.Deserialize(data);
            info.RemoteEndPoint = address;

            InfoAgvDictionary.AddOrUpdate(deviceId, info, (key, value) => info);

            if(info.IsChanged)
            {
                OnInfoAgv?.Invoke(info);
            }
        }
        private void HandlingRailAgv1062(string remoteEndPoint, byte[] data)
        {
            if (!(data[0] == 0x10 && data[1] == 0x62 && data[data.Length - 1] == 0x03)) return;

            int deviceId = data[2];

            bool isContainsKey = InfoAgvDictionary.ContainsKey(deviceId);
            var info = isContainsKey ? InfoAgvDictionary[deviceId] : new InfoAgv1062();

            if (isContainsKey && !(info is InfoAgv1062))
            {
                info = new InfoAgv1062();
            }

            info.Deserialize(data);
            info.RemoteEndPoint = remoteEndPoint;

            InfoAgvDictionary.AddOrUpdate(deviceId, info, (key, value) => info);

            if (info.IsChanged)
            {
                OnInfoAgv?.Invoke(info);
            }
        }
        private void HandlingRoller(string address, byte[] data)
        {
            int deviceId = data[2];

            bool isContainsKey = InfoRollerDictionary.ContainsKey(deviceId);
            var info = isContainsKey ? InfoRollerDictionary[deviceId] : new InfoRoller();

            bool lastFullTray = info.FullTray;
            bool lastEmpty = info.FullEmpty;
            info.Deserialize(data);
            info.RemoteEndPoint = address;

            InfoRollerDictionary.AddOrUpdate(deviceId, info, (key, value) => info);


            if (info.FullTray != lastFullTray || info.FullEmpty != lastEmpty || !isContainsKey || data[4] == 0x22)
            {
                OnInfoRoller?.Invoke(info);
            }
        }
        private void HandlingAlarmLight(string remoteEndPoint, byte[] data)
        {
            int deviceId = data[14];

            bool isContainsKey = InfoAlarmLightDictionary.ContainsKey(deviceId);
            var info = isContainsKey ? InfoAlarmLightDictionary[deviceId] : new InfoAlarmLight();

            info.Deserialize(data);
            info.RemoteEndPoint = remoteEndPoint;

            InfoAlarmLightDictionary.AddOrUpdate(deviceId, info, (key, value) => info);
        }
        private void HandlingCallBox(string address, byte[] data)
        {
            if (!(data[1] == 0x41 || data[1] == 0x42 || data[1] == 0x46)) return;

            int route = (data[6] << 8) + data[7] - 0x3030;

            bool isContainsKey = InfoBoxDictionary.ContainsKey(route);
            var info = isContainsKey ? InfoBoxDictionary[route] : new InfoBox();

            info.Deserialize(data);
            info.RemoteEndPoint = address;

            InfoBoxDictionary.AddOrUpdate(route, info, (key, value) => info);

            if (info.Function == 0x41 || info.Function == 0x42)
            {
                //直接回复
                CommandBoxReply boxReply = new CommandBoxReply();
                boxReply.CurrentData = data;
                boxReply.Function = (byte)(data[1] + 2);

                SendCommand(boxReply.Serialization(), info.RemoteEndPoint);
            }

            OnInfoBox?.Invoke(info);
        }
        private void HandlingReleaseBox(string remoteEndPoint, byte[] data)
        {
            if (!(data[1] == 0x36 || data[1] == 0x37)) return;

            int mark = ((int)data[8] << 8 | data[9]);

            bool isContainsKey = InfoBoxReleaseDictionary.ContainsKey(mark);
            var info = isContainsKey ? InfoBoxReleaseDictionary[mark] : new InfoBoxRelease();

            info.Deserialize(data);
            info.RemoteEndPoint = remoteEndPoint;

            InfoBoxReleaseDictionary.AddOrUpdate(mark, info, (key, value) => info);

            OnInfoBoxRelease?.Invoke(info);
        }
        private void HandlingIOBox(string address, byte[] data)
        {
            if (!(data[1] == 0x81)) return;

            int deviceId = data[2];

            bool isContainsKey = InfoIODictionary.ContainsKey(deviceId);
            var info = isContainsKey ? InfoIODictionary[deviceId] : new InfoIO();
            byte lastOutput = info.Output;
            byte lastInput = info.Input;

            info.Deserialize(data);
            info.RemoteEndPoint = address;

            InfoIODictionary.AddOrUpdate(deviceId, info, (key, value) => info);

            //if (info.Output != lastOutput || info.Input != lastInput || !isContainsKey)
            {
                OnInfoIO?.Invoke(info);
            }
        }

        private void HandlingVcu(string address, byte[] data)
        {
            if (address != "VCU") return;
            if (!(data[0] == 0x25)) return;

            int deviceId = BitConverter.ToUInt16(data, 1);

            bool isContainsKey = InfoVcuDictionary.ContainsKey(deviceId);
            var info = isContainsKey ? InfoVcuDictionary[deviceId] : new InfoVcu();

            info.Deserialize(data);
            info.RemoteEndPoint = address;

            InfoVcuDictionary.AddOrUpdate(deviceId, info, (key, value) => info);
        }

        private bool HandlingPackLine(string address, byte[] data)
        {
            if (data.Length >= 8 && data[0] == 0x4A && data[1] == 0x54 && data[2] == 0x58)//帧开头
            {
                var info = new InfoSkq();
                info.Deserialize(data);
                info.RemoteEndPoint = address;

                OnInfoSkq?.Invoke(info);
                return true;
            }

            return false;
        }
    }
}
