using IACommService4CSharp;
using PlcCom;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Drawing.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;
using static PlcComponent.PlcDataMonitor;

namespace PlcComponent
{
    //public delegate void DataTriggered(object sender, MonitorEventArgs e);
    public partial class PlcDataMonitor :Component, IMultiOutputComponent
    {
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IPlcComProtocol Protocol { get; set; }

        [Category("PLC")]
        [Description("添加数据集合地址入口")]
        [Browsable(false)]
        [Editor(typeof(NodeTypeEditor), typeof(UITypeEditor))]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Node NodeToAdd 
        { 
            get => nodeToAdd; 
            set 
            { 
                if (value != null && !readNodes.Contains(value)) 
                { 
                    nodeToAdd = value;
                    readNodes.Add(nodeToAdd);
                    ConfigReadNodes();
                }
            } 
        }
        private Node nodeToAdd;

        [Category("PLC")]
        [Description("数据集合，请不要调用Add或者Remove方法")]
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Editor(typeof(NodesCollectionEditor), typeof(UITypeEditor))]
        public List<Node> ReadNodes
        {
            get => readNodes;
            set
            {
                readNodes = value;
                ConfigReadNodes();
            }
        }

        private List<Node> readNodes;

        public Dictionary<string, object> NodeStringValuePairs;

        [Category("PLC")]
        [Description("数据更新周期ms")]
        [DefaultValue(100)]
        public int ReadInterval { get => readInterval; set{ readInterval = value < 10 ? 10 : value; } }
        private int readInterval=100;

        [Category("PLC")]
        [Description("是否启用")]
        [DefaultValue(true)]
        public bool BackgroundRefreshEnable { get; set; } = true;

        [Category("PLC")]
        [Description("数据监控集合")]
        [Editor(typeof(PlcDataMonitorEditor), typeof(UITypeEditor))]
        public List<MonitorRule> MonitorRules
        {
            get => monitorRules; 
            set
            {
                monitorRules=value;
                readNodes.Clear();

                List<MonitorRule> rulesNeedToRemove=new List<MonitorRule>();
                foreach (var item in monitorRules)
                {
                    //NodeToAdd = item.Node;
                    if (item.Node != null && !readNodes.Contains(item.Node))
                    {
                        readNodes.Add(item.Node);
                    }
                    else if(readNodes.Contains(item.Node))
                    {
                        rulesNeedToRemove.Add(item);//剔除重复的
                    }
                }
                if (rulesNeedToRemove.Count > 0)
                {
                    foreach (var item in rulesNeedToRemove)
                        monitorRules.Remove(item);
                }
                ConfigReadNodes();
            } 
        }
        private List<MonitorRule> monitorRules;

        [Category("PLC")]
        [Description("是否检测有和指定值相同")]
        [DefaultValue(true)]
        public bool CheckAnyEqualsTargetValue { get; set; } = true;

        public bool HaveDataEqualsTargetValue { get;private set; }


        public int RefreshCount;
        public TimeSpan RefreshTimeElapsed;


        // 新增：报警/事件专用事件
        [Description("寄存器的一些值变化时")]
        public event Action<List<(Node node, object value)>> OnMonitoredValuesChanged;

        [Description("监控的某个寄存器被触发时")]
        public event Action<object, List<MonitorEventArgs>> OnDataTriggered;
        [Description("监控的某个寄存器触发被清除时")]
        public event Action<object, List<MonitorEventArgs>> OnDataCleared;
        [Description("监控的所有寄存器都不满足触发条件时的事件")]
        public event Action<object> OnAllDataCleared;

        [Description("监控的所有寄存器的值都刷新过后")]
        [Browsable(true)]
        public event Action<object, List<MonitorRule>> OnAllDataRefreshed;

        [Description("有监控的值和目标值相同时")]
        [Browsable(true)]
        public event Action<object, List<MonitorRule>> OnDatasEqualTarget;

        /// <summary>
        /// Occurs when reading nodes from the PLC fails.
        /// </summary>
        /// <remarks>This event is triggered when an attempt to read nodes from the PLC does not succeed.
        /// Subscribers can use this event to handle errors or retry logic.</remarks>
        public event Action<IPlcBindableComponent, List<Node>> ReadNodesFailed;

        public class MonitorEventArgs : EventArgs
        {
            public MonitorRule Rule { get; set; }
            public object CurrentValue { get; set; }
            public DateTime Timestamp { get; set; }
            public string Message => Rule?.Message;
        }




        public PlcDataMonitor()
        {
            InitializeComponent();
            readNodes = new List<Node>();
            monitorRules = new List<MonitorRule>();
        }
        public PlcDataMonitor(IContainer container)
        {
            container.Add(this);

            InitializeComponent();
            readNodes = new List<Node>();
            monitorRules = new List<MonitorRule>();
        }

        private void ConfigReadNodes()
        {
            NodeStringValuePairs = new Dictionary<string, object>();
            foreach (var node in readNodes)
            {
                if (node != null)
                {
                    if (!string.IsNullOrEmpty(node.Value))
                    {
                        NodeStringValuePairs.Add(node.Value, null);
                    }
                }
            }
        }

        public List<Node> RemoveInvalidNodes()
        {
            var nodesToRemove = new List<Node>();

            List<string> strings = ReadNodes.Select(n => n.Value).ToList();
            try
            {
                var rt = Protocol.ReadValue(strings, out List<object> results);
                if (rt == 0)
                {
                    for (int i = 0; i < strings.Count; i++)
                    {
                        if (results[i] == null)
                        {
                            nodesToRemove.Add(ReadNodes[i]);
                        }
                    }
                }
            }
            catch (Exception)
            {
            }
            foreach (var item in nodesToRemove)
            {
                ReadNodes.Remove(item);
                //ReadOneNodeFailed.Invoke(c, item, 0);
            }
            if (nodesToRemove.Count > 0)
            {
                var itemsToRemove = new HashSet<Node>(nodesToRemove);
                MonitorRules = MonitorRules.Where(b => !itemsToRemove.Contains(b.Node)).ToList();
                ReadNodesFailed?.Invoke(this, nodesToRemove);
            }
            return nodesToRemove;
        }

        public event RegisterValueChangedHandler OnPLCRegisterValueChanged;
        public event ReadRegisterFailedHandler OnReadRegisterFailed;
        public event UpdateComponentStateFailedHandler OnUpdateComponentStateFailed;


        private bool isReadNodesCached = false; 
        private List<string> addresses=new List<string>();
        private List<string> CacheReadNodes()
        {
            if(!isReadNodesCached)
            {
                addresses = ReadNodes.Select(n => n.Value).ToList();
                isReadNodesCached = true;
            }
            return addresses;
        }

        public List<object> Read()
        {
            var sw=Stopwatch.StartNew();
            if (Protocol != null && ReadNodes != null)
            {
                CacheReadNodes();
                var rt = Protocol.ReadValue(addresses, out List<object> results);
                if (rt == 0)
                {
                    UpdateValues(ReadNodes, results);
                    RefreshTimeElapsed = sw.Elapsed;
                    return results;
                }
                else
                {
                    OnReadRegisterFailed?.Invoke(this, ReadNodes[0], rt);
                }
            }
            return null;
        }
        private Dictionary<Node, MonitorRule> _ruleDictCache;
        private bool _isCacheValid = false;

        private void EnsureRuleDict()
        {
            if (!_isCacheValid)
            {
                _ruleDictCache = MonitorRules.ToDictionary(r => r.Node, r => r);
                _isCacheValid = true;
            }
        }
        List<(Node, object)> changedMonitoredValues = new List<(Node, object)>();
        List<MonitorRule> dataEqualsTargetRules = new List<MonitorRule>();
        List<MonitorEventArgs> triggeredData = new List<MonitorEventArgs>();
        List<MonitorEventArgs> clearedData = new List<MonitorEventArgs>();
        public void UpdateValues(List<Node> nodes, List<object> values)
        {
            EnsureRuleDict(); //缓存字典
            
            bool haveDataEqualsTarget = false;
            changedMonitoredValues.Clear();
            dataEqualsTargetRules.Clear();
            triggeredData.Clear();
            clearedData.Clear();
            for (int i = 0; i < nodes.Count; i++)
            {
                // 1. 原有值变化通知
                if (NodeStringValuePairs.TryGetValue(nodes[i].Value, out var pair))
                {
                    if (!object.Equals(pair, values[i]))
                    {
                        NodeStringValuePairs[nodes[i].Value] = values[i];

                        OnPLCRegisterValueChanged?.Invoke(this, ReadNodes[i], values[i]);
                        changedMonitoredValues.Add((ReadNodes[i], values[i]));
                    }
                }
               
                // 2. 监控值的变化通知
                if (_ruleDictCache.TryGetValue(nodes[i], out var rule))
                {
                    rule.CurrentValue = values[i];

                    if (CheckAnyEqualsTargetValue && object.Equals(rule.TargetValue, values[i]))
                    {
                        haveDataEqualsTarget = true;
                        dataEqualsTargetRules.Add(rule);
                    }
                    var result = CheckAndTrigger(rule);
                    if (result== CompareResult.Rising)
                    {
                        triggeredData.Add(new MonitorEventArgs
                        {
                            Rule = rule,
                            CurrentValue = rule.CurrentValue,
                            Timestamp = DateTime.Now
                        });
                    }
                    else if (result== CompareResult.Falling)
                    {
                        clearedData.Add(new MonitorEventArgs
                        {
                            Rule = rule,
                            CurrentValue = rule.CurrentValue,
                            Timestamp = DateTime.Now
                        });
                    }
                }
            }
            if(changedMonitoredValues.Count>0)
            {
                OnMonitoredValuesChanged?.Invoke(changedMonitoredValues);
            }
            if(triggeredData.Count>0)
            {
                OnDataTriggered?.Invoke(this, triggeredData);
            }
            if (clearedData.Count > 0)
            {
                OnDataCleared?.Invoke(this, clearedData);
            }
            if (HaveDataEqualsTargetValue && !haveDataEqualsTarget)
            {
                OnAllDataCleared?.Invoke(this);
            }
            HaveDataEqualsTargetValue = haveDataEqualsTarget;
            if(haveDataEqualsTarget)
            {
                OnDatasEqualTarget?.Invoke(this, dataEqualsTargetRules);
            }
            OnAllDataRefreshed?.Invoke(this, MonitorRules);
            RefreshCount++;
        }
        private Dictionary<Node, (object value, DateTime timestamp)> lastStableValues = new Dictionary<Node, (object value, DateTime timestamp)>();

        private CompareResult CheckAndTrigger(MonitorRule rule)
        {
            int rt=0;
            
            object x=rule.CurrentValue,  y=null;

            CompareResult result;

            if (rule.CompareType == CompareType.ToTarget)
            {
                y = rule.TargetValue;
                bool e = object.Equals(x, y);
                if (rule.HaveComparedOnce)
                {
                    if (e)
                    {
                        if (rule.LastObjectEqual)
                            result = CompareResult.NoChange;
                        else
                            result = CompareResult.Rising;
                    }
                    else
                    {
                        if (rule.LastObjectEqual)
                            result = CompareResult.Falling;
                        else
                            result = CompareResult.NoChange;
                    }
                }
                else
                {
                    if (e)
                        result = CompareResult.Rising;
                    else
                        result = CompareResult.None;

                    rule.HaveComparedOnce = true;
                }
                rule.LastObjectEqual = e;
                return result;
            }
            else if (rule.CompareType == CompareType.ToPreviewValue)
            {
                // 获取上一次值
                if (lastStableValues.TryGetValue(rule.Node, out var lastValue))
                {
                    y = lastValue.value;
                }
                else if (rule.FirstRefreshTrigger)
                {
                    y = GetDefaultValue(rule.CurrentValue);
                }

                lastStableValues[rule.Node] = (x, DateTime.Now);

                if (y != null)
                {
                    rt = Comparer<object>.Default.Compare(x, y);

                    if (rt > 0)
                        result = CompareResult.Rising;
                    else if (rt == 0)
                        result = CompareResult.NoChange;
                    else
                        result = CompareResult.Falling;

                    return result;
                }
                else
                {
                    return CompareResult.None;
                }
            }
            else
                return CompareResult.None;
        }

        static object GetDefaultValue(object boxedValue)
        {
            if (boxedValue == null)
                throw new ArgumentNullException(nameof(boxedValue));

            Type t = boxedValue.GetType();
            if (!t.IsValueType)
                throw new ArgumentException("Only value types are supported.");

            return Activator.CreateInstance(t);
        }
    }
    public enum MonitorType
    {
        RisingEdge,
        FallingEdge,
        RisingOrFallingEdge,
        SameValue,
    }
    [Serializable]
    public class MonitorRule
    {
        private object targetValue = true;

        [Description("读取地址")]
        [Editor(typeof(NodeTypeEditor), typeof(UITypeEditor))]
        public Node Node { get; set; }

        [Description("监控变量类型")]
        [DefaultValue(MonitorType.RisingOrFallingEdge)]
        public MonitorType MonitorType { get; set; } = MonitorType.RisingOrFallingEdge;

        [Description("当前值")]
        public object CurrentValue { get; set; }

        [Description("数据比较方式")]
        [DefaultValue(CompareType.ToTarget)]
        public CompareType CompareType { get; set; }

        public bool HaveComparedOnce { get; set; }

        public bool LastObjectEqual {  get; set; }

        [Description("当CompareType=ToTarget 时所要对比的值")]
        [DefaultValue(true)]
        [TypeConverter(typeof(StringConverter))]
        public object TargetValue { get => targetValue; set { targetValue = value;if (targetValue is bool b) TargetIsBoolFalse = !b; } }
        [Description("第一次满足上升或者下降沿时是否触发事件")]
        [DefaultValue(true)]
        public bool FirstRefreshTrigger { get; set; } = true;  // 第一次满足条件是否触发

        [Description("触发事件的消息")]
        public string Message { get; set; }

        internal bool TargetIsBoolFalse { get; set; }
    }
    public enum CompareType
    {
        /// <summary>
        /// 和目标值比较
        /// </summary>
        ToTarget,
        /// <summary>
        /// 和前一次数据比较
        /// </summary>
        ToPreviewValue
    }
    public enum CompareResult
    {
        None,
        NoChange,
        Rising,
        Falling
    }
}
