using Aitex.Common.Util;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using MECF.Framework.Common.Device.Festo;
using MECF.Framework.Common.Net;
using MECF.Framework.Simulator.Core.Driver;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
namespace CyberX8_Simulator.Devices
{
    public class FestoSocketSimulator : SocketDeviceSimulator
    {
        //Write Address   
        private const ushort FESTO_DO_START_ADDRESS = 0x9C43;//FestoDO起始地址(40003)
        //Read Address
        private const ushort FESTO_DI_START_ADDRESS = 0xB153;//FestoDI起始地址(45395)
        //Register Count
        private const int FESTO_REGISTER_COUNT = 100;
        private IByteTransform byteTransform = new BigEndianByteTransformBase();
        /// 
        /// 数据(index - data)
        /// 
        private short[] _festoOutputDataDic = new short[FESTO_REGISTER_COUNT];
        /// 
        /// 数据(DOName - FestoDO)
        /// 
        public Dictionary FestoNameIndexDic = new Dictionary();
        /// 
        /// 数据字典(DOName - value)
        /// 
        public Dictionary FestoNameValueDic = new Dictionary();
        /// 
        /// do 索引对象字典(key-地址索引-bit,value--do名称)
        /// 
        private Dictionary _doNameDictionary = new Dictionary();
        /// 
        /// 构造函数
        /// 
        /// 
        public FestoSocketSimulator(int port) : base(port)
        {
            InitData(port);            
        }
        /// 
        /// 解析信息
        /// 
        /// 
        protected override void ProcessUnsplitMessage(byte[] data)
        {
            short flag = byteTransform.TransInt16(data, 0);//事务标识符
            byte channel = data[6];//单元标识符
            byte command = data[7];//功能码
            if (command == 0x03)//读取
            {
                ushort startAddress = byteTransform.TransUInt16(data, 8);//起始寄存器地址
                short registerCount = byteTransform.TransInt16(data, 10);//寄存器数量
                byte[] bytes = new byte[2 * registerCount];//读取2*registerCount Byte数据
                if (startAddress >= FESTO_DI_START_ADDRESS)
                {
                    for (int i = 0; i < registerCount; i++)
                    {
                        Array.Copy(byteTransform.GetBytes(_festoOutputDataDic[startAddress - FESTO_DI_START_ADDRESS + i]), 0, bytes, i * 2, 2);
                    }
                    OnWriteMessage(CreateReadResponse(flag, channel, command, registerCount, bytes));
                }
                else
                {
                    OnWriteMessage(CreateError(flag, channel, command, 0x8A));
                }                
            }
            else if(command == 0x06)//写入
            {
                ushort startAddress = byteTransform.TransUInt16(data, 8);//起始寄存器地址
                short value = byteTransform.TransInt16(data, 10);//写入的值(2 Byte)
                if(startAddress >= FESTO_DO_START_ADDRESS)
                {
                    //通知相关数据变化
                    bool changeFlag = true;
                    var result = DecodeDOData(startAddress, value, out changeFlag);
                    if (changeFlag)
                    {                      
                        SimulatorCommManager.Instance.CheckDataChanged(result.Item1, result.Item2, FestoNameIndexDic[result.Item1].Invert);
                    }                    
                    //modbus起始地址n为数据,n+1为诊断数据,取地址n下的数据
                    _festoOutputDataDic[(startAddress - FESTO_DO_START_ADDRESS) * 2] = value;
                    
                    OnWriteMessage(CreateWriteResponse(flag, channel, command, startAddress, value));
                }
                else
                {
                    OnWriteMessage(CreateError(flag, channel, command, 0x8A));
                }                
            }
            else
            {
                OnWriteMessage(CreateError(flag, channel, command, 0x84));
            }
        }
        /// 
        /// 读回复
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        private byte[] CreateReadResponse(short flag, byte channel, byte command, short registerCount, byte[] values)
        {
            
            byte[] bytes = new byte[6 + 3 + values.Length];
            Array.Copy(byteTransform.GetBytes(flag), 0, bytes, 0, 2);
            bytes[2] = 0x00;
            bytes[3] = 0x00;
            short dataLength = (short)(3 + values.Length);
            Array.Copy(byteTransform.GetBytes(dataLength), 0, bytes, 4, 2);
            bytes[6] = channel;
            bytes[7] = command;
            bytes[8] = (byte)(2 * registerCount);
            Array.Copy(values, 0, bytes, 9, values.Length);
            return bytes;
        }
        /// 
        /// 写回复
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        private byte[] CreateWriteResponse(short flag, byte channel, byte command, ushort startAddress, short value)
        {
            byte[] bytes = new byte[12];
            Array.Copy(byteTransform.GetBytes(flag), 0, bytes, 0, 2);
            bytes[2] = 0x00;
            bytes[3] = 0x00;
            bytes[4] = 0x00;
            bytes[5] = 0x06;
            bytes[6] = channel;
            bytes[7] = command;
            byte[] addressByt = byteTransform.GetBytes(startAddress);
            Array.Copy(addressByt, 0, bytes, 8, 2);
            byte[] valueByt = byteTransform.GetBytes(value);
            Array.Copy(valueByt, 0, bytes, 10, 2);
            return bytes;
        }
        
        /// 
        /// 错误回复
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        private byte[] CreateError(short flag, byte channel, byte command, byte error)
        {
            byte[] bytes = new byte[9];
            Array.Copy(byteTransform.GetBytes(flag), 0, bytes, 0, 2);
            bytes[2] = 0x00;
            bytes[3] = 0x00;
            bytes[4] = 0x00;
            bytes[5] = 0x03;
            bytes[6] = channel;
            bytes[7] = (byte)(command | 0x80);
            bytes[8] = error;
            return bytes;
        }
        /// 
        /// 更新DI数据
        /// 
        /// 
        /// 
        public void UpdataDOBytes(string name, int value)
        {
            if (FestoNameIndexDic.ContainsKey(name))
            {
                FestoDO festoDO = FestoNameIndexDic[name];
                short byteValue = (short) (_festoOutputDataDic[(festoDO.Address - FESTO_DO_START_ADDRESS) * 2] & ~(1 << festoDO.Bit));
                short tmp = (short)(value << festoDO.Bit);
                _festoOutputDataDic[(festoDO.Address - FESTO_DO_START_ADDRESS) * 2] = (short)(byteValue | tmp);
            }
        }
        /// 
        /// 解析数据
        /// 
        /// 
        /// 
        /// 
        private (string, bool) DecodeDOData(ushort address, short value, out bool flag, bool check = false)
        {
            flag = true;
            int index = (address - FESTO_DO_START_ADDRESS) * 2;
            if (!check &&_festoOutputDataDic[index] == value) 
            {
                flag = false;
                return ("", false);
            } 
            int bitNum = (int)Math.Log(_festoOutputDataDic[index] ^ value, 2);
            bool valueBool = (value & (1 << bitNum)) != 0 ? true : false;
            string str = $"{address}-{bitNum}";
            return (_doNameDictionary[str], valueBool);
        }
        /// 
        /// 获取当前DO数据
        /// 
        /// 
        /// 
        public int GetDOData(string doName)
        {
            FestoDO data = FestoNameIndexDic[doName];
            int result = 0;
            result = (_festoOutputDataDic[(data.Address - FESTO_DO_START_ADDRESS) * 2] & (short)(1 << data.Bit)) == 0 ? 0 : 1;
            return result;
        }
        /// 
        /// 初始化
        /// 
        private void InitData(int port)
        {
            //初始化数据
            for (int i = 0; i < FESTO_REGISTER_COUNT; i++)
            {
                _festoOutputDataDic[i] = 0x00;
            }
            //加载对应配置文件 FestoControllerCfg-Simulator.xml
            try
            {
                string oldXmlPath = PathManager.GetCfgDir();
                string newXmlPath = oldXmlPath.Replace("CyberX8_Simulator", "CyberX8_RT") + "Devices\\FestoControllerCfg-Simulator.xml";
                FestoControllerCfg cfg = CustomXmlSerializer.Deserialize(new FileInfo(newXmlPath));
                if (cfg != null)
                {
                    foreach (FestoDeviceConfig config in cfg.FestoDeviceConfigs)
                    {
                        if (port == config.Port)
                        {
                            foreach (FestoDO item in config.FestoDoes)
                            {
                                FestoNameIndexDic[item.Name] = item;
                                string str = $"{item.Address}-{item.Bit}";
                                _doNameDictionary[str] = item.Name;
                            }
                        }
                    }
                }
            }
            catch
            {
                LOG.WriteLog(eEvent.ERR_FESTO, "Festo", "Load festo FestoControllerCfg-Simulator.xml failed");
            }
            
            
        }
    }
}