using Aitex.Core.RT.Log;
using MECF.Framework.Common.Beckhoff.IOAxis;
using MECF.Framework.Common.IOCore;
using MECF.Framework.Common.Utilities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TwinCAT.Ads;
namespace MECF.Framework.Common.TwinCat
{
    public class TwincatCoeInterface
    {
        #region 常量
        private const int INDEXGROUP_SDO_UPLOAD_DOWNLOAD = 0x0000f302;
        #endregion
        #region 内部变量
        /// 
        /// ads COE对象
        /// 
        private TcAdsClient _adsClientCOE;
        /// 
        /// Net Id
        /// 
        private string _netId;
        /// 
        /// 端口号
        /// 
        private int _port;
        /// 
        /// 轴对象
        /// 
        private BeckhoffAxis _axis;
        /// 
        /// 连接输入变量(连接后读取变量)
        /// 
        private BeckhoffAxisInput _connectInputVariable;
        /// 
        /// 连接状态
        /// 
        private bool _isConnected = false;
        /// 
        /// coe输入变量(key-名称,value-输入变量对象)
        /// 
        private Dictionary _nameCoeInputDic = new Dictionary();
        /// 
        /// COE名称变量字典(key-名称,value-输出变量对象)
        /// 
        private Dictionary _nameCoeOutputDic = new Dictionary();
        /// 
        /// 连接是否完成
        /// 
        private bool _isConnectComplete = false;
        /// 
        /// 启动读取线程
        /// 
        private Thread _startReadThread = null;
        #endregion
        /// 
        /// 模拟
        /// 
        /// 
        /// 
        /// 
        public TwincatCoeInterface(BeckhoffAxis beckhoffAxis) 
        {
            _axis = beckhoffAxis;
            _netId = beckhoffAxis.COEAddress;
            _port = beckhoffAxis.COEPort;
            AnalyseConnectInputVariable();
            _adsClientCOE = new TcAdsClient();
            _adsClientCOE.ConnectionStateChanged += AdsClientCOE_ConnectionStateChanged;
        }
        /// 
        /// 解析连接变量
        /// 
        private void AnalyseConnectInputVariable()
        {
            foreach(BeckhoffAxisInput item in _axis.Inputs)
            {
                if(item.Address.StartsWith("0x"))
                {
                    _connectInputVariable = item;
                    break;
                }
            }
        }
        /// 
        /// 初始化输入输出变量
        /// 
        public void InnitialInputAndOutputVariable(BeckhoffAxis beckhoffAxis)
        {
            foreach (BeckhoffAxisInput item in beckhoffAxis.Inputs)
            {
                if (item.Address.StartsWith("0x"))
                {
                    _nameCoeInputDic[$"{beckhoffAxis.Name}.{item.Type}"] = item;
                }
            }
            foreach(BeckhoffAxisOutput item in beckhoffAxis.Outputs)
            {
                if (item.Address.StartsWith("0x"))
                {
                    _nameCoeOutputDic[$"{beckhoffAxis.Name}.{item.Type}"] = item;
                }
            }
        }
        /// 
        /// 开启读取变量线程
        /// 
        public void StartReadInputDataThread()
        {
            if (_startReadThread == null)
            {
                _startReadThread = new Thread(new ThreadStart(StartReadInputData));
                _startReadThread.IsBackground = true;
                _startReadThread.Start();
            }
        }
        /// 
        /// 读取变量
        /// 
        private void StartReadInputData()
        {
            DateTime dt = DateTime.Now;
            while(true)
            {
                if(DateTime.Now.Subtract(dt).TotalSeconds>=5)
                {
                    break;
                }
                if(!_isConnectComplete)
                {
                    continue;
                }
                if(!_isConnected)
                {
                    continue;
                }
                if (_nameCoeInputDic.Count != 0)
                {
                    ReadAllCoeInputs();
                    break;
                }
                else
                {
                    Thread.Sleep(500);
                }
            }
            
        }
        /// 
        /// 读取所有COE变量
        /// 
        private void ReadAllCoeInputs()
        {
            if(_nameCoeInputDic.Keys==null|| _nameCoeInputDic.Count==0)
            {
                return;
            }
            List keys = _nameCoeInputDic.Keys.ToList();
            foreach (string key in keys)
            {
                BeckhoffAxisInput item = _nameCoeInputDic[key];
                Type type = DataTypeUtil.GetSystemDataTypeByBeckhoffDataType(item.DataType);
                var sdoResult = AnalyseSdoIndex(item.Address);
                if (sdoResult.Item1)
                {
                    object value = ReadCOEData(sdoResult.Item2, sdoResult.Item3, type);
                    if (value != null)
                    {
                        IOModuleManager.Instance.UpdateIoValue(key, value);
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.ERR_TWINCAT, "System", $"read {key} error");
                    }
                }
                Thread.Sleep(10);
            }
        }
        /// 
        /// 连接状态变化
        /// 
        /// 
        /// 
        private void AdsClientCOE_ConnectionStateChanged(object sender, TwinCAT.ConnectionStateChangedEventArgs e)
        {
            if (e.NewState == TwinCAT.ConnectionState.Connected)
            {
                try
                {
                    if (_connectInputVariable != null)
                    {
                        var inputResult = AnalyseSdoIndex(_connectInputVariable.Address);
                        if (inputResult.Item1)
                        {
                            object obj= ReadFirstCOEData(inputResult.Item2, inputResult.Item3, DataTypeUtil.GetSystemDataTypeByBeckhoffDataType(_connectInputVariable.DataType));
                            if (obj != null)
                            {
                                _isConnected = true;
                                _isConnectComplete = true;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    _isConnectComplete = true;
                    _isConnected = false;
                    LOG.WriteLog(eEvent.ERR_TWINCAT, "System", $"Twincat connect{_netId}:{_port} error");
                    return;
                }
            }
            else
            {
                _isConnected = false;
                LOG.WriteLog(eEvent.ERR_TWINCAT,"System", $"Twincat connect{_netId}:{_port} error");
            }
        }
        /// 
        /// 连接
        /// 
        /// 
        public void Connect()
        {
            try
            {                
                _adsClientCOE.Connect(_netId, _port);
            }
            catch(Exception ex)
            {
                LOG.WriteLog(eEvent.ERR_TWINCAT, "System", ex.Message);
            }
        }
        #region 写数据
        /// 
        /// 写数据
        /// 
        /// 
        /// 
        /// 
        /// 
        public bool WriteModuleCoeData(string moduleName,string variableName,object value)
        {
            if(_isConnected)
            {
                string str = $"{moduleName}.{variableName}";
                if(_nameCoeOutputDic.ContainsKey(str))
                {
                    BeckhoffAxisOutput output = _nameCoeOutputDic[str];
                    try
                    {
                        var sdoResult = AnalyseSdoIndex(output.Address);
                        if(sdoResult.Item1)
                        {
                            bool result=WriteCOEData(sdoResult.Item2, sdoResult.Item3, value);
                            if (result&&_nameCoeInputDic.ContainsKey(str))
                            {
                                object rdValue = ReadCOEData(sdoResult.Item2, sdoResult.Item3, DataTypeUtil.GetSystemDataTypeByBeckhoffDataType(output.DataType));
                                if (rdValue != null)
                                {
                                    IOModuleManager.Instance.UpdateIoValue(str, rdValue);
                                }
                            }
                            return true;
                        }
                        else 
                        {
                            return false;
                        }
                    }
                    catch(Exception ex)
                    {
                        LOG.WriteLog(eEvent.ERR_TWINCAT, "System", $"twincat cannot write COE data, message is [{ex.Message}]");
                        return false;
                    }
                }
                else
                {
                    LOG.WriteLog(eEvent.ERR_TWINCAT, "System", $"twincat doesnot have [{moduleName}.{variableName}] variable ,cannot write COE data");
                    return false;
                }
            }
            else
            {
                LOG.WriteLog(eEvent.ERR_TWINCAT, "System", "twincat connect failed,cannot write COE data");
                return false;
            }
        }
        /// 
        /// 解析Sdo索引
        /// 
        /// 
        /// 
        private (bool,int,int) AnalyseSdoIndex(string address)
        {
            string[] strAry = address.Split(':');
            if (strAry.Length != 2)
            {
                LOG.WriteLog(eEvent.ERR_TWINCAT, "System", $"twincat invalid address is {address}");
                return (false,0,0);
            }
            else
            {
                
                if (int.TryParse(strAry[0].Remove(0,2), System.Globalization.NumberStyles.HexNumber, System.Globalization.NumberFormatInfo.InvariantInfo, out int sdoIndex) && 
                    int.TryParse(strAry[1], out int sdoSubIndex))
                {
                    return (true,sdoIndex,sdoSubIndex);
                }
                else
                {
                    LOG.WriteLog(eEvent.ERR_TWINCAT, "System", $"twincat invalid address is {address}");
                    return (false,0,0);
                }
            }
        }
        /// 
        /// 写COE Byte数据
        /// 
        /// 
        /// 
        /// 
        private bool WriteCOEData(int sdoIndex,int sdoSubIndex,object value)
        {
            if (_isConnected)
            {
                int indexOffset = (int)sdoIndex * 65536 + sdoSubIndex;
                try
                {
                    _adsClientCOE.WriteAny(INDEXGROUP_SDO_UPLOAD_DOWNLOAD, indexOffset, value);
                    return true;
                }
                catch (Exception ex)
                {
                    LOG.WriteLog(eEvent.ERR_TWINCAT, "System", $"twincat write COE data failed {ex.Message}.IndexOffset:{sdoIndex}:{sdoSubIndex}");
                    return false;
                }
            }
            else
            {
                LOG.WriteLog(eEvent.ERR_TWINCAT, "System", "twincat connect failed,cannot write COE data");
                return false;
            }
        }
        #endregion
        #region 读数据
        /// 
        /// 读取COE Byte数据
        /// 
        /// 
        /// 
        /// 
        public object ReadCOEData(int sdoIndex,int sdoSubIndex,Type type) 
        {
            if (_isConnected)
            {
                int indexOffset = (int)sdoIndex * 65536 + sdoSubIndex;
                try
                {
                    return _adsClientCOE.ReadAny(INDEXGROUP_SDO_UPLOAD_DOWNLOAD, indexOffset, type);
                }
                catch(Exception ex)
                {
                    LOG.WriteLog(eEvent.ERR_TWINCAT, "System", $"twincat read COE data failed {ex.Message}.IndexOffset:{sdoIndex}:{sdoSubIndex}");
                    return null;
                }
            }
            else
            {
                LOG.WriteLog(eEvent.ERR_TWINCAT, "System", "twincat connect failed,cannot read COE data");
                return null;
            }
        }
        /// 
        /// 读取第一个COE数据
        /// 
        /// 
        /// 
        /// 
        /// 
        private object ReadFirstCOEData(int sdoIndex, int sdoSubIndex, Type type)
        {
            int indexOffset = (int)sdoIndex * 65536 + sdoSubIndex;
            try
            {
                return _adsClientCOE.ReadAny(INDEXGROUP_SDO_UPLOAD_DOWNLOAD, indexOffset, type);
            }
            catch (Exception ex)
            {
                LOG.WriteLog(eEvent.ERR_TWINCAT, "System", $"twincat read COE data failed {ex.Message}.IndexOffset:{sdoIndex}:{sdoSubIndex}");
                return null;
            }
        }
        #endregion
    }
}