using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WjwLib.API.Communication;
using GatewayTool.Protocols;
using GatewayTool.Models;

namespace GatewayTool.Businesses
{
    /// <summary>
    /// 服务操作类
    /// </summary>
    public class ServiceOptions
    {
        private static readonly object _locker = new object();
        private List<ICommunication> _comList = new List<ICommunication>();

        private DTUProtocol _protocol = new DTUProtocol();
        private XModem _xModem = new XModem();

        private const int SINGLE_PACK_LENGTH = 128;

        public event EventHandler ExecDeviceListUpdated;
        public event EventHandler<GatewayBase> ExecReadSystemState;
        public event EventHandler<GatewayBase> ExecReadDeviceConfig;
        public event EventHandler<bool> ExecWriteDeviceConfig;
        public event EventHandler<GatewayBase> ExecUpgradeProgressUpdated;
        public event EventHandler<GatewayBase> ExecUpgradeProgressFinished;
        public event EventHandler<ServiceInteractiveData> ExecServiceDataInteractive;

        private CommunicationTcpServer _TcpServer;
        /// <summary>
        /// Tcp Server实例
        /// </summary>     
        public CommunicationTcpServer TcpServer
        {
            get
            {
                if (_TcpServer == null)
                {
                    _TcpServer = new CommunicationTcpServer();
                }
                return _TcpServer;
            }
        }

        private ICommunication _Udp;
        /// <summary>
        /// UDP实例
        /// </summary>
        public ICommunication Udp
        {
            get
            {
                if (_Udp == null)
                {
                    _Udp = new CommunicationServer(CommunicationType.CT_NETPORT_UDP);
                    _Udp.ExecDataArrived += ReplyDataArrived;
                }
                return _Udp;
            }
        }

        private List<GatewayBase> _FoundDeviceList = new List<GatewayBase>();
        /// <summary>
        /// 已发现网关设备列表
        /// </summary>
        public List<GatewayBase> FoundDeviceList
        {
            get => _FoundDeviceList;
            set => _FoundDeviceList = value;
        }

        /// <summary>
        /// 读取DTU系统状态命令数据包
        /// </summary>
        public byte[] ReadDTUSystemStateCommand
        {
            get
            {
                return _protocol.PackReadDTUSystemState();
            }
        }

        /// <summary>
        /// 读取DTU设备配置命令数据包
        /// </summary>
        public byte[] ReadDTUDeviceConfigCommand
        {
            get
            {
                return _protocol.PackReadDTUDeviceConfig();
            }
        }

        /// <summary>
        /// 写DTU设备配置命令数据包
        /// </summary>
        /// <param name="json">json配置</param>
        public byte[] WriteDTUDeviceConfigCommand(string json)
        {
            return _protocol.PackWriteDTUDeviceConfig(json);
        }

        /// <summary>
        /// 准备开始DTU设备固件升级数据包
        /// </summary>
        public byte[] PrepareToUpgradeCommand
        {
            get
            {
                return _protocol.PackPrepareToUpgrade();
            }
        }

        /// <summary>
        /// 启动串口嗅探
        /// </summary>
        /// <param name="baudrate">波特率</param>
        /// <param name="databits">数据位</param>
        /// <param name="stopbits">停止位</param>
        /// <param name="parity">校验位</param>
        public void StartComSniff(EnumBaudrate baudrate, EnumDataBits databits, EnumStopBits stopbits, EnumCheckBit parity)
        {
            ICommunication com = new CommunicationServer(CommunicationType.CT_SERIALPORT);
            string[] portNames = com.GetAvailableCommPorts();
            if (portNames != null)
            {
                foreach (string name in portNames)
                {
                    ICommunication existCom = GetComInstance(name);
                    if (existCom == null)
                    {
                        com = new CommunicationServer(CommunicationType.CT_SERIALPORT);
                        com.ExecDataArrived += ReplyDataArrived;
                        com.OpenCommPort(new CommunicationSerialportArgs() { PortNum = name, BaudRate = (int)baudrate, DataBits = (int)databits, StopBits = ConvertStopBits(stopbits), Parity = ConvertParity(parity) });
                        _comList.Add(com);
                    }
                    else
                    {
                        com = existCom;
                    }
                    if (com.IsOpen) 
                    {
                        byte[] bytes = _protocol.PackSniffDevice();
                        com.SendData(bytes);
                        ExecServiceDataInteractive?.Invoke(this, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = "To All", Data = bytes });
                    }
                }
            }
        }

        /// <summary>
        /// 根据串口名从本地已有的串口列表中获取对应实例
        /// </summary>
        /// <param name="name">串口名</param>
        /// <returns>串口实例</returns>
        private ICommunication GetComInstance(string name)
        {
            if (_comList == null || _comList.Count == 0)
            {
                return null;
            }
            for (int i = _comList.Count - 1; i >= 0; i--)
            {
                CommunicationServer sp = _comList[i] as CommunicationServer;
                if (sp != null && sp.GetCommPortCfg().Contains(name))
                {
                    return sp;
                }
            }
            return null;
        }

        /// <summary>
        /// 转换自定义停止位为标准停止位
        /// </summary>
        /// <param name="enumStopBits">自定义停止位</param>
        /// <returns>标准停止位</returns>
        private StopBits ConvertStopBits(EnumStopBits enumStopBits)
        {
            switch (enumStopBits)
            {
                case EnumStopBits.Bit_1:
                    return StopBits.One;
                case EnumStopBits.Bit_2:
                    return StopBits.Two;
                default:
                    return StopBits.None;
            }
        }

        /// <summary>
        /// 转换自定义校验位为标准校验位
        /// </summary>
        /// <param name="enumStopBits">自定义校验位</param>
        /// <returns>标准校验位</returns>
        private Parity ConvertParity(EnumCheckBit enumCheckBit)
        {
            switch (enumCheckBit)
            {
                case EnumCheckBit.none:
                    return Parity.None;
                case EnumCheckBit.odd:
                    return Parity.Odd;
                case EnumCheckBit.even:
                    return Parity.Even;
                default:
                    return Parity.None;
            }
        }

        /// <summary>
        /// 启动网络嗅探
        /// </summary>
        /// <param name="port">远程端口号</param>
        /// <returns>操作结果：true成功，false失败</returns>
        public bool StartNetSniff(int port)
        {
            if (!this.Udp.IsOpen)
            {
                bool res = this.Udp.OpenCommPort(new CommunicationNetportArgs()
                {
                    ServerArgs = new EndPointArgs() { IP = "", PortNum = port },
                    ClientUdpArgs = new EndPointArgs() { IP = this.Udp.GetAvailableCommPorts()[0], PortNum = 100, PortNumGenType = PortNumGenType.Type_Specify }
                });
            }
            if (!this.Udp.IsOpen) return false;
            return this.Udp.SendData(_protocol.PackSniffDevice());
        }

        /// <summary>
        /// 回复数据到达处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ReplyDataArrived(object sender, ArriveDataEventArgs e)
        {
            lock (_locker)
            {
                if (e.Data.Length > 1)
                {
                    byte code = e.Data[1];
                    switch (code)
                    {
                        case 0x01:
                            ParseSniff(sender, e);
                            break;
                        case 0x02:
                            ParseReadSystemState(sender, e);
                            break;
                        case 0x03:
                            ParseReadDeviceConfig(sender, e);
                            break;
                        case 0x04:
                            ParseWriteDeviceConfig(sender, e);
                            break;
                        default:
                            break;
                    }
                    bool isConvertGB2312 = e.Data.Length > 3 && e.Data[0] == 0x4C && e.Data[1] == 0x4F && e.Data[2] == 0x47; //LOG标识
                    ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.RECV, GatewaySN = GetExistDevice(e.Source)?.SniffInfo.SN, Data = e.Data, IsConvertGB2312 = isConvertGB2312 }); //防止未及时嗅探出设备编号
                }
                else
                {
                    ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.RECV, GatewaySN = GetExistDevice(e.Source)?.SniffInfo.SN, Data = e.Data }); //防止收到在发送前面

                    if (e.Data[0] == 0x43) //收到字符'C'
                    {
                        ParseAllowUpgrade(sender, e);
                    }
                    else if (e.Data[0] == XModemConstants.ACK) // XModem ACK  继续发送
                    {
                        ParseGoOnNextPacket(sender, e);
                    }
                    else if (e.Data[0] == XModemConstants.NAK) // XModem NAK  重发分包
                    {
                        ParseResendLastPacket(sender, e);
                    }
                    else if (e.Data[0] == XModemConstants.CAN) // XModem CAN  强制停止
                    {
                        ParseTerminateUpgrade(sender, e);
                    }
                }                
            }
        }

        /// <summary>
        /// 解析XModem允许升级回复数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="data"></param>
        private void ParseAllowUpgrade(object sender, ArriveDataEventArgs e)
        {
            GatewayBase gateway = GetExistDevice(e.Source);
            if (gateway == null) return;
            if (string.IsNullOrEmpty(gateway.UpgradeInfo.FirmwareFilePath)) return;
            using (FileStream fs = new FileStream(gateway.UpgradeInfo.FirmwareFilePath, FileMode.Open))
            {
                using (BinaryReader br = new BinaryReader(fs))
                {
                    byte[] totalData = br.ReadBytes((int)fs.Length);
                    int packNum;
                    byte[] packet = _xModem.GetPacket(totalData, 1, SINGLE_PACK_LENGTH, out packNum);
                    gateway.Communication.SendData(packet);
                    ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = gateway.SniffInfo.SN, Data = packet });
                    gateway.UpgradeInfo.SeperatePackNum = 1;
                    gateway.UpgradeInfo.TotalPackNum = packNum;
                    gateway.UpgradeInfo.TotalData = totalData;
                }
            }
            ExecUpgradeProgressUpdated?.Invoke(sender, gateway);
        }

        /// <summary>
        /// 解析XModem继续发送下一包回复数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="data"></param>
        private void ParseGoOnNextPacket(object sender, ArriveDataEventArgs e)
        {
            GatewayBase gateway = GetExistDevice(e.Source);
            if (gateway == null) return;
            int packNum;
            gateway.UpgradeInfo.SeperatePackNum += 1;
            if (gateway.UpgradeInfo.SeperatePackNum == gateway.UpgradeInfo.TotalPackNum + 1) //收到最后一个分包应答
            {
                byte[] packet = new byte[] { XModemConstants.EOT };
                gateway.Communication.SendData(packet);
                ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = gateway.SniffInfo.SN, Data = packet });
            }
            else if (gateway.UpgradeInfo.SeperatePackNum == gateway.UpgradeInfo.TotalPackNum + 2) //收到EOT的回复，表明全部结束
            {
                ExecUpgradeProgressFinished?.Invoke(sender, gateway);
            }
            else //中间分包
            {
                byte[] packet = _xModem.GetPacket(gateway.UpgradeInfo.TotalData, gateway.UpgradeInfo.SeperatePackNum, SINGLE_PACK_LENGTH, out packNum);
                gateway.Communication.SendData(packet);
                ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = gateway.SniffInfo.SN, Data = packet });
                ExecUpgradeProgressUpdated?.Invoke(sender, gateway);
            }
        }

        /// <summary>
        /// 解析XModem重新发送上一包回复数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="data"></param>
        private void ParseResendLastPacket(object sender, ArriveDataEventArgs e)
        {
            GatewayBase gateway = GetExistDevice(e.Source);
            if (gateway == null) return;
            int packNum;
            byte[] packet = _xModem.GetPacket(gateway.UpgradeInfo.TotalData, gateway.UpgradeInfo.SeperatePackNum, SINGLE_PACK_LENGTH, out packNum);
            if (packet == null) //取不出数据时还要求发上一包，一定是EOT
            {
                byte[] bytes = new byte[] { XModemConstants.EOT };
                gateway.Communication.SendData(bytes);
                ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = gateway.SniffInfo.SN, Data = bytes});
            }
            else //中间分包
            {
                gateway.Communication.SendData(packet);
                ExecServiceDataInteractive?.Invoke(sender, new ServiceInteractiveData() { DataType = InteractiveType.SEND, GatewaySN = gateway.SniffInfo.SN, Data = packet });
                ExecUpgradeProgressUpdated?.Invoke(sender, gateway);
            }
        }

        /// <summary>
        /// 解析XModem终止升级回复数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="data"></param>
        private void ParseTerminateUpgrade(object sender, ArriveDataEventArgs e)
        {
            GatewayBase gateway = GetExistDevice(e.Source);
            if (gateway == null) return;
            gateway.UpgradeInfo.SeperatePackNum = 0;
            ExecUpgradeProgressUpdated?.Invoke(sender, gateway);
        }

        /// <summary>
        /// 解析设备嗅探回复数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="data"></param>
        private void ParseSniff(object sender, ArriveDataEventArgs e)
        {
            SniffReplyInfo info = _protocol.UnpackSniff(e.Data);
            if (info != null)
            {
                SniffDeviceInfo sniffInfo = new SniffDeviceInfo();
                sniffInfo.Source = e.Source.Split(':')[0];
                sniffInfo.SN = info.SN;
                sniffInfo.Version = info.Version;

                GatewayBase gateway = GetExistDevice(sniffInfo);
                bool isExisted = gateway != null;
                if (!isExisted)
                {
                    if (sniffInfo.SN.StartsWith("DT")) //DTU标识符
                    {
                        DTU dtu = new DTU();
                        dtu.SystemState = new DTUSystemState();
                        dtu.Config = new DTUConfig();
                        dtu.Config.DeviceId = info.SN; //初始化设备配置中的序列号
                        dtu.UpgradeInfo = new UpgradeInfo();
                        gateway = dtu; //先默认为DTU，后期根据识别符进行判定
                    }
                }
                if (gateway != null)
                {
                    gateway.SniffInfo = sniffInfo;
                    gateway.Communication = sender as ICommunication;
                    if (!isExisted) _FoundDeviceList.Add(gateway);
                    ExecDeviceListUpdated?.Invoke(sender, null);
                }
            }
        }

        /// <summary>
        /// 解析读系统状态回复数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="data"></param>
        private void ParseReadSystemState(object sender, ArriveDataEventArgs e)
        {
            DTUSystemState state = _protocol.UnpackReadDTUSystemState(e.Data);
            if (state != null)
            {
                DTU dtu = (DTU)GetExistDevice(e.Source);
                if (dtu != null)
                {
                    dtu.SystemState = state;
                }
                ExecReadSystemState?.Invoke(sender, dtu);
            }
        }

        /// <summary>
        /// 解析读设备配置回复数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="data"></param>
        private void ParseReadDeviceConfig(object sender, ArriveDataEventArgs e)
        {
            DTUConfig config = _protocol.UnpackReadDTUDeviceConfig(e.Data);
            if (config != null)
            {
                DTU dtu = (DTU)GetExistDevice(e.Source);
                if (dtu != null)
                {
                    dtu.Config = config;
                }
                ExecReadDeviceConfig?.Invoke(sender, dtu);
            }
        }

        /// <summary>
        /// 解析写设备配置回复数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="data"></param>
        private void ParseWriteDeviceConfig(object sender, ArriveDataEventArgs e)
        {
            bool res = _protocol.UnpackWriteDTUDeviceConfig(e.Data);
            ExecWriteDeviceConfig?.Invoke(sender, res);
        }

        /// <summary>
        /// 获取已存在于列表中的设备实例
        /// </summary>
        /// <param name="device">设备</param>
        /// <returns>true是，false否</returns>
        public GatewayBase GetExistDevice(SniffDeviceInfo device)
        {
            if (_FoundDeviceList == null || _FoundDeviceList.Count == 0) return null;
            for (int i = _FoundDeviceList.Count - 1; i >= 0; i--)
            {
                if (_FoundDeviceList[i].SniffInfo.Source.Equals(device.Source) && _FoundDeviceList[i].SniffInfo.SN.Equals(device.SN))
                {
                    return _FoundDeviceList[i];
                }
            }
            return null;
        }

        /// <summary>
        /// 获取已存在的设备实例
        /// </summary>
        /// <param name="source">通信来源</param>
        /// <returns>true是，false否</returns>
        public GatewayBase GetExistDevice(string source)
        {
            if (_FoundDeviceList == null || _FoundDeviceList.Count == 0) return null;
            for (int i = _FoundDeviceList.Count - 1; i >= 0; i--)
            {
                if (_FoundDeviceList[i].SniffInfo.Source.Equals(source))
                {
                    return _FoundDeviceList[i];
                }
            }
            return null;
        }

        /// <summary>
        /// 启动服务
        /// </summary>
        /// <param name="port">端口号</param>
        /// <returns>启动结果，true成功，false失败</returns>
        public bool StartService(int port)
        {
            return this.TcpServer.OpenServer(port);
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        public void StopService()
        {
            this.TcpServer.CloseServer();
        }
    }

    /// <summary>
    /// 交互类型
    /// </summary>
    public enum InteractiveType 
    {
        /// <summary>
        /// 发送
        /// </summary>
        SEND,
        /// <summary>
        /// 接收
        /// </summary>
        RECV,
    }

    /// <summary>
    /// 服务交互数据类
    /// </summary>
    public class ServiceInteractiveData
    {
        /// <summary>
        /// 数据交互类型
        /// </summary>
        public InteractiveType DataType { get; set; }
        /// <summary>
        /// 网关序列号
        /// </summary>
        public string GatewaySN { get; set; }
        /// <summary>
        /// 数据
        /// </summary>
        public byte[] Data { get; set; }
        /// <summary>
        /// 是否需要将数据转为GB2312编码
        /// </summary>
        public bool IsConvertGB2312 { get; set; }
    }
}