using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using MECF.Framework.Common.PLC;
using MECF.Framework.RT.Core.IoProviders;
using System;
using System.Diagnostics;
using System.Xml;

namespace Aitex.Core.RT.Device.Devices
{
    public partial class SiemensIoProvider : IoProvider
    {
        protected readonly DOAccessor _doShutDown;

        private IAdsPlc _ads;
        private static object _locker = new object();

        private R_TRIG _trigConnected = new R_TRIG();
        protected override void SetParameter(XmlElement nodeParameter)
        {
           
        }
        private bool MonitorAdsConnection()
        {

            //if (_SiemensClient == null)
            //{
            //    _SiemensClient = DEVICE.GetDevice<PMModule>(Module + "." + Module)?.Siemens;
            //}
            //if (_SiemensClient != null) return _SiemensClient.IsTrue;
            //return false;

            if (_ads == null)
            {
                _ads = DEVICE.GetOptionDevice($"{Module}.MainPLC", typeof(SicAds)) as IAdsPlc;
            }

            return _ads != null && _ads.CheckIsConnected();
        }

        //public bool IsCommunicationError
        //{
        //    get { return _ads.IsCommunicationError; }
        //}
        protected override void Close()
        {

        }
        protected override void Open()
        {

        }

        protected override bool OnTimer()
        {
            _trigConnected.CLK = MonitorAdsConnection();

            if (!_trigConnected.M)
                return true;

            lock (_locker)
            {
                // PLC数据以32位二级制存储，软件读取的值与DINT相同
                // 如果是DINT类型数据，直接使用，无需转换；
                // 如果是REAL类型数据，先使用BitConverter.GetBytes获取有4个元素的字节数组，再使用BitConverter.ToSingle，得到float类型值；
                // 如果是DWORD类型数据，先使用BitConverter.GetBytes获取有4个元素的字节数组，再进行位运算，得到每位的值

                //int[] input = ReadInput();
                //Console.WriteLine(input[0]);
                //byte[] byteArr = BitConverter.GetBytes(input[0]);
                //float fValve = BitConverter.ToSingle(byteArr, 0);

                foreach (var key in DiVariables.Keys)
                {
                    var ioVar = DiVariables[key];
                    var diData = ReadDIn(ioVar.Name, (ushort)ioVar.Length);
                    if (diData != null)
                    {
                        var diBuffer = ParseDI(key, diData);
                        if (diBuffer != null)
                        {
                            _buffer.SetDiBuffer(_source, ioVar.Block.Index, diBuffer);
                        }
                    }
                }


                foreach (var key in DoVariables.Keys)
                {
                    var ioVar = DoVariables[key];
                    var doData = ReadDOut(ioVar.Name, (ushort)ioVar.Length);
                    if (doData != null)
                    {
                        var doBuffer = ParseDO(key, doData);
                        if (doBuffer != null)
                        {
                            _buffer.SetDoBuffer(_source, ioVar.Block.Index, doBuffer);
                        }
                    }
                }


                foreach (var key in AiVariables.Keys)
                {
                    var ioVar = AiVariables[key];
                    var aiData = ReadAIn(ioVar.Name, (ushort)ioVar.Length);
                    if (aiData != null)
                    {
                        var aiBuffer = ParseAI(key, aiData);
                        if (aiBuffer != null)
                        {
                            _buffer.SetAiBuffer(_source, ioVar.Block.Index, aiBuffer);
                        }
                    }
                }

                foreach (var key in AoVariables.Keys)
                {
                    var ioVar = AoVariables[key];
                    var aoData = ReadAOut(ioVar.Name, (ushort)ioVar.Length);
                    if (aoData != null)
                    {
                        var aoBuffer = ParseAO(key, aoData);
                        if (aoBuffer != null)
                        {
                            _buffer.SetAoBuffer(_source, ioVar.Block.Index, aoBuffer);
                        }
                    }
                }

            }

            return true;
        }

        private bool CommunicationError()
        {
            //_buffer.SetDiBuffer(_source, 0, new bool[1000]);
            //_buffer.SetDoBuffer(_source, 0, new bool[1000]);
            //_buffer.SetAiBufferFloat(_source, 0, new float[1000]);
            //_buffer.SetAoBufferFloat(_source, 0, new float[1000]); 

            return true;
        }

        protected byte[] ReadDIn(string variable, ushort len)
        {
            if (!_trigConnected.M)
                return null;

            if (_ads.BulkReadRenderResult(variable, len, out var data))
                return data;

            return null;
        }
        private byte[] ReadDOut(string variable, ushort len)
        {
            if (!_trigConnected.M)
                return null;

            if (_ads.BulkReadRenderResult(variable, len, out var data))
                return (byte[])data;

            return null;
        }

        protected byte[] ReadAIn(string variable, ushort len)
        {
            if (!_trigConnected.M)
                return null;

            if (_ads.BulkReadRenderResult(variable, len, out var data))
                return data;

            return null;
        }
        private byte[] ReadAOut(string variable, ushort len)
        {
            if (!_trigConnected.M)
                return null;

            if (_ads.BulkReadRenderResult(variable, len, out var data))
                return (byte[])data;

            return null;
        }

        private bool[] ParseDI(int blockIndex, byte[] inputValue)
        {
            var dataCount = inputValue.Length * 8;
            var result = new bool[dataCount];
            for (var i = 0; i < dataCount; i++)
            {
                var whichBit = i % 8;
                var whichByte = i % 8 == 0 ? i / 8 : (i - i % 8) / 8;

                result[i] = ((inputValue[whichByte] >> whichBit) & 0x1) == 1;
            }

            return result;
        }


        private bool[] ParseDO(int blockIndex, byte[] outputValue)
        {
            var dataCount = outputValue.Length * 8;
            var result = new bool[dataCount];
            for (var i = 0; i < dataCount; i++)
            {
                var whichBit = i % 8;
                var whichByte = i % 8 == 0 ? i / 8 : (i - i % 8) / 8;

                result[i] = ((outputValue[whichByte] >> whichBit) & 0x1) == 1;
            }

            return result;
        }


        private float[] ParseAI(int blockIndex, byte[] inputValue)
        {
            Debug.Assert(inputValue.Length % 4 == 0, "The length of 'inputValue' must be 4-byte align");

            var dataCount = inputValue.Length / 4;
            var result = new float[dataCount];
            for (var i = 0; i < dataCount; i++)
            {
                if (blockIndex == 0)
                {
                    var index = i;
                    if (index <= 71 || index >= 108 && index <= 117 || index >= 181 || index == 119 || index == 123 || index == 124 || index == 125)
                    {
                        if (index >= 181)
                            index += 181; // 跳过181个AO

                        result[i] = BitConverter.ToSingle([inputValue[index * 4 + 3], inputValue[index * 4 + 2], inputValue[index * 4 + 1], inputValue[index * 4]
                        ], 0);
                    }
                    else if (index >= 72 && index <= 103)
                    {
                        result[i] = BitConverter.ToSingle([inputValue[index * 4 + 3], inputValue[index * 4 + 2], inputValue[index * 4 + 1], inputValue[index * 4]
                        ], 0);
                    }
                    else if (index >= 104 && index <= 107 || index >= 118 && index <= 128)
                    {
                        result[i] = BitConverter.ToUInt32([inputValue[index * 4 + 3], inputValue[index * 4 + 2], inputValue[index * 4 + 1], inputValue[index * 4]
                        ], 0);
                    }
                    else if (index == 129)
                    {
                        result[i] = BitConverter.ToSingle([inputValue[index * 4 + 3], inputValue[index * 4 + 2], inputValue[index * 4 + 1], inputValue[index * 4]
                        ], 0);
                    }
                    else if (index >= 130 && index <= 180)
                    {
                        result[i] = BitConverter.ToInt32([inputValue[index * 4 + 3], inputValue[index * 4 + 2], inputValue[index * 4 + 1], inputValue[index * 4]
                        ], 0);
                    }
                }
                else
                {
                    result[i] = BitConverter.ToSingle([
                        inputValue[i * 4 + 3],
                        inputValue[i * 4 + 2],
                        inputValue[i * 4 + 1],
                        inputValue[i * 4]
                    ], 0);
                }
            }

            return result;

            /*  var aiList = Singleton<IoManager>.Instance.GetAIList($"{Module}.io").Where(x=>x.BlockIndex == blockIndex);
              var result = new float[500];
              foreach (var aiAccessor in aiList)
              {
                  var addr = aiAccessor.Addr;
                  if (int.TryParse(addr, out var index))
                  {
                      var valve = BitConverter.ToSingle([
                          inputValue[index * 4 + 3],
                          inputValue[index * 4 + 2],
                          inputValue[index * 4 + 1],
                          inputValue[index * 4]
                      ], 0);
                      result[aiAccessor.Index] = valve;
                  }
              }
              return result;*/
        }


        private float[] ParseAO(int blockIndex, byte[] outputValue)
        {

            Debug.Assert(outputValue.Length % 4 == 0, "The length of 'outputValue' must be 4-byte align");

            var dataCount = outputValue.Length / 4;
            var result = new float[dataCount];
            for (var i = 0; i < dataCount; i++)
            {
                if (blockIndex == 0)
                {
                    var index = i;
                    var valve = 0.0f;

                    if (index >= 92 && index <= 103)
                    {
                        valve = BitConverter.ToSingle([outputValue[index * 4 + 3], outputValue[index * 4 + 2], outputValue[index * 4 + 1], outputValue[index * 4]
                        ], 0);
                    }
                    else if (index >= 104 && index <= 107)
                    {
                        valve = BitConverter.ToSingle([outputValue[index * 4 + 3], outputValue[index * 4 + 2], outputValue[index * 4 + 1], outputValue[index * 4]
                        ], 0);
                    }
                    else if (index <= 117 || index >= 181 || index == 129 || index == 122 || index == 123 || index == 124 || index == 125)
                    {
                        if (index >= 181)
                            index += 100; // 跳过100个AI

                        valve = BitConverter.ToSingle([outputValue[index * 4 + 3], outputValue[index * 4 + 2], outputValue[index * 4 + 1], outputValue[index * 4]
                        ], 0);
                    }
                    else if (index >= 118 && index <= 128 && index != 119)
                    {
                        valve = BitConverter.ToUInt32([outputValue[index * 4 + 3], outputValue[index * 4 + 2], outputValue[index * 4 + 1], outputValue[index * 4]
                        ], 0);
                    }
                    else if (index >= 130 && index <= 180)
                    {
                        valve = BitConverter.ToInt32([outputValue[index * 4 + 3], outputValue[index * 4 + 2], outputValue[index * 4 + 1], outputValue[index * 4]
                        ], 0);
                    }

                    result[i] = valve;
                }
                else
                {
                    result[i] = BitConverter.ToSingle([
                        outputValue[i * 4 + 3],
                        outputValue[i * 4 + 2],
                        outputValue[i * 4 + 1],
                        outputValue[i * 4]
                    ], 0);
                }
            }

            return result;
        }
        protected override short[] ReadAi(int offset, int size)
        {
            throw new NotImplementedException();
        }

        protected override bool[] ReadDi(int offset, int size)
        {
            throw new NotImplementedException();
        }



        protected override void WriteAo(int offset, short[] buffer)
        {
            throw new NotImplementedException();
        }

        protected override void ReadDo(int offset, bool[] buffer)
        {
            throw new NotImplementedException();
        }

        protected override short[] ReadAo(int offset, int size)
        {
            throw new NotImplementedException();
        }

        protected override void WriteDo(int offset, bool[] buffer)
        {
            throw new NotImplementedException();
        }
        

        public override bool SetValue(AOAccessor aoItem, float value)
        {
            if (!_trigConnected.M)
                return false;

            lock (_locker)
            {
                var data = new byte[4];

                //// 获取下标，转换成Byte数组，然后写下去
                var addr = aoItem.Addr;
                var index = int.Parse(addr);
                var ioVar = AoVariables[aoItem.BlockIndex];

                // Address: DB100.DBDxxx
                // target: xxx +index
                var stringSeparators = new string[] { "DBD" };
                var result = ioVar.Name.Split(stringSeparators, StringSplitOptions.None);
                int.TryParse(result[1], out var startIndex);
                var finalIndex = startIndex + index * sizeof(float); // 4 bytes per data
                var address = $"{result[0]}DBD{finalIndex}";

                if (aoItem.BlockIndex == 0)
                {

                    if (index >= 92 && index <= 103)
                    {
                        data = BitConverter.GetBytes(value);
                    }
                    else if (index <= 117 || index >= 181 || index == 129 || index == 119 || index == 122 || index == 123 || index == 124 || index == 125)
                    {
                        data = BitConverter.GetBytes(value);
                    }
                    else if (index >= 118 && index <= 128)
                    {
                        data = BitConverter.GetBytes((uint)value);
                    }
                    else if (index >= 130 && index <= 180)
                    {
                        data = BitConverter.GetBytes((int)value);
                    }
                }
                else
                {
                    data = BitConverter.GetBytes(value);
                    
                }

                Array.Reverse(data);
                return _ads.BulkWriteByteRenderResult(address, data);
            }
        }

        public override bool SetValue(DOAccessor doItem, bool value)
        {
            if (!_trigConnected.M)
            {
                return false;
            }

            if (!IO.CanSetDO(doItem.Name, value, out var reason))
            {
                LOG.Write(reason);
                return false;
            }

            lock (_locker)
            {
                var ioVar = DoVariables[doItem.BlockIndex];

                ////获取单个DWORD数据，修改其中一位，然后整个Byte写下去
                var addr = doItem.Addr;
                var add = int.Parse(addr);
                var indexByte = add % 8;
                var index = add % 8 == 0 ? add / 8 : (add - add % 8) / 8;
                var val = 1 << indexByte;
                var output = ReadDOut(ioVar.Name, (ushort)ioVar.Length);
                if (output == null)
                {
                    LOG.Write("ReadDOut = null , in SetValue()");
                    return false;
                }

                output[index] = value
                    ? Convert.ToByte(Convert.ToUInt16(output[index]) | val)
                    : Convert.ToByte((Convert.ToUInt16(output[index]) & ~val));

                
                var stringSeparators = new string[] { "DBB" };
                var result = ioVar.Name.Split(stringSeparators, StringSplitOptions.None);
                int.TryParse(result[1], out var startIndex);
                var finalIndex = startIndex + index;
                var address = $"{result[0]}DBB{finalIndex}";
                var data = new byte[] { output[index] };

                return _ads.BulkWriteByteRenderResult(address, data);
            }
        }
    }
}
