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 ModbusAsciiServer: IModbusServer
    {
        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
        const byte MB_ERR_FORMAT = 0x06;//ASCII 翻译出错


        enum ModbusAsciiStatus
        {
            MBA_STATUS_3A = 0,
            MBA_STATUS_0A = 2
        }
        ModbusAsciiStatus status;
        ModbusRegReadWriteHandler regRead;
        ModbusRegReadWriteHandler regWrite;
        Action<IEnumerable<byte>> sendMsg;

        //接收缓冲
        List<byte> recBuf = new List<byte>();

        //上一次接收数据的时间
        DateTime lastRecMsgTime;

        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;

            //每个包间隔最大1s。大于1s，清空
            this.status = ModbusAsciiStatus.MBA_STATUS_3A;
        }

        private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Modbus_IntervalCb();
        }



        /// <summary>
        /// Read REGs
        /// </summary>
        /// <param name="buf">传入通讯字元</param>
        /// <param name="startIndex"></param>
        /// <param name="len">buf长度</param>
        void Modbus_Func03(IEnumerable<byte> buf, int startIndex, int len)
        {

            //请求
            //0.	':' 0x3A
            //1.	slave address : 2bytes ascii 
            //2.	Function : 2bytes ascii 
            //3.	Starting Address Hi : 2bytes ascii 
            //4.	Starting Address Lo : 2bytes ascii 
            //5.	Number of Register Hi : 2bytes ascii 
            //6.	Number of Register Lo : 2bytes ascii 
            //7.	LRC : 2bytes ascii 
            //8.	0x0D 0x0A

            //响应
            //0.	':' 0x3A
            //1.	slave address : 2bytes ascii 
            //2.	Function : 2bytes ascii 
            //3.	Number * 2 : 2bytes ascii 

            //下面重复 Number 次
            //4.	Data Hi : 2bytes ascii  
            //5.	Data Lo : 2bytes ascii 

            //6.	LRC : 2bytes ascii 
            //7.	0x0D 0x0A


            //example: 下面为 ASCII
            //请求
            //: 01 03 00 01 00 01 ?? (0X0D) (0X0A)
            //响应
            //: 01 03 02 12 34 ?? (0X0D) (0X0A)

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

            if (len < 6)
            {
                Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常
                return;
            }

            byte deviceNo = buf.ElementAt(startIndex);
            UInt16 startAddr = buf.ToUInt16_Big_Endian(startIndex + 2);
            UInt16 number = buf.ToUInt16_Big_Endian(startIndex + 4);

            if (number == 0)
            {
                Modbus_DefaultCallback(buf, startIndex, MB_ERR_NUM_0);//指令异常
                return;
            }
            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)
            {
                //读取失败，没有设备，不用回复
                return;
            }

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

            Modbus_AddLrcAndSend(midBuf);
        }


        /// <summary>
        /// Write Single REG
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="startIndex"></param>
        /// <param name="len"></param>
        void Modbus_Func06(IEnumerable<byte> buf, int startIndex, int len)
        {
            //请求
            //0.	':' 0x3A
            //1.	slave address : 2bytes ascii 
            //2.	Function : 2bytes ascii 
            //3.	Starting Address Hi : 2bytes ascii 
            //4.	Starting Address Lo : 2bytes ascii 
            //5.	Data Hi : 2bytes ascii 
            //6.	Data Lo : 2bytes ascii 
            //7.	LRC : 2bytes ascii 
            //8.	0x0D 0x0A

            //响应
            //0.	':' 0x3A
            //1.	slave address : 2bytes ascii 
            //2.	Function : 2bytes ascii 
            //3.	Starting Address Hi : 2bytes ascii 
            //4.	Starting Address Lo : 2bytes ascii 
            //5.	Data Hi : 2bytes ascii 
            //6.	Data Lo : 2bytes ascii 
            //7.	LRC : 2bytes ascii 
            //8.	0x0D 0x0A

            //example: 下面为 ASCII
            //请求
            //: 01 06 00 01 00 01 ?? (0X0D) (0X0A)
            //响应
            //: 01 06 00 01 00 01 ?? (0X0D) (0X0A)

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


            if (len < 6)
            {
                Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);//指令异常
                return;
            }
            byte deviceNo = buf.ElementAt(startIndex);
            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)
            {
                //写入失败，没有设备，不用回复
                return;
            }

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

        /// <summary>
        /// Write REGs
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="startIndex"></param>
        /// <param name="len"></param>
        void Modbus_Func10(IEnumerable<byte> buf, int startIndex, int len)
        {
            //请求
            //0.	':' 0x3A
            //1.	slave address : 2bytes ascii 
            //2.	Function : 2bytes ascii 
            //3.	Starting Address Hi : 2bytes ascii 
            //4.	Starting Address Lo : 2bytes ascii 
            //5.	Number of Register Hi : 2bytes ascii 
            //6.	Number of Register Lo : 2bytes ascii 
            //7.	Byte Count : 2bytes ascii 
            //8.	Data Hi : 2bytes ascii 
            //9.	Data Lo : 2bytes ascii 
            //10.	Data Hi : 2bytes ascii 
            //11.	Data Lo : 2bytes ascii 
            //12.	Data Hi : 2bytes ascii 
            //13.	Data Lo : 2bytes ascii 
            //14.	LRC : 2bytes ascii 
            //15.	0x0D 0x0A

            //响应
            //0.	':' 0x3A
            //1.	slave address : 2bytes ascii 
            //2.	Function : 2bytes ascii 
            //3.	Starting Address Hi : 2bytes ascii 
            //4.	Starting Address Lo : 2bytes ascii 
            //5.	Number of Register Hi : 2bytes ascii 
            //6.	Number of Register Lo : 2bytes ascii 
            //7.	LRC : 2bytes ascii 
            //8.	0x0D 0x0A

            //example: 下面为 ASCII
            //请求
            //: 01 10 00 01 00 01 02 12 34 ?? (0X0D) (0X0A)
            //响应
            //: 01 10 00 01 00 01 ?? (0X0D) (0X0A)

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

            if (len < 7)
            {
                //指令异常,指令字节数不足
                Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);
                return;
            }

            byte deviceNo = buf.ElementAt(startIndex);
            UInt16 startAddr = buf.ToUInt16_Big_Endian(startIndex + 2);
            UInt16 number = buf.ToUInt16_Big_Endian(startIndex + 4);
            byte cnt = buf.ElementAt(startIndex + 6);
            if (number == 0)
            {
                //指令异常, 居然没有任何寄存器
                Modbus_DefaultCallback(buf, startIndex, MB_ERR_NUM_0);
                return;
            }

            if (cnt != number * 2)
            {
                //指令异常，字节数!=寄存器数*2
                Modbus_DefaultCallback(buf, startIndex, MB_ERR_CNT);
                return;
            }

            if (len < (7 + cnt))
            {
                //指令异常, 接受的字符不足够
                Modbus_DefaultCallback(buf, startIndex, MB_ERR_LEN);
                return;
            }

            
            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)
            {
                //写入失败，没有设备，不用回复
                return;
            }

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

        void 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);

            Modbus_AddLrcAndSend(midBuf);
        }

        int last_index = -1;
        void RecMsg(IEnumerable<byte> buf)
        {
            if (lastRecMsgTime != DateTime.MinValue)
            {
                if (DateTime.Now - lastRecMsgTime > TimeSpan.FromSeconds(1)) {
                    //异常，清空数据
                    recBuf.Clear();
                }
            }
            lastRecMsgTime = DateTime.Now;


            recBuf.AddRange(buf);

            bool ret = false;
            while (true)
            {
                int index = last_index + 1;
                if (index >= recBuf.Count())
                    break;
                ret = Modbus_SingleRec(recBuf, index);
                if (ret == true)
                {
                    last_index = -1;
                    break;
                }
                else
                {
                    last_index = index;
                }
            }

            if (ret)
                recBuf.Clear();
        }
        void ClearRecMsg() 
        {
            recBuf.Clear();
            last_index = -1;
        }

        /// <summary>
        /// 每个字符判断, 处理完成，返回true, buf全部清除
        /// </summary>
        /// <param name="buf"></param>
        /// <param name="startIndex"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        bool Modbus_SingleRec(IEnumerable<byte> buf, int index)
        {
            if (index == 0)
            {
                if (buf.ElementAt(index) != (byte)':')
                {
                    //包头错，可以删除
                    return true;
                }
                this.status = ModbusAsciiStatus.MBA_STATUS_0A;
            }
            else
            {
                if (index >= 3)
                {
                    if (buf.ElementAt(index) == 0x0A) 
                    {
                        if (buf.ElementAt(index - 1) == 0x0D)
                        {
                            //可以解析了
                            Modbus_Handle(buf, 0, index+1);
                            this.status = ModbusAsciiStatus.MBA_STATUS_3A;
                            return true;
                        }
                        else 
                        {
                            //结尾出错
                            return true;
                        }
                    }
                }
            }
            return false;
        }


        void Modbus_IntervalCb()
        {
            if (this.status == ModbusAsciiStatus.MBA_STATUS_3A)
                return;

            //长时间没收到更多数据，清除数据缓存
            this.status = ModbusAsciiStatus.MBA_STATUS_3A;
            ClearRecMsg();
        }

        void Modbus_Handle(IEnumerable<byte> buf, int startIndex, int len)
        {
            //0x3A(':')
            //address	:2bytes ascii 
            //function	:2bytes ascii
            //data		:2*n bytes ascii
            //LRC		:2bytes ascii
            //0x0D 0X0A
            //



            //DEBUG_printf("Modbus_Check_RTU(buf,%d)\r\n",len);
            //for(i=0;i<len;i++)
            //	DEBUG_printf("0x%x ",buf[i]);
            //DEBUG_printf("\r\n");

            if (len < 11)//最短是11个bytes
                return;
            if (len % 2 != 1)//必须是奇数
                return;
            int adu_len = (len - 1 - 2 - 2) / 2;

            //ascii 转为 hex
            List<byte> midBuf = new List<byte>();
            int index = 1;
            byte? b;
            for (int i = 0; i < adu_len; i++)
            {
                b = buf.Ascii2Hex(startIndex + index);
                if (b == null) {
                    Modbus_DefaultCallback(midBuf, 0, MB_ERR_FORMAT);//指令异常
                    return ;
                }
                midBuf.Add((byte)b);
                index += 2;
            }
            //# ifndef MB_DEBUG_NO_LRC
            b= buf.Ascii2Hex(startIndex + index);
            if (b == null)
            {
                Modbus_DefaultCallback(midBuf, 0, MB_ERR_FORMAT);//指令异常
                return;
            }

            byte lrc = (byte)b;
            byte lrcCheck = midBuf.LRC8(0, midBuf.Count());
            if (lrc != lrcCheck)
            {
                Modbus_DefaultCallback(midBuf, 0, MB_ERR_LRC);//指令异常
                return;
            }
            //#endif

            byte func = midBuf[1];

            switch (func)
            {
                //case 0x01:
                //	Modbus_Func01(buf, adu_len);
                //	break;
                case 0x03:
                    Modbus_Func03(midBuf, 0, adu_len);
                    break;
                //case 0x05:
                //	Modbus_Func05(buf, adu_len);
                //	break;
                case 0x06:
                    Modbus_Func06(midBuf, 0, adu_len);
                    break;
                //case 0x0F:
                //	Modbus_Func0F(buf, adu_len);
                //	break;
                case 0x10:
                    Modbus_Func10(midBuf, 0, adu_len);
                    break;
                default:
                    Modbus_DefaultCallback(midBuf, 0, MB_ERR_NOFUNC);//没此func
                    break;
            }
        }

        void Modbus_AddLrcAndSend(IEnumerable<byte> buf)
        {
            int startIndex = 0;
            int len = buf.Count();

            //添加 : , LRC, 0x0D 0x0A
            byte lrc = buf.LRC8(startIndex, len);

            List<byte> sendBuf = new List<byte>();
            sendBuf.Add((byte)':');           
            sendBuf.AddRange(buf.HexToAscii(startIndex, len));
            sendBuf.AddRange(lrc.HexToAscii());
            sendBuf.Add(0x0D);
            sendBuf.Add(0x0A);
            
            sendMsg?.Invoke(sendBuf);
        }

    }

}
