using ProjectManagementSystem.Common.Config;
using ProjectManagementSystem.Common.Log;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ProjectManagementSystem.Common.Core
{
    public class PlcControl
    {
        public event Action<List<PlcData>> OnConnectionChanged;

        private static PlcControl m_instance;

        private bool[] m_plcConnectedArray;
        private bool[] m_plcIsNeedClear;

        private Dictionary<string, HslCommunication.Core.IReadWriteNet> m_plcDict = new Dictionary<string, HslCommunication.Core.IReadWriteNet>();
        private Dictionary<string, PlcData> m_plcDataDict = new Dictionary<string, PlcData>();

        public static PlcControl Instance
        {
            get
            {
                if (m_instance == null)
                {
                    m_instance = new PlcControl();
                    if (!m_instance.Initialize())
                    {
                        m_instance = null;
                    }
                }
                return m_instance;
            }
        }

        public Dictionary<string, PlcData> PlcDataDict { get { return m_plcDataDict; } }

        public void SetPlcNeedClear()
        {
            for (int i = 0; i < m_plcIsNeedClear.Length; i++)
            {
                m_plcIsNeedClear[i] = true;
            }
        }

        public HslCommunication.Core.IReadWriteNet GetPlcReadWrite(string plcIpAddress)
        {
            if (m_plcDict.ContainsKey(plcIpAddress))
            {
                return m_plcDict[plcIpAddress];
            }
            return null;
        }

        private bool Initialize()
        {
            var dataList = ExcelConfig.Instance.RouteConfigList;

            m_plcConnectedArray = new bool[dataList.Count];
            m_plcIsNeedClear = new bool[dataList.Count];

            for (int i = 0; i < dataList.Count; i++)
            {
                var data = dataList[i];
                string plcIpAddr = data.PlcIpAddr;

                if (!string.IsNullOrEmpty(plcIpAddr)
                    && !m_plcDict.ContainsKey(plcIpAddr))
                {
                    HslCommunication.Profinet.Siemens.SiemensS7Net plc = new HslCommunication.Profinet.Siemens.SiemensS7Net(HslCommunication.Profinet.Siemens.SiemensPLCS.S1500, plcIpAddr);
                    plc.ConnectTimeOut = 1000;
                    plc.ReceiveTimeOut = 1000;
                    plc.SetPersistentConnection();

                    m_plcDict.Add(plcIpAddr, plc);
                    m_plcDataDict.Add(plcIpAddr, new PlcData() { IpAddress = plcIpAddr });
                }
            }

            Task.Factory.StartNew(UserThread);
            return true;
        }

        public void Clear()
        {
            for (int i = 0; i < ExcelConfig.Instance.RouteConfigList.Count; i++)
            {
                Clear(i);
            }
        }

        private void Clear(int index)
        {
            var routeConfig = ExcelConfig.Instance.RouteConfigList[index];
            var plcBehaviorList = ExcelConfig.Instance.PlcBehaviorList.Where(d => d.Remark.Contains("初始化")).ToList();
            if (plcBehaviorList == null
                || plcBehaviorList.Count == 0)
            {
                string strLog = $"{routeConfig.LocationCode} IP地址：{routeConfig.PlcIpAddr}，无需清零地址";
                CLog.Instance.GetLog("System").WriteInfo(strLog);
                return;
            }

            foreach (var item in plcBehaviorList)
            {
                foreach (string addrDesc in item.DbAddressWrite)
                {
                    Clear(routeConfig, addrDesc);
                }
            }
        }

        private void Clear(RouteConfig routeConfig, string addrDesc)
        {
            string addr = GetPlcTaskAddr(routeConfig.LocationCode, addrDesc);
            if (string.IsNullOrEmpty(addr)) return;

            HslCommunication.Core.IReadWriteNet plc = GetPlcReadWrite(routeConfig.PlcIpAddr);
            if (plc == null)
            {
                return;
            }

            var result = plc.Write(addr, false);
            string strLog = $"{routeConfig.LocationCode}，{routeConfig.PlcIpAddr}，清零：{addr} ({addrDesc})，结果：{result.IsSuccess}";
            CLog.Instance.GetLog("System").WriteInfo(strLog);
        }

        public string GetPlcTaskAddr(string location, string addrDesc)
        {
            if(!System.Text.RegularExpressions.Regex.IsMatch(addrDesc, @"[\u4e00-\u9fa5]"))
            {
                return addrDesc;
            }

            return ExcelConfig.Instance.GetLocationMemberCache(location, addrDesc);
        }

        private void SetConnected(int index, bool isConnected)
        {
            if (index < m_plcConnectedArray.Length)
            {
                if (m_plcConnectedArray[index] != isConnected)
                {
                    var routeConfig = ExcelConfig.Instance.RouteConfigList[index];

                    if (m_plcDataDict[routeConfig.PlcIpAddr].Online != isConnected)
                    {
                        m_plcDataDict[routeConfig.PlcIpAddr].Online = isConnected;
                        CLog.Instance.GetLog("System").WriteInfo($"{routeConfig.PlcIpAddr} 连接：{isConnected}");

                        if (OnConnectionChanged != null)
                        {
                            OnConnectionChanged(m_plcDataDict.Values.ToList());
                        }
                    }
                }
                m_plcConnectedArray[index] = isConnected;
            }
        }

        private void PlcTaskProc()
        {
            var RouteConfigList = ExcelConfig.Instance.RouteConfigList;
            for (int i = 0; i < RouteConfigList.Count; i++)
            {
                ReadTaskAddr(i);
            }

            for (int i = 0; i < m_plcIsNeedClear.Length; i++)
            {
                if (m_plcIsNeedClear[i]
                    && m_plcConnectedArray[i])
                {
                    m_plcIsNeedClear[i] = false;
                    Clear(i);
                }
            }
        }

        private void ReadTaskAddr(int i)
        {
            var routeConfig = ExcelConfig.Instance.RouteConfigList[i];
            if (string.IsNullOrEmpty(routeConfig.PlcHeartbearAddr))
            {
                return;
            }

            HslCommunication.Core.IReadWriteNet plc = GetPlcReadWrite(routeConfig.PlcIpAddr);
            if (plc == null)
            {
                return;
            }

            var tempResult = plc.ReadBool(routeConfig.PlcHeartbearAddr);
            SetConnected(i, tempResult.IsSuccess);

            if (tempResult.IsSuccess)
            {
                plc.Write(routeConfig.PlcHeartbearAddr_AGV, tempResult.Content);
            }
        }

        private void UserThread()
        {
            Thread.Sleep(2000);
            while (true)
            {
                try
                {
                    PlcTaskProc();
                }
                catch (Exception ex)
                {
                    CLog.Instance.GetLog("System").WriteException("Exception", ex);
                    Thread.Sleep(5000);
                }
                Thread.Sleep(1000);
            }
        }
    }

    public class PlcData
    {
        public string IpAddress { get; set; }
        public bool Online { get; set; }
    }
}

