using PlcCom;
using PlcComponent;
using Sunny.UI.Win32;
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;
public class PlcBindingManager
{
    private readonly List<ISingleOutputComponent> singleOutputComponents =new List<ISingleOutputComponent>();
    private readonly List<ISingleInputComponent> singleInputComponents = new List<ISingleInputComponent>();

    private readonly List<IMultiOutputComponent> multiOutputComponents = new List<IMultiOutputComponent>();
    private readonly List<IPlcMultiInputComponent> multiInputComponents = new List<IPlcMultiInputComponent>();


    private List<IPlcUiControl> controlsWithInvisibilityProperty = new List<IPlcUiControl>();
    private List<IPlcUiControl> controlsWithDisabilityProperty = new List<IPlcUiControl>();

    private readonly List<string> readAddressesOfSingleOutputComs = new List<string>();
    private readonly List<string> writeAddressesOfSingleInputComs = new List<string>();

    private TimeSpan TimeSpanReadOverTime = TimeSpan.FromSeconds(1);
    private List<Timer> timers=new List<Timer>();

    private Dictionary<string, List<IPlcUiControl>> dictInvisibilityTriggerNode_Controls;
    private Dictionary<string, List<IPlcUiControl>> dictDisabilityTriggerNode_Controls;
    public void Bind(List<IPlcBindableComponent> components,List<IPlcUiControl> controls)
    {
        foreach (var c in components)
        {
            if (c is ISingleOutputComponent soc)
            {
                if (soc.ReadNode != null && !string.IsNullOrEmpty(soc.ReadNode.Value))
                {
                    singleOutputComponents.Add(soc);
                    //readAddressesOfSingleOutputComs.Add(soc.ReadNode.Value);
                }
            }
            if (c is ISingleInputComponent sic)
            {
                if (sic.WriteNode != null && !string.IsNullOrEmpty(sic.WriteNode.Value))
                {
                    singleInputComponents.Add(sic);
                    writeAddressesOfSingleInputComs.Add(sic.WriteNode.Value);
                }
            }
            if (c is IMultiOutputComponent moc)
            {
                if (moc.ReadNodes != null)
                    multiOutputComponents.Add(moc);
            }
            if (c is IPlcMultiInputComponent mic)
            {
                if (mic.WriteNodes != null)
                    multiInputComponents.Add(mic);
            }
        }
        foreach (var item in controls)
        {
            if (item.InvisibilityTriggerNode != null)
                controlsWithInvisibilityProperty.Add(item);
            if (item.DisabilityTriggerNode != null)
                controlsWithDisabilityProperty.Add(item);
        }
        dictInvisibilityTriggerNode_Controls = controlsWithInvisibilityProperty
            .GroupBy(c => c.InvisibilityTriggerNode.Value)
            .ToDictionary(g => g.Key, g => g.ToList());


        dictDisabilityTriggerNode_Controls = controlsWithDisabilityProperty
            .GroupBy(c => c.DisabilityTriggerNode.Value)
            .ToDictionary(g => g.Key, g => g.ToList());
    }
    public void SetPlcContext(PlcContex context, int intervalMs = 100)
    {
        SetBingControlsProtocol(context.Protocol);
        if (context.Protocol.IsConnected)
        {
            /*********************************剔除绑定单个地址的无效组件地址，并开始刷新组件*********************************************/
            RemoveInvalidNodes(context, singleOutputComponents);//单个刷新一次，把无法读取的控件剔除掉
            readAddressesOfSingleOutputComs.AddRange(singleOutputComponents.Select(c=>c.ReadNode.Value).ToList());

            /**********************************剔除无效的控件不可见触发地址，并开始刷新组件********************************************/
            RemoveInvalidNodes(context, dictInvisibilityTriggerNode_Controls);//单个刷新一次，把无法读取的控件剔除掉

            /**********************************剔除无效的控件不可用触发地址，并开始刷新组件********************************************/
            RemoveInvalidNodes(context, dictDisabilityTriggerNode_Controls);//单个刷新一次，把无法读取的控件剔除掉

            if (singleOutputComponents.Count > 0 || dictInvisibilityTriggerNode_Controls.Count > 0 || dictDisabilityTriggerNode_Controls.Count > 0)
            {
                Timer _timer;
                _timer = new Timer();
                _timer.Interval = intervalMs;
                _timer.Tick += async (s, e) =>{ 
                    ((Timer)s).Stop(); 
                    if(singleOutputComponents.Count > 0) await RefreshSingleOutputComponentsByListAsync(context);
                    if (dictInvisibilityTriggerNode_Controls.Count > 0) await RefreshInvisibilityTriggerNodeAsync(context, dictInvisibilityTriggerNode_Controls);
                    if (dictDisabilityTriggerNode_Controls.Count > 0) await RefreshDisabilityTriggerNodeAsync(context, dictDisabilityTriggerNode_Controls);
                    ((Timer)s).Start(); };// RemoveInvalidNodes(context);
                _timer.Start();
                timers.Add(_timer);
            }
            /**********************************剔除绑定多个地址的无效组件地址，并开始刷新组件********************************************/
            foreach (var c in multiOutputComponents)
            {
                if (c.BackgroundRefreshEnable)
                {
                    var invalidNodes= c.RemoveInvalidNodes();
                    foreach (var node in invalidNodes)
                    {
                        ReadOneNodeFailed.Invoke(c, node, 0);
                    }
                    if (c.ReadNodes.Count > 0)
                    {
                        var timer = new Timer();
                        timer.Interval = c.ReadInterval;
                        timer.Tick += async (s, e) => { ((Timer)s).Stop(); await RefreshMultiOutputComponentsByListAsync(context, c); ((Timer)s).Start(); };
                        timer.Start();
                        timers.Add(timer);
                    }
                }
            }
        }
    }


    void SetBingControlsProtocol(IPlcComProtocol plcComProtocol)
    {
        foreach (var component in singleOutputComponents)
        {
            component.Protocol = plcComProtocol;
        }
        foreach (var component in singleInputComponents)
        {
            component.Protocol = plcComProtocol;
        }
        foreach (var component in multiOutputComponents)
        {
            component.Protocol = plcComProtocol;
        }
        foreach (var component in multiInputComponents)
        {
            component.Protocol = plcComProtocol;
        }
    }
    public event Action ReadOK;
    public event Action<IPlcBindableComponent,Node,uint> ReadOneNodeFailed;
    public event Action<List<string>, uint> ReadMultiNodesFailed;
    public event Action ReadOverTime;

    

    /// <summary>
    /// 逐个读取单个输出的控件，目的是剔除掉无法读取的控件
    /// </summary>
    /// <param name="context"></param>
    private List<ISingleOutputComponent> RemoveInvalidNodes(PlcContex context, List<ISingleOutputComponent> components)
    {
        var toRemove = new List<ISingleOutputComponent>();

        List<string> strings = components.Select(n => n.ReadNode.Value).ToList();
        try
        {
            var rt= context.Protocol.ReadValue(strings, out List<object> results);
            if(rt==0)
            {
                for(int i = 0; i < strings.Count; i++)
                {
                    if (results[i]==null)
                    {
                        toRemove.Add(components[i]);
                    }
                }
            }
        }
        catch (Exception)
        {
        }
        foreach (var item in toRemove)
        {
            components.Remove(item);
            ReadOneNodeFailed.Invoke(item, item.ReadNode, 0);
        }
        return components;
    }
    private Dictionary<string, List<IPlcUiControl>> RemoveInvalidNodes(PlcContex context, Dictionary<string, List<IPlcUiControl>> keyValuePairs)
    {
        var toRemove = new List<(string,List<IPlcUiControl>)>();

        List<string> strings = keyValuePairs.Select(k => k.Key).ToList();
        try
        {
            var rt = context.Protocol.ReadValue(strings, out List<object> results);
            if (rt == 0)
            {
                for (int i = 0; i < strings.Count; i++)
                {
                    if (results[i] == null)
                    {
                        toRemove.Add((strings[i],keyValuePairs[strings[i]]));
                        keyValuePairs.Remove(strings[i]);
                    }
                }
            }
        }
        catch (Exception)
        {
        }
        foreach (var item in toRemove)
        {
            foreach (var c in item.Item2)
            {
                ReadOneNodeFailed.Invoke((IPlcBindableComponent)c, new Node(item.Item1), 0);
            }
        }
        return keyValuePairs;
    }

    private int _isReading = 0; // 0 = idle, 1 = busy

    /// <summary>
    /// 批量读取页面中单个输出的控件
    /// </summary>
    /// <param name="context"></param>
    private async Task RefreshSingleOutputComponentsByListAsync(PlcContex context)
    {
        if (Interlocked.CompareExchange(ref _isReading, 1, 0) == 0)
        {
            try
            {
                List<object> results;
                Stopwatch stopwatch = Stopwatch.StartNew();
                var task = Task.Run(() =>
                {
                    var readRt = context.Protocol.ReadValue(readAddressesOfSingleOutputComs, out results);
                    return (readRt, results);
                });

                // 使用超时等待
                if (await Task.WhenAny(task, Task.Delay(TimeSpanReadOverTime)) == task)
                {
                    // 在超时前完成
                    var rt = await task;
                    if (rt.readRt == 0)
                    {
                        ReadOK?.Invoke();
                        for (int i = 0; i < rt.results.Count; i++)
                        {
                            singleOutputComponents[i].UpdateValue(rt.results[i]);
                        }
                    }
                    else
                    {
                        ReadMultiNodesFailed?.Invoke(readAddressesOfSingleOutputComs, rt.readRt);
                    }
                }
                else
                {
                    ReadOverTime?.Invoke();
                }
            }
            finally
            {
                _isReading = 0; // 重置
            }
        }
    }
    private async Task RefreshMultiOutputComponentsByListAsync(PlcContex context, IMultiOutputComponent c)
    {
        if (Interlocked.CompareExchange(ref _isReading, 1, 0) == 0)
        {
            try
            {
                Stopwatch stopwatch = Stopwatch.StartNew();
                List<string> strings = c.ReadNodes.Select(n => n.Value).ToList();

                var task = Task.Run(() =>
                {
                    var readRt = context.Protocol.ReadValue(strings, out List<object> results);
                    return (readRt, results);
                });

                // 使用超时等待
                if (await Task.WhenAny(task, Task.Delay(TimeSpanReadOverTime)) == task)
                {
                    // 在超时前完成
                    var rt = await task;
                    if (rt.readRt == 0)
                    {
                        ReadOK?.Invoke();
                        c.UpdateValues(c.ReadNodes, rt.results);
                    }
                    else
                    {
                        ReadMultiNodesFailed?.Invoke(strings, rt.readRt);
                    }
                }
                else
                {
                    ReadOverTime?.Invoke();
                }
            }
            finally
            {
                _isReading = 0; // 重置
            }
        }
    }

    private List<string> invisibilityTriggerNode;
    private bool _isCacheValid = false;

    private void EnsureInvisibilityTriggerNodes(Dictionary<string, List<IPlcUiControl>> dictNodeControls)
    {
        if (!_isCacheValid)
        {
            invisibilityTriggerNode = dictNodeControls.ToList().Select(kv => kv.Key).ToList();
            _isCacheValid = true;
        }
    }
    private async Task RefreshInvisibilityTriggerNodeAsync(PlcContex context, Dictionary<string, List<IPlcUiControl>> dictNodeControls)
    {
        if (Interlocked.CompareExchange(ref _isReading, 1, 0) == 0)
        {
            try
            {
                EnsureInvisibilityTriggerNodes(dictNodeControls);
                List<object> results;
                Stopwatch stopwatch = Stopwatch.StartNew();

                var task = Task.Run(() =>
                {
                    var readRt = context.Protocol.ReadValue(invisibilityTriggerNode, out results);
                    return (readRt, results);
                });

                // 使用超时等待
                if (await Task.WhenAny(task, Task.Delay(TimeSpanReadOverTime)) == task)
                {
                    // 在超时前完成
                    var rt = await task;
                    if (rt.readRt == 0)
                    {
                        for (int i = 0; i < invisibilityTriggerNode.Count; i++)
                        {
                            if (rt.results[i] is bool b)
                            {
                                foreach (var control in dictNodeControls[invisibilityTriggerNode[i]])
                                {
                                    control.Invisibility = control.ValueForInvisibility == b;
                                }
                            }
                        }
                    }
                    else
                    {
                        ReadMultiNodesFailed?.Invoke(invisibilityTriggerNode, rt.readRt);
                    }
                }
            }
            finally
            {
                _isReading = 0; // 重置
            }
        }
    }
    private List<string> disabilityTriggerNode;
    private bool _isCacheValid2 = false;

    private void EnsureDisabilityTriggerNodes(Dictionary<string, List<IPlcUiControl>> dictNodeControls)
    {
        if (!_isCacheValid2)
        {
            disabilityTriggerNode = dictNodeControls.ToList().Select(kv => kv.Key).ToList();
            _isCacheValid2 = true;
        }
    }
    private async Task RefreshDisabilityTriggerNodeAsync(PlcContex context, Dictionary<string, List<IPlcUiControl>> dictNodeControls)
    {
        if (Interlocked.CompareExchange(ref _isReading, 1, 0) == 0)
        {
            try
            {
                EnsureDisabilityTriggerNodes(dictNodeControls);

                List<object> results;
                Stopwatch stopwatch = Stopwatch.StartNew();

                var task = Task.Run(() =>
                {
                    var readRt = context.Protocol.ReadValue(disabilityTriggerNode, out results);
                    return (readRt, results);
                });

                // 使用超时等待
                if (await Task.WhenAny(task, Task.Delay(TimeSpanReadOverTime)) == task)
                {
                    // 在超时前完成
                    var rt = await task;
                    if (rt.readRt == 0)
                    {
                        for (int i = 0; i < disabilityTriggerNode.Count; i++)
                        {
                            if (rt.results[i] is bool b)
                            {
                                foreach (var control in dictNodeControls[disabilityTriggerNode[i]])
                                {
                                    control.Disability = control.ValueForDisability && b;
                                }
                            }
                        }
                    }
                    else
                    {
                        ReadMultiNodesFailed?.Invoke(disabilityTriggerNode, rt.readRt);
                    }
                }
            }
            finally
            {
                _isReading = 0; // 重置
            }
        }
    }

    public void Stop()
    {
        foreach (var item in timers)
        {
            item.Stop();
        }
    }
}
