using NLog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GeneralGommunication
{
    public class ModbusRtuServer : IModbusServer
    {
        public Logger logger = NLog.LogManager.GetCurrentClassLogger();

        const byte MB_ERR_LRC = 0x04;//LRC校验出错
        const byte MB_ERR_LEN = 0x01;//指令字节数不足
        const byte MB_ERR_NUM_0 = 0x02;//寄存器数量=0
        const byte MB_ERR_CNT = 0x03;//字节数!=寄存器数量*2
        const byte MB_ERR_NOFUNC = 0x05;//没有这个func

       
        ModbusRegReadWriteHandler regRead;
        ModbusRegReadWriteHandler regWrite;
        Action<IEnumerable<byte>> sendMsg;

        //接收缓冲
        List<byte> recBuf = new List<byte>();
        //System.Timers.Timer timer3d5t;//通讯包的 3.5T 时间间隔。 实际只是 15ms定时而已
        System.Threading.Timer timer3d5t;//通讯包的 3.5T 时间间隔。 实际只是 15ms定时而已


        public void Init(ModbusRegReadWriteHandler regRead, ModbusRegReadWriteHandler regWrite,
            Action<IEnumerable<byte>> sendMsg, out Action<IEnumerable<byte>> recMsg)
        {
            this.regRead = regRead;
            this.regWrite = regWrite;
            this.sendMsg = sendMsg;
            recMsg = RecMsg;

            //timer3d5t = new System.Timers.Timer();
            //timer3d5t.Interval = 30;//设得再小也没有用。 它只是用系统的15ms定时器触发而已。 
            //timer3d5t.AutoReset = false;//触发一次
            //timer3d5t.Elapsed += Timer3d5t_Elapsed;

            timer3d5t = new System.Threading.Timer(new System.Threading.TimerCallback(Timer3d5t_Elapsed), null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
        }

        private void Timer3d5t_Elapsed(object state)
        {
            ParsePack();
        }

        //private void Timer3d5t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        //{
        //    ParsePack();

        //}

        /// <summary>
        /// 读寄存器, 返回已经处理的长度
        /// </summary>
        /// <param name="buf">接收缓冲</param>
        /// <param name="startIndex">数据开始序号</param>
        /// <param name="len">数据长度</param>
        /// <returns></returns>
        int Modbus_Func03(IEnumerable<byte> buf, int startIndex, int len, out byte[] sendBuf)
        {
            //请求
            //1.	slave address : 1byte
            //2.	Function : 1byte
            //3.	Starting Address Hi : 1byte
            //4.	Starting Address Lo : 1byte
            //5.	Number of Register Hi : 1byte
            //6.	Number of Register Lo : 1byte
            //7.	CRC16 : 2bytes

            //响应
            //1.	slave address : 1byte
            //2.	Function : 1byte
            //3.	Number * 2 : 1byte 

            //下面重复 Number 次
            //4.	Data Hi : 1byte  
            //5.	Data Lo : 1byte  

            //6.	CRC16 : 2bytes

            //example:
            //请求
            //01 03 00 01 00 01 ?? ??
            //响应
            //01 03 02 12 34 ?? ??

            int req_len = 8; 
            //buf 从 slave address 开始
            if (len < req_len)
            {
                logger.Error($"REQ 指令字节数不足");
                sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常
                return len;
            }


            //CRC
            int adu_len = req_len - 2;//去除crc后的数据包长

            UInt16 crc = buf.CRC16(startIndex, req_len - 2);
            UInt16 rec_crc = buf.ToUInt16_Little_Endian(startIndex + req_len - 2);//CRC 是 小端模式

            if (crc != rec_crc)
            {
                logger.Error($"REQ CRC 校验出错 接收:{rec_crc:X4} 计算:{crc:X4}");

                sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LRC);//指令异常
                return len;//指令异常，应该全部删除
            }


            byte deviceNo = recBuf[0];
            UInt16 startAddr = buf.ToUInt16_Big_Endian(startIndex + 2);
            UInt16 number = buf.ToUInt16_Big_Endian(startIndex + 4);

            if (number == 0)
            {
                logger.Error($"REQ 没有任何寄存器");
                sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_NUM_0);//指令异常
                return req_len;
            }
            if (number > 50)//限制数量，因为缓存区不够大
                number = 50;
            List<byte> midBuf = new List<byte>();
            midBuf.Add(deviceNo);
            midBuf.Add(0x03);
            midBuf.Add((byte)(number * 2));

            UInt16[] regValues = new UInt16[number];
            bool ret = this.regRead(deviceNo, startAddr, regValues);
            if (!ret)
            {
                //读取失败，没有设备，不用回复
                logger.Debug($"REQ 读取失败，没有设备，不用回复");
                sendBuf = null;
                return req_len;
            }

            for (int i = 0; i < number; i++)
            {
                midBuf.AddRange(regValues[i].GetBytes_Big_endian());
            }

            sendBuf = midBuf.ToArray();
            return req_len; 
            //Modbus_AddCrcAndSend(midBuf);
        }


        /// <summary>
        /// Write Single REG
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="startIndex"></param>
        /// <param name="len"></param>
        int Modbus_Func06(IEnumerable<byte> buf, int startIndex, int len, out byte[] sendBuf)
        {
            //协议为
            //1.	slave address
            //2.	Function
            //3.	Starting Address Hi
            //4.	Starting Address Lo
            //5.	Data Hi : 
            //6.	Data Lo : 
            //7.	CRC16 : 2bytes 

            //响应
            //1.	slave address
            //2.	Function
            //3.	Starting Address Hi
            //4.	Starting Address Lo
            //5.	Data Hi : 
            //6.	Data Lo : 
            //7.	CRC16 : 2bytes 


            //example: 下面为 ASCII
            //请求
            //01 06 00 01 12 34 ?? ??
            //响应
            //01 06 00 01 12 34 ?? ??

            //buf 从 slave address 开始

            int req_len = 8;

            if (len < req_len)
            {
                logger.Error($"REQ 指令字节数不足");
                sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常
                return len;
            }

            //CRC
            int adu_len = req_len - 2;//去除crc后的数据包长

            UInt16 crc = buf.CRC16(startIndex, req_len - 2);
            UInt16 rec_crc = buf.ToUInt16_Little_Endian(startIndex + req_len - 2);

            if (crc != rec_crc)
            {
                logger.Error($"REQ CRC 校验出错 接收:{rec_crc:X4} 计算:{crc:X4}");

                sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LRC);//指令异常
                return len;//指令异常，应该全部删除
            }


            byte deviceNo = recBuf[0];
            UInt16 startAddr = buf.ToUInt16_Big_Endian(startIndex+2);
            UInt16 regValue = buf.ToUInt16_Big_Endian(startIndex+4);


            UInt16[] regValues = new UInt16[] { regValue };
            bool ret = this.regWrite(deviceNo, startAddr, regValues);
            if (!ret)
            {
                logger.Debug($"REQ 写入失败，没有设备，不用回复");
                sendBuf = null;
                return req_len;
            }

            //完成
            List<byte> midBuf = new List<byte>();
            midBuf.AddRange(buf.Skip(startIndex).Take(6));

            sendBuf = midBuf.ToArray();
            return req_len;

            //Modbus_AddCrcAndSend(midBuf);
        }

        /// <summary>
        /// Write REGs
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="startIndex"></param>
        /// <param name="len"></param>
        int Modbus_Func10(IEnumerable<byte> buf, int startIndex, int len, out byte[] sendBuf)
        {
            //协议为
            //1.	slave address
            //2.	Function 
            //3.	Starting Address Hi 
            //4.	Starting Address Lo 
            //5.	Number of Register Hi
            //6.	Number of Register Lo 
            //7.	Byte Count 
            //8.	Data Hi 
            //9.	Data Lo  
            //10.	Data Hi 
            //11.	Data Lo 
            //12.	Data Hi 
            //13.	Data Lo
            //14.	CRC16 : 2bytes

            //响应
            //1.	slave address
            //2.	Function 
            //3.	Starting Address Hi 
            //4.	Starting Address Lo 
            //5.	Number of Register Hi
            //6.	Number of Register Lo 
            //7.	CRC16 : 2bytes

            //example: 下面为 ASCII
            //请求
            //: 01 10 00 01 00 01 02 12 34 ?? ??
            //响应
            //: 01 10 00 01 00 01 ?? ??

            //buf 已经是 ascii转hex 后结果。 从 slave address 开始

            int req_len = 9;

            if (len < req_len)
            {
                logger.Error($"REQ 指令字节数不足");
                sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常
                return len;
            }
            byte deviceNo = recBuf[0];
            UInt16 startAddr = buf.ToUInt16_Big_Endian(startIndex + 2);
            UInt16 number = buf.ToUInt16_Big_Endian(startIndex + 4); 
            byte cnt = buf.ElementAt(startIndex + 6);

            req_len = 9 + cnt;
            if (len < req_len)
            {
                logger.Error($"REQ 指令字节数不足");
                sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常
                return len;
            }

            //CRC
            int adu_len = req_len - 2;//去除crc后的数据包长

            UInt16 crc = buf.CRC16(startIndex, req_len - 2);
            UInt16 rec_crc = buf.ToUInt16_Little_Endian(startIndex + req_len - 2);//CRC 是 小端模式

            if (crc != rec_crc)
            {
                logger.Error($"REQ CRC 校验出错 接收:{rec_crc:X4} 计算:{crc:X4}");

                sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_LRC);//指令异常
                return len;//指令异常，应该全部删除
            }

            if (number == 0)
            {
                //指令异常, 居然没有任何寄存器
                logger.Error($"REQ 没有任何寄存器");
                sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_NUM_0);
                return req_len;
            }

            if (cnt != number * 2)
            {
                //指令异常，字节数!=寄存器数*2
                logger.Error($"REQ 字节数!=寄存器数*2");
                sendBuf = Modbus_DefaultCallback(buf, startIndex, MB_ERR_CNT);
                return req_len;
            }

            int index = 7;
            UInt16[] regValues = new UInt16[number];
            for (int i = 0; i < number; i++)
            {
                UInt16 regValue = buf.ToUInt16_Big_Endian(startIndex + index);
                index += 2;
                regValues[i] = regValue;
            }

            bool ret = this.regWrite(deviceNo, startAddr, regValues);
            if (!ret)
            {
                logger.Debug($"REQ 写入失败，没有设备，不用回复");
                sendBuf = null;
                return req_len;
            }


            //完成
            List<byte> midBuf = new List<byte>();
            midBuf.AddRange(buf.Skip(startIndex).Take(6));
            sendBuf = midBuf.ToArray();
            return req_len;
            //Modbus_AddCrcAndSend(midBuf);
        }

        byte[] Modbus_DefaultCallback(IEnumerable<byte> buf, int startIndex, byte err)
        {
            //格式
            //Field Name		Example(Hex)
            //Slave Address		01
            //Function			81
            //Exception Code	02
            List<byte> midBuf = new List<byte>();
            midBuf.Add(buf.ElementAt(startIndex + 0));
            midBuf.Add((byte)(buf.ElementAt(startIndex + 1) | 0x80));
            midBuf.Add(err);
            return midBuf.ToArray();
            //Modbus_AddCrcAndSend(midBuf);
        }





        void RecMsg(IEnumerable<byte> buf)
        {
            //15ms后再处理。
            //timer3d5t.Stop();
            timer3d5t.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            recBuf.AddRange(buf);


            if (logger.IsDebugEnabled)
            {
                string msg = recBuf.ToString_Bytes2Hex();
                logger.Debug($"REQ {msg}");
            }

            ParsePack();
            //timer3d5t.Start();
            timer3d5t.Change(30, System.Threading.Timeout.Infinite);
        }

        void ParsePack() 
        {
            //只处理最后一条


            //address	:1byte
            //function	:1byte
            //data		:Nbyte
            //CRC16		:2bytes

            //收到了完成一段数据再判断的

            if (recBuf.Count() < 5)
            {
                //异常 数据太少
                recBuf.Clear();
                return;
            }

            while (true)
            {
                byte func = recBuf[1];
                byte[] sendBuf;
                int len;
                switch (func)
                {
                    //case 0x01:
                    //	Modbus_Func01(buf, adu_len);
                    //	break;
                    case 0x03:
                        len = Modbus_Func03(recBuf, 0, recBuf.Count(), out sendBuf);
                        break;
                    //case 0x05:
                    //	Modbus_Func05(buf, adu_len);
                    //	break;
                    case 0x06:
                        len = Modbus_Func06(recBuf, 0, recBuf.Count(), out sendBuf);
                        break;
                    //case 0x0F:
                    //	Modbus_Func0F(buf, adu_len);
                    //	break;
                    case 0x10:
                        len = Modbus_Func10(recBuf, 0, recBuf.Count(), out sendBuf);
                        break;
                    default:
                        {
                            logger.Error($"REQ 不支持功能号");
                            //全删除
                            sendBuf = Modbus_DefaultCallback(recBuf, 0, MB_ERR_NOFUNC);//没此func
                            len = recBuf.Count();
                        }
                        break;
                }

                recBuf.RemoveRange(0, len);
                if (recBuf.Count == 0) {
                    if (sendBuf != null)
                    {
                        //把最后一条发送出去
                        Modbus_AddCrcAndSend(sendBuf);
                    }
                    return;
                }
            }
        }

        
        void Modbus_AddCrcAndSend(IEnumerable<byte> buf)
        {
            //添加 CRC16
            UInt16 crc = buf.CRC16(0, buf.Count());

            List<byte> sendBuf = new List<byte>();
            sendBuf.AddRange(buf);
            sendBuf.AddRange(crc.GetBytes_Little_Endian());

            if (logger.IsDebugEnabled)
            {
                string msg = sendBuf.ToString_Bytes2Hex();
                logger.Debug($"ACK {msg}");
            }

            sendMsg?.Invoke(sendBuf);
        }

    }

}
