using Aitex.Core.RT.Event;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.Common.Communications;
using MECF.Framework.Common.Event;
using MECF.Framework.Common.PLC;
using MECF.Framework.RT.Core.IoProviders.Siemens;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.OffsetManage;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;

namespace Aitex.Core.RT.Device.Devices
{
    public class TcAds : BaseDevice, IConnectable, IConnectionContext, IDevice, IConnection, IAdsPlc
    {
        #region IConnectionContext

        public int RetryConnectIntervalMs => 1000;

        //这个属性是底层库重连次数，超过重连次数，以后不在重新连接
        public int MaxRetryConnectCount => 5;
        public bool EnableCheckConnection => true;
        public virtual string Address { get; }
        public bool IsAscii { get; }
        public string NewLine { get; }
        public bool EnableLog { get; }

        private int MaxFailCount = 3;

        private int readPLCFailCount;

        private R_TRIG trig_readPLCFailCount = new R_TRIG();

        private int writePLCFailCount;

        private R_TRIG trig_writePLCFailCount = new R_TRIG();

        private R_TRIG trig_Reconnection = new R_TRIG();

        private PeriodicJob _thread;

        #endregion IConnectionContext

        #region IConnectable

        public event Action<string> OnCommunicationError;

        public event Action<string> OnAsciiDataReceived;

        public event Action<byte[]> OnBinaryDataReceived;

        #endregion IConnectable

        /// <summary>
        ///  软件正常关闭信号，  _doShutDown  DO置位true，软件打开连接PLC时，置位false,
        /// </summary>
        protected readonly DOAccessor _doShutDown;

        public event Action OnDisconnected;

        bool IAdsPlc.CheckIsConnected()
        {
            return Connection.IsConnected;
        }

        bool IConnection.IsConnected
        {
            get
            {
                return Connection.IsConnected;
            }
        }

        bool IConnection.Connect()
        {
            Connect(out _);
            return true;
        }

        bool IConnection.Disconnect()
        {
            Disconnect(out _);
            return true;
        }

        public FsmConnection Connection { get; set; } = new FsmConnection();

        //public bool IsCommunicationError
        //{
        //    get
        //    {
        //        return _isCommunicationError;
        //    }
        //}

        //private bool _isCommunicationError;
        private OffsetManager offset;

        private OperateResult connect;
        public SiemensS7Net siemensTcpNet = null;
        private SiemensPLCS siemensPLCSelected = SiemensPLCS.S1500;
        private string _plcType;

        private Dictionary<string, int> _nameHandleMap = new Dictionary<string, int>();

        private bool _isTargetConnected;

        public AlarmEventItem AlarmConnectFailed { get; set; }
        public AlarmEventItem AlarmCommunicationError { get; set; }

        public event Action OnConnected;

        public TcAds(string module, XmlElement node, string ioModule = "")
        {
            var attrModule = node.GetAttribute("module");
            Module = string.IsNullOrEmpty(attrModule) ? module : attrModule;

            Name = node.GetAttribute("id");

            Address = SC.GetStringValue($"PM.{Module}.SiemensIP");
            _plcType = SC.GetStringValue($"PM.{Module}.SiemensType");

            _doShutDown = ParseDoNode("doShutDown", node, Module);
        }

        public bool Initialize()
        {
            if (!SC.GetValue<bool>($"System.SetUp.Is{Module}Installed"))
                return true;

            offset = new OffsetManager(Module);
            AlarmConnectFailed = SubscribeAlarm($"{Module}.{Name}.ConnectionError", $"Can not connect with {Address}", null);
            AlarmCommunicationError = SubscribeAlarm($"{Module}.{Name}.CommunicationError", $"Can not Communication {Address}", null);

            Connection = new FsmConnection();
            Connection.Initialize(100, this, this);

            Connection.OnConnected += Connection_OnConnected;
            Connection.OnDisconnected += Connection_OnDisconnected;
            Connection.OnError += Connection_OnError;

            ConnectionManager.Instance.Subscribe($"{Module}.{Name}", this);

            _thread = new PeriodicJob(2000, OnTimer, "PLC Reconnection Thread", false);

            OP.Subscribe($"{Module}.ShutDownPLC", (function, args) =>
            {
                LOG.Write($"{Module}.Shut down plc true");
                _doShutDown?.SetValue(true, out _);
                return true;
            });
            return true;
        }

        bool IConnectable.CheckIsConnected()
        {
            return siemensTcpNet != null && siemensTcpNet.IsConnected;
        }

        public bool Connect(out string reason)
        {
            var ipAddr = ParseAdsIPAddr(Address);
            siemensPLCSelected = (SiemensPLCS)Enum.Parse(typeof(SiemensPLCS), _plcType);
            siemensTcpNet = new SiemensS7Net(siemensPLCSelected);
            siemensTcpNet.IpAddress = ipAddr.Item1;// _ipAddress;
            siemensTcpNet.Port = ipAddr.Item2;   // int.Parse(_port);
            siemensTcpNet.ConnectTimeOut = 1;
            siemensTcpNet.ReceiveTimeOut = 1000;

            try
            {
                if (siemensPLCSelected != SiemensPLCS.S200Smart &&
                    siemensPLCSelected != SiemensPLCS.S200)
                {
                    siemensTcpNet.Rack = 0;
                    siemensTcpNet.Slot = 0;

                    siemensTcpNet.ConnectionType = 1;
                    siemensTcpNet.LocalTSAP = 258;
                }

                connect = siemensTcpNet.ConnectServer();
                if (!siemensTcpNet.IsConnected)
                {
                    //_isCommunicationError = true;
                    //IsConnect = true;
                    //EV.PostInfoLog(Module, "PLC Connection Successful！");
                }
                else
                {
                    //_isCommunicationError = false;
                }

                //return connect.IsSuccess;
            }
            catch (Exception ex)
            {
                _isTargetConnected = false;
                //_isCommunicationError = true;
                reason = ex.Message;
                return false;
            }

            _isTargetConnected = true;
            reason = string.Empty;
            return true;
        }

        public bool Disconnect(out string reason)
        {
            reason = string.Empty;
            siemensTcpNet.ConnectClose();
            siemensTcpNet = null;
            connect = null;
            return false;
        }

        private void Connection_OnError(string obj)
        {
            AlarmConnectFailed.Set();
            _thread.Start();
        }

        private void Connection_OnDisconnected()
        {
            EV.PostInfoLog(Module, $"Disconnect from PLC {Address}");
            _thread.Start();
            ResetAlarm();
        }

        private void Connection_OnConnected()
        {
            EV.PostInfoLog(Module, $"Connected with PLC {Address}");
            EV.PostInfoLog(Module, $"Delay 5s can do offset");
            Task.Delay(5000).ContinueWith(x =>
            {
                offset.Set();
                LOG.Write($"{Module}.Shut down plc false");
                _doShutDown?.SetValue(false, out _);
            });
            
            ResetAlarm();
        }

        protected override void HandleMonitor()
        {
            //Reconnection();
        }

        public bool OnTimer()
        {
            Reconnection();
            return true;
        }

        /// <summary>
        /// 事件触发线程启动，PLC断线延时2秒重连，连接后才会退出线程
        /// </summary>
        protected void Reconnection()
        {
            try
            {
                if (siemensTcpNet == null || siemensTcpNet.IsConnected)//连接被主动切断，或者已连接直接退出
                    return;

                trig_Reconnection.CLK = true;
                if (trig_Reconnection.Q)//首次进去提示用户
                    EV.PostWarningLog(Module, "Disconnect from PLC,Stat reconnection");

                connect = siemensTcpNet.ConnectServer();
                if (siemensTcpNet.IsConnected)
                {
                    trig_Reconnection.RST = true;
                    Connection.InvokeReset();
                    EV.PostInfoLog(Module, "PLC reconnection successful！");
                    _thread.Stop();
                }
            }
            catch
            { }
            
        }

        public void Terminate()
        {

            Task.Delay(1000).ContinueWith(x => Connection.Terminate());
        }

        private Tuple<string, int> ParseAdsIPAddr(string addr)
        {
            if (addr == null)
                throw (new Exception("PM.SiemensIP is not exist in sc.data!"));

            Regex reg = new Regex(@"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?):(\d{1,5})$");
            if (!reg.IsMatch(addr))
                throw (new Exception($@"{Module}.SiemensIP '{addr}' is invalid!"));

            var item = addr.Split(':');
            return Tuple.Create(item[0], int.Parse(item[1]));
        }

        public void Reset()
        {
            ResetAlarm();
            trig_Reconnection.RST = true;//复位重连trig信号

            //复位读写异常trig信号
            trig_readPLCFailCount.RST = true;
            trig_writePLCFailCount.CLK = true;
            Connection.InvokeReset();
        }

        public bool Read(string variableName, out object data, string type, int length, out string reason)
        {
            data = null;
            reason = string.Empty;
            return true;
        }

        public bool WriteArrayElement(string variableName, int index, object data, out string reason)
        {
            reason = "";
            return true;
        }

        public bool SendBinaryData(byte[] data)
        {
            return true;
        }

        public bool SendAsciiData(string data)
        {
            return true;
        }

        public bool BulkReadRenderResult(string Adrress, ushort length, out byte[] data)
        {
            try
            {
                if (siemensTcpNet == null || !siemensTcpNet.IsConnected)
                {
                    data = null;
                    return false;
                }

                OperateResult<byte[]> read = siemensTcpNet.Read(Adrress, length);// ushort.Parse(lengthTextBox.Text));
                if (read.IsSuccess)
                {
                    data = read.Content;

                    //复位读错误次数
                    trig_readPLCFailCount.RST = true;
                    readPLCFailCount = 0;
                    return true;
                }
                else
                {
                    //if (!_isCommunicationError)
                    LOG.Write($"PLC BulkReadRenderResult Communication error with Siemens PLC , {read.Message}");
                    //_isCommunicationError = true;
                    data = null;

                    //新增读错误次数判断
                    if (readPLCFailCount < MaxFailCount)
                    {
                        readPLCFailCount++;
                    }
                    else
                    {
                        trig_readPLCFailCount.CLK = true;
                        if (trig_readPLCFailCount.Q)
                        {
                            EV.PostWarningLog(Module, $"PLC BulkReadRenderResult error with Siemens PLC , {read.Message}");
                        }
                    }

                    return false;
                }
            }
            catch (Exception ex)
            {
                // EV.PostAlarmLog("", "Read Failed：" + ex.Message);
            }

            data = null;
            return false;
        }

        public bool BulkWriteByteRenderResult(string Adrress, byte[] data)
        {
            try
            {
                if (siemensTcpNet == null || !siemensTcpNet.IsConnected)
                {
                    data = null;
                    return false;
                }
                OperateResult write = siemensTcpNet.Write(Adrress, data);
                if (!write.IsSuccess)
                {
                    //if (!_isCommunicationError)
                    //EV.PostInfoLog("PLC", $"Communication error with Siemens PLC , {write.Message}");
                    LOG.Write($"PLC BulkWriteByteRenderResult Communication error with Siemens PLC , {write.Message}");
                    //_isCommunicationError = true;

                    //新增写错误次数判断
                    if (writePLCFailCount < MaxFailCount)
                    {
                        writePLCFailCount++;
                    }
                    else
                    {
                        trig_writePLCFailCount.CLK = true;
                        if (trig_writePLCFailCount.Q)
                        {
                            EV.PostWarningLog(Module, $"PLC BulkWriteByteRenderResult error with Siemens PLC , {write.Message}");
                        }
                    }
                    return false;
                }
                //复位写错误次数
                trig_writePLCFailCount.RST = true;
                writePLCFailCount = 0;
                
                // 638712423114877878

                return write.IsSuccess;
            }
            catch (Exception ex)
            {
                // EV.PostAlarmLog("", "Read Failed：" + ex.Message);
            }
            return false;
        }
    }
}