using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Xml;
using Aitex.Core.RT.IOCore.Interlock.DataProvider;
using Aitex.Core.RT.IOCore.Interlock.Limits;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using MECF.Framework.Common.Equipment;
using MECF.Framework.RT.Core.IoProviders;

namespace Aitex.Core.RT.IOCore.Interlock;

public abstract class InterlockManagerBase<TAction>
    where TAction : IInterlockAction
{
    #region Variables

    private List<IIOAccessor> _diMap;
    private List<IIOAccessor> _doMap;
    private List<IIOAccessor> _aiMap;
    private List<IIOAccessor> _aoMap;

    protected string RootNodeName;
    
    protected readonly ConcurrentDictionary<(string actionName, bool doValue), TAction> _dictINTLKActionsFileLoader;

    /// <summary>
    /// 每个Limit对应的DoAction集合。
    /// </summary>
    protected readonly Dictionary<IInterlockLimit, List<IInterlockAction>> _dictLIMT2INTLKActionMap;
    
    /// <summary>
    /// 每个Module对应的DoAction集合。
    /// </summary>
    protected readonly Dictionary<ModuleName, List<TAction>> _dictINTLKActionsPerModule;
    
    /// <summary>
    /// 每个Module对应的Limit集合。
    /// </summary>
    protected readonly Dictionary<ModuleName, List<IInterlockLimit>> _dictLIMTPerModule;

    private PeriodicJob _monitorThread;
    private readonly R_TRIG _rTrigMonitorError;

    #endregion

    #region Constructors

    /// <summary>
    /// 互锁管理器的构造函数。
    /// </summary>
    protected InterlockManagerBase()
    {
        _dictINTLKActionsFileLoader = new();
        _dictINTLKActionsPerModule = new();
        _dictLIMT2INTLKActionMap = new ();
        _dictLIMTPerModule = new ();

        _rTrigMonitorError = new();
    }

    #endregion

    #region Methods

    /// <summary>
    /// 初始化互锁管理器。
    /// </summary>
    /// <param name="configFile">互锁配置文件。</param>
    /// <param name="ioManager">点表。</param>
    /// <returns></returns>
    public virtual void Initialize(string configFile, IIoBuffer ioManager)
    {
        Debug.Assert(!string.IsNullOrEmpty(configFile));
        Debug.Assert(ioManager != null);

        if (string.IsNullOrEmpty(configFile))
        {
            LOG.Error("Interlock config file does not specified.");
            return;
        }

        _doMap = ioManager.DOMap.Values.Cast<IIOAccessor>().ToList();
        _diMap = ioManager.DIMap.Values.Cast<IIOAccessor>().ToList();
        _aiMap = ioManager.AIMap.Values.Cast<IIOAccessor>().ToList();
        _aoMap = ioManager.AOMap.Values.Cast<IIOAccessor>().ToList();

        var sbReason = new StringBuilder();
        try
        {
            var doc = new XmlDocument();
            doc.Load(configFile);
            var xmlNode = doc.SelectSingleNode(RootNodeName);

            if (xmlNode == null) // 如果Interlock节点不存在
            {
                var err =
                    $"Failed to load interlock config file, the node '{RootNodeName}' is not found in {configFile}.";
                sbReason.AppendLine(err);
                LOG.Error(err);
                return;
            }

            foreach (XmlNode childNode in xmlNode.ChildNodes)
            {
                // 遍历'Action'节点

                if (!CheckIsXmlElement(childNode, out var actionNode))
                    continue;

                if (actionNode.Name != "Action")
                {
                    if (actionNode.NodeType != XmlNodeType.Comment)
                        LOG.Write("interlock daemon file contains no comments content, " + actionNode.InnerXml);

                    continue;
                }

                // Action节点仅支持对DO进行配置
                if (!actionNode.HasAttribute("do") || !actionNode.HasAttribute("value"))
                {
                    sbReason.AppendLine("action node has no [do] or [value] attribute");
                    continue;
                }

                var doName = actionNode.GetAttribute("do");
                var doValue = Convert.ToBoolean(actionNode.GetAttribute("value"));
                var tip = string.Empty;
                var dicTips = new Dictionary<string, string>();
                if (!ioManager.DOMap.ContainsKey(doName))
                {
                    sbReason.AppendLine("action node " + doName + " no such DO defined");
                    continue;
                }

                // 获取DO实例
                var targetDo = ioManager.DOMap[doName];
                if (targetDo == null)
                {
                    // 如果DO不存在，则读取下一个Action节点
                    sbReason.AppendLine("action node " + doName + " no such DO defined");
                    continue;
                }

                if (actionNode.HasAttribute("tip"))
                    tip = actionNode.GetAttribute("tip");

                if (actionNode.HasAttribute("tip.zh-CN"))
                    dicTips["zh-CN"] = actionNode.GetAttribute("tip.zh-CN");

                if (actionNode.HasAttribute("tip.en-US"))
                    dicTips["en-US"] = actionNode.GetAttribute("tip.en-US");

                var ignoreReverseAll = false; //单个Action的全局ignore，为True时所有Limit条件不反向控制
                if (actionNode.HasAttribute("ignoreReverse"))
                {
                    var strIgnoreReverse = actionNode.GetAttribute("ignoreReverse");
                    if (!bool.TryParse(strIgnoreReverse, out ignoreReverseAll))
                        LOG.Error($"Unable to convert attribute 'ignoreReverse' of action '{doName}' to bool.");
                }

                var moduleName = GetModuleFromIo(targetDo.Name);
                if (!_dictINTLKActionsPerModule.ContainsKey(moduleName))
                    _dictINTLKActionsPerModule[moduleName] = new List<TAction>();

                // 创建InterlockAction对象，首先判断该动作是否已经存在，若存在，则报错
                var action = (TAction)Activator.CreateInstance(typeof(TAction), moduleName.ToString(),
                    targetDo, doValue, tip, dicTips);

                if (_dictINTLKActionsFileLoader.ContainsKey((action.ActionName, doValue)))
                {
                    var err = $"Interlock Action {action.ActionName}={doValue} duplicated in {configFile}";
                    LOG.Error(err);
                    Debug.Assert(false, err);
                }


                // 遍历Action下的Limit节点，并创建InterlockLimit对象。
                foreach (XmlNode limitNode in actionNode.ChildNodes)
                {
                    if (!CheckIsXmlElement(limitNode, out var node))
                        continue;

                    if (node.Name.ToLower() == "or")
                    {
                        var groupOr = CreateOrLimitGroup(node, out var err0);
                        action.AddLogicOrGroup(groupOr);
                    }
                    else
                    {
                        // 获取InterlockLimit对象。
                        var limit = CreateInterlockLimit(node, out var err);
                        if (limit != null)
                            action.AddLimit(limit);
                        else
                            sbReason.AppendLine(err);
                    }
                }

                _dictINTLKActionsPerModule[moduleName].Add(action);

                if (_dictINTLKActionsFileLoader.ContainsKey((action.ActionName, doValue)))
                {
                    LOG.Error($"Duplicate interlock action definition: {action.ActionName}, {doValue}");
                    continue;
                }

                if (!_dictINTLKActionsFileLoader.TryAdd((action.ActionName, doValue), action))
                    LOG.Error($"Unable to add {action} to the ConcurrentDictionary");

                // 如果当前Action设置了忽略翻转，则不要注册Limit到当前Action的映射，避免Limit触发导致Action翻转。
                if (!ignoreReverseAll)
                {
                    foreach (var limit in action.Limits)
                    {
                        if (!limit.IgnoreReverse) //如果单个limit也忽略反转，不要注册Limit到当前Action的映射，避免Limit触发导致Action翻转。
                        {
                            // 创建以Limit分组的Action字典
                            if (!_dictLIMT2INTLKActionMap.ContainsKey(limit))
                                _dictLIMT2INTLKActionMap[limit] = new List<IInterlockAction>();

                            _dictLIMT2INTLKActionMap[limit].Add(action);
                        }
                    }
                }

            }

            Debug.Assert(!_dictINTLKActionsPerModule.ContainsKey(ModuleName.UnDefined),
                $"InterlockManager {nameof(_dictINTLKActionsPerModule)} contains key {ModuleName.UnDefined}");

            Debug.Assert(!_dictLIMTPerModule.ContainsKey(ModuleName.UnDefined),
                $"InterlockManager {nameof(_dictLIMTPerModule)} contains key {ModuleName.UnDefined}");
        }
        catch (Exception ex)
        {
            sbReason.AppendLine(ex.Message);
        }
        finally
        {
            _monitorThread ??= new PeriodicJob(200, OnTimer, $"{GetType().Name} Monitor Thread", isStartNow: true);
        }
    }

    private bool OnTimer()
    {
        try
        {
            Monitor();
            _rTrigMonitorError.CLK = false;
        }
        catch (Exception ex)
        {
            _rTrigMonitorError.CLK = true;
            if (_rTrigMonitorError.Q)
                LOG.Write(ex);
        }

        return true;
    }

    /// <summary>
    /// 背景扫描线程执行的任务。
    /// </summary>
    public abstract void Monitor();

    /// <summary>
    /// 通过XML配置创建互锁限制条件对象。
    /// </summary>
    /// <param name="limitNode">包含Limit定义的Xml节点。</param>
    /// <param name="reason">创建失败并返回null的原因。</param>
    /// <returns></returns>
    private IInterlockLimit CreateInterlockLimit(XmlElement limitNode, out string reason)
    {
        reason = "";

        // 检查节点名称是否为Limit
        if (limitNode.Name != "Limit") // 节点名称不是Limit
        {
            reason = "the name of xml node is not 'Limit'";
            if (limitNode.NodeType != XmlNodeType.Comment)
            {
                reason = "interlock config file contains no comments content, " + limitNode.InnerXml;
                LOG.Write(reason);
            }

            return null;
        }

        string limitValue;
        if (limitNode.HasAttribute("value"))
            limitValue = limitNode.GetAttribute("value");
        else
        {
            reason = "limit node lack of value attribute";
            return null;
        }

        var tip = string.Empty;
        var dicLimitTips = new Dictionary<string, string>();
        if (limitNode.HasAttribute("tip"))
            tip = limitNode.GetAttribute("tip");

        if (limitNode.HasAttribute("tip.zh-CN"))
        {
            dicLimitTips["zh-CN"] = limitNode.GetAttribute("tip.zh-CN");
            if (string.IsNullOrEmpty(tip))
                tip = dicLimitTips["zh-CN"];

        }

        if (limitNode.HasAttribute("tip.en-US"))
        {
            dicLimitTips["en-US"] = limitNode.GetAttribute("tip.en-US");
            if (string.IsNullOrEmpty(tip))
                tip = dicLimitTips["en-US"];
        }

        var ignoreReverse = false;
        if (limitNode.HasAttribute("ignoreReverse"))
        {
            var strIgnoreReverse = limitNode.GetAttribute("ignoreReverse");
            if (!bool.TryParse(strIgnoreReverse, out ignoreReverse))
                LOG.Error($"Unable to convert attribute 'ignoreReverse' of Limit to bool.");
        }

        // 节点不包含‘di’、‘do’、‘ai’、‘ao’，或者不包含‘value’属性
        IInterlockLimit limit;
        if (limitNode.HasAttribute("di"))
        {
            var limitName = limitNode.GetAttribute("di");
            var io = GetIoByIoType(IOType.DI, limitName);
            var provider = new DiValueProvider((DIAccessor)io);
            limit = new DiLimit(provider, limitValue, tip, dicLimitTips, ignoreReverse);
        }
        else if (limitNode.HasAttribute("do"))
        {
            var limitName = limitNode.GetAttribute("do");
            var io = GetIoByIoType(IOType.DO, limitName);
            var provider = new DoValueProvider((DOAccessor)io);
            limit = new DoLimit(provider, limitValue, tip, dicLimitTips, ignoreReverse);
        }
        else if (limitNode.HasAttribute("ai"))
        {
            var limitName = limitNode.GetAttribute("ai");
            var io = GetIoByIoType(IOType.AI, limitName);
            var provider = new AiValueProvider((AIAccessor)io);
            limit = new AiLimit(provider, limitValue, tip, dicLimitTips, ignoreReverse);
        }
        else if (limitNode.HasAttribute("ao"))
        {
            var limitName = limitNode.GetAttribute("ao");
            var io = GetIoByIoType(IOType.AO, limitName);
            var provider = new AoValueProvider((AOAccessor)io);
            limit = new AoLimit(provider, limitValue, tip, dicLimitTips, ignoreReverse);
        }
        else if (limitNode.HasAttribute("polldouble"))
        {
            var limitName = limitNode.GetAttribute("polldouble");
            var provider = new DoubleDataPollProvider(limitName);
            limit = new DoubleDataPollLimit(provider, limitValue, tip, dicLimitTips, ignoreReverse);
        }
        else if (limitNode.HasAttribute("pollbool"))
        {
            var limitName = limitNode.GetAttribute("pollbool");
            var provider = new BoolDataPollProvider(limitName);
            limit = new BoolDataPollLimit(provider, limitValue, tip, dicLimitTips, ignoreReverse);
        }
        else
        {
            reason = "limit node lack of di/do/ai/ao/io/pollbool/polldouble attribute";
            return null;
        }

        // 从_dictLIMTPerModule字典中查找当前创建的Limit是否已经存在；
        // 如果存在，直接返回已存在的Limit实例；
        // 否则，将当前创建的Limit放到字典中，然后返回实例；
        var moduleName = GetModuleFromIo(limit.Name);
        if (_dictLIMTPerModule.TryGetValue(moduleName, out var list))
        {
            var existLimit = list.FirstOrDefault(x => x.UniqueId == limit.UniqueId);
            if(existLimit != null)
                limit = existLimit; // 返回字典中的Limit实例
            else
                list.Add(limit); // 将当前Limit加入字典
        }
        else
        {
            // 在字典中创建Module，并将Limit实例放入字典
            _dictLIMTPerModule[moduleName] = new List<IInterlockLimit>(new[] { limit });
        }

        return limit;
    }

    /// <summary>
    /// 创建“OR”定义的Limit组。
    /// </summary>
    /// <param name="xmlNodeOr"></param>
    /// <param name="reason"></param>
    /// <returns></returns>
    private List<IInterlockLimit> CreateOrLimitGroup(XmlNode xmlNodeOr, out string reason)
    {
        reason = "";
        var limitList = new List<IInterlockLimit>();
        foreach (XmlNode childNode in xmlNodeOr.ChildNodes)
        {
            if (!CheckIsXmlElement(childNode, out var node))
                continue;

            var limit = CreateInterlockLimit(node, out reason);
            if (limit != null)
                limitList.Add(limit);
            else
                return null;
        }

        return limitList;
    }

    /// <summary>
    /// 根据给定的IO类型和IO名称，从IO列表中获取IO对象实例。
    /// </summary>
    /// <param name="type">IO类型，请参考<see cref="IOType"/>。</param>
    /// <param name="ioName">IO名称。</param>
    /// <returns></returns>
    /// <exception cref="InvalidIoTypeExeption">无效的IO类型。</exception>
    /// <exception cref="IoNotFoundException">未找到IO。</exception>
    private IIOAccessor GetIoByIoType(IOType type, string ioName)
    {
        List<IIOAccessor> dictMap;
        switch (type)
        {
            case IOType.DI:
                dictMap = _diMap;
                break;

            case IOType.DO:
                dictMap = _doMap;
                break;

            case IOType.AI:
                dictMap = _aiMap;
                break;

            case IOType.AO:
                dictMap = _aoMap;
                break;

            default:
                throw new InvalidIoTypeExeption();
        }

        var io = dictMap.FirstOrDefault(x => x.Name == ioName);
        if (io != null)
            return io;

        throw new IoNotFoundException(type, ioName);
    }

    /// <summary>
    /// 从指定的IO中解析该IO所属的模组。
    /// </summary>
    /// <param name="ioName"></param>
    /// <returns></returns>
    protected static ModuleName GetModuleFromIo(string ioName)
    {
        Debug.Assert(!string.IsNullOrEmpty(ioName), "the IO name can not be empty.");
        if (string.IsNullOrEmpty(ioName))
        {
            LOG.Error($"InterlockManager GetModuleFromIo failed, the parameter ioName is empty");
            return ModuleName.UnDefined;
        }

        // Simulator中TM的Io表配置中没有"TM."前缀；
        // 为兼容此问题，如果没有找到Module前缀，则用System代替
        var posDot = ioName.IndexOf('.');
        if (posDot <= 0)
            return ModuleName.System;

        var moduleName = ioName.Substring(0, posDot);
        return ModuleHelper.Converter(moduleName);
    }

    /// <summary>
    /// 检查制定的XmlNode是否为XmlElement.
    /// </summary>
    /// <param name="node"></param>
    /// <param name="element"></param>
    /// <returns></returns>
    private static bool CheckIsXmlElement(XmlNode node, out XmlElement element)
    {
        element = null;
        if (node.NodeType != XmlNodeType.Comment && node is XmlElement)
        {
            element = (XmlElement)node;
            return true;
        }

        return false;
    }

    #endregion
}