using System;
using System.Collections.Generic;
using System.Xml;
using Aitex.Core.Common.DeviceData;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;

namespace Aitex.Core.RT.Device.Devices
{
    public interface IValve
    {
        bool TurnValve(bool isOn, out string reason);
    }

    public class IoValve : BaseDevice, IDevice, IValve
    {
        #region Variables

        private readonly DeviceTimer _timMfcRampTimeout = new DeviceTimer();
        private const int MFC_RAMP_DURATION_SEC = 5;
        private const int MFC_RAMP_TIMEOUT_SEC = 10;
        private readonly object _mfcLock = new object();
        private IoMFC _mappedMfc;
        private bool _isValveOnAfterMfcRamp;

        #endregion


        public string GVName { get { return Name; } }

        public string GVDeviceID { get { return DeviceID; } }

        public bool GVIsDefaultOpen { get { return _isDefaultOpen; } }

        public Func<bool, bool> FuncCheckInterLock;
        public Func<bool, bool> FuncForceOpen;
        public Action<bool> ActionAfterOnOff;        //作用于EPV1和EPV2


        [Subscription(AITValveDataPropertyName.SetPoint)]
        public bool SetPoint    //True：open| False：close
        {
            get
            {
                if (_doOpen == null)
                    return false;

                return _isNc ? _doOpen.Value : !_doOpen.Value;
            }
            set
            {
                if (_doOpen != null)
                {
                    _doOpen.Value = _isNc ? value : !value;
                }
                if (_doClose != null)
                {
                    _doClose.Value = _isNc ? !value : value;
                }
            }
        }

        [Subscription(AITValveDataPropertyName.Status)]
        public bool Status    //True：open | False：close
        {
            get
            {
                if (_diOpenSensor != null && _diCloseSensor != null)
                    return _diOpenSensor.Value && !_diCloseSensor.Value;

                if (_diCloseSensor != null)
                    return !_diCloseSensor.Value;

                if (_diOpen != null)
                    return _isNc ? _diOpen.Value : !_diOpen.Value;

                if (_doOpen != null)
                    return _isNc ? _doOpen.Value : !_doOpen.Value;

                if (_doClose != null)
                    return _isNc ? !_doClose.Value : _doClose.Value;
                return false;
            }
        }

        private AITValveData DeviceData
        {
            get
            {
                AITValveData data = new AITValveData()
                {
                    UniqueName = _uniqueName,
                    DeviceName = GVName,
                    DefaultValue = GVIsDefaultOpen,
                    DeviceSchematicId = DeviceID,
                    DisplayName = Display,
                    Feedback = Status,
                    SetPoint = SetPoint,
                };

                return data;
            }
        }

        /// <summary>
        /// normal closed, 0 关闭，1打开
        /// </summary>
        public bool _isNc;


        /// <summary>
        /// default open
        /// </summary>
        public bool _isDefaultOpen;

        private DIAccessor _diOpenSensor;
        private DIAccessor _diCloseSensor;

        private DIAccessor _diOpen;
        private DOAccessor _doOpen;
        private DOAccessor _doClose;

        private bool _operation;

        R_TRIG forceOpenTrigger = new R_TRIG();
        R_TRIG eventTrigger = new R_TRIG();
        R_TRIG _mutexSignalTrigger = new R_TRIG();
        DeviceTimer _timer = new DeviceTimer();
        DeviceTimer _mutexSignalTimer = new DeviceTimer();
        private bool _scBypassEnableTable;

        private string _uniqueName;

        public IoValve(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
        {
            //var attrModule = node.GetAttribute("module");
            //base.Module = string.IsNullOrEmpty(attrModule) ? module : attrModule;
            //base.Name = node.GetAttribute("id");
            //base.Display = node.GetAttribute("display");
            //base.DeviceID = node.GetAttribute("schematicId");


            _isNc = GetValue(node, "isNc");// Convert.ToBoolean(node.GetAttribute("isNc"));
            _isDefaultOpen = GetValue(node, "isDefaultOpen");// Convert.ToBoolean(node.GetAttribute("isDefaultOpen"));

            _diOpenSensor = ParseDiNode("diOpenSensor", node, ioModule);
            _diCloseSensor = ParseDiNode("diCloseSensor", node, ioModule);
            _doOpen = ParseDoNode("doOpen", node, ioModule);
            _diOpen = ParseDiNode("diOpen", node, ioModule);
            _doClose = ParseDoNode("doClose", node, ioModule);

            _uniqueName = $"{Module}.{Name}";

            string path;
            if(module.StartsWith("PM"))
                path = $"PM.{module}.BypassEnableTable";    //_scBypassEnableTable = ParseScNode("scBypassEnableTable", node, module, $"PM.{module}.BypassEnableTable");
            else
                path = $"{module}.BypassEnableTable";  //_scBypassEnableTable = ParseScNode("scBypassEnableTable", node, module, $"{module}.BypassEnableTable");


            _scBypassEnableTable = SC.SafeGetValue(path, false);
            SC.RegisterValueChangedCallback(path, obj => _scBypassEnableTable = (bool)obj);
        }

        private bool GetValue(XmlElement node , string xmlNodeName)
        {
            var xmlNodeValue = node.GetAttribute(xmlNodeName);
            return  string.IsNullOrEmpty(xmlNodeValue)?  SC.GetValue<bool>($"{ScBasePath}.{Name}.{xmlNodeName}") : Convert.ToBoolean(xmlNodeValue);
        }

        public bool Initialize()
        {
            DATA.Subscribe($"Device.{Module}.{GVName}", () => DeviceData);
            DATA.Subscribe($"{_uniqueName}.DeviceData", () => DeviceData);
            DATA.Subscribe($"{_uniqueName}.Status", () => Status);

            OP.Subscribe($"{_uniqueName}.{AITValveOperation.GVTurnValve}", InvokeOpenCloseValve);


            DEVICE.Register(String.Format("{0}.{1}", Name, AITValveOperation.GVTurnValve), (out string reason, int time, object[] param) =>
            {
                bool bOn = Convert.ToBoolean((string)param[0]);
                bool ret = TurnValve(bOn, out reason);
                if (ret)
                {
                    reason = string.Format("Valve {0}{1}", Name, bOn ? "Open" : "Close");
                    return true;
                }

                return false;
            });

            //for recipe
            DEVICE.Register(String.Format("{0}", Name), (out string reason, int time, object[] param) =>
            {
                bool bOn = Convert.ToBoolean((string)param[0]);
                bool ret = TurnValve(bOn, out reason);
                if (ret)
                {
                    reason = string.Format("Valve {0}{1}", Name, bOn ? "Open" : "Close");
                    return true;
                }

                return false;
            });

            return true;
        }

        public void Terminate()
        {
            string reason;
            lock (_mfcLock)
            {
                if (_mappedMfc != null)
                {
                    _mappedMfc.StopRamp();
                    _mappedMfc = null;
                    _timMfcRampTimeout.Stop();
                }
            }

            TurnValve(_isDefaultOpen, out reason);
        }



        private bool InvokeOpenCloseValve(string method, object[] args)
        {
            string reason;
            bool op = Convert.ToBoolean(args[0]);
            string name = op ? "Open" : "Close";

            if (!TurnValve(op, out reason))
            {
                EV.PostWarningLog(Module, $"Can not {name} valve {Module}.{Name}, {reason}");
                return false;
            }

            EV.PostInfoLog(Module, $"{name} valve {Module}.{Name}");

            return true;
        }

        protected override void HandleMonitor()
        {
            try
            {
                if (_diOpenSensor != null && _diCloseSensor != null)
                {
                    if (_diOpenSensor.Value != _diCloseSensor.Value)
                    {
                        _mutexSignalTimer.Start(2000);
                    }

                    _mutexSignalTrigger.CLK = _mutexSignalTimer.IsTimeout();

                    if (_mutexSignalTrigger.Q)
                    {
                        EV.PostWarningLog(Module, $"Valve {Name} was abnormal，Reason：diOpenSensor's value is {_diOpenSensor.Value} and diCloseSensor's value is {_diCloseSensor.Value} too.");
                    }
                }

                MonitorMfc();

                if (_timer.IsTimeout())
                {
                    forceOpenTrigger.RST = true;
                    _timer.Stop();

                    if (Status != _operation)
                    {
                        if (_operation)
                        {
                            string reason;
                            if (!_doOpen.Check(_isNc ? true : false, out reason))
                                EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Open", "：Failed for interlock " + reason);
                            else
                                EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Open", "：Valve keep closed ");

                        }
                        else
                        {
                            string reason;
                            if (!_doOpen.Check(_isNc ? true : false, out reason))
                                EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Close", "：Failed for interlock " + reason);
                            else
                                EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Close", "：Valve keep open");

                        }
                    }
                    else if (ActionAfterOnOff != null)
                    {
                        ActionAfterOnOff(Status);
                    }
                    _operation = SetPoint;
                }
                else if (_timer.IsIdle())
                {
                    eventTrigger.CLK = SetPoint != _operation;   // fire event only check at first, SetPoint set by interlock
                    if (eventTrigger.Q)
                    {
                        if (_operation)
                        {
                            string reason;
                            if (!_doOpen.Check(_isNc ? true : false, out reason))
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}，Reason：{2}", Display, "Close", reason));
                            else
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}，Reason {2}", Display, "Close", "PLC kept"));
                        }
                        else
                        {
                            string reason;
                            if (!_doOpen.Check(_isNc ? true : false, out reason))
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}，Reason：{2}", Display, "Open", reason));
                            else
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}，Reason {2}", Display, "Open", "PLC Kept"));
                        }
                        _operation = SetPoint;
                    }
                }

                //达到一定条件，强制打开阀门
                if (FuncForceOpen != null && !_scBypassEnableTable)
                {
                    forceOpenTrigger.CLK = FuncForceOpen(Status);
                    if (forceOpenTrigger.Q)
                    {
                        SetPoint = !Status;
                        EV.PostMessage(Module, EventEnum.SwInterlock, Module, $"Force Set {Name} to {!Status} for Interlock enable table!");
                    }
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
        }


        public bool TurnValve(bool isOn, out string reason)
        {
            reason = "";
            bool bValue = _isNc ? isOn : !isOn;

            // 如果要打开阀门，并且阀门已经在打开状态，则啥也不干
            if (isOn && Status)
            {
                return true;
            }

            _timer.Stop();

            if (_doOpen != null)
            {
                if (!_doOpen.Check(bValue, out reason))
                {
                    EV.PostWarningLog(Module, $"Turn {Display} {ValveStateToString(isOn)} failed for Interlock!");
                    EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}，Reason：{2}", Display, "Open", reason));
                    return false;
                }

                if (FuncCheckInterLock != null && !_scBypassEnableTable)
                {
                    if (!FuncCheckInterLock(isOn))
                    {
                        reason = "Interlock check failed!";
                        //EV.PostWarningLog(Module, $"Turn {Display} {ValveStateToString(isOn)} failed for check condition!");
                        return false;
                    }
                }
            }
            if (_doClose != null)
            {
                if (!_doClose.Check(!bValue, out reason))
                {
                    EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}，Reason：{2}", Display, "Close", reason));
                    return false;
                }
            }

            if (ValveToMFCMap.ValveControlMFC.TryGetValue(Name, out var _mfcName)) // 需要Ramp前置MFC
            {
                if (!isOn)//关闭蝶阀时，关闭MFC,如果MFC为0时可以不用设置，但是没有获取MFC当前状态的函数，
                {
                    if (_doOpen.Value)//气阀是开启状态了，需要关闭MFC，
                    {
                        CloseMFC(isOn, _mfcName);
                        return true;
                    }
                }
                else//这是开阀指令，开阀时如果阀门时关闭状态，MFC设置为零，保证气流平缓
                {
                    if (Name != "TMVent" && !_doOpen.Value)//气阀是关闭状态了，需要关闭MFC，
                    {
                        CloseMFC(isOn, _mfcName);
                        return true;
                    }
                }

            }

            EV.PostInfoLog(Module, $"Turn {Display} {ValveStateToString(isOn)}");

            reason = "";
            SetPoint = isOn;

            _operation = isOn;

           
            _timer.Restart(2000);         //2 seconds to monitor

            return true;

        }

        /// <summary>
        /// 如果存在前置MFC，则等待MFC流量到0后再开关阀。
        /// </summary>
        private void MonitorMfc()
        {
            lock (_mfcLock)
            {
                if (_mappedMfc != null)
                {
                    if (_mappedMfc.FeedBack < _mappedMfc.Scale * 0.015) // 等待MFC Ramp到0
                    {
                        SetPoint = _isValveOnAfterMfcRamp;
                        _operation = _isValveOnAfterMfcRamp;
                        _mappedMfc = null;
                        _timMfcRampTimeout.Stop();
                        _timer.Start(2000);
                    }
                    else if (_timMfcRampTimeout.IsTimeout()) // 等待MFC Ramp 到0超时
                    {
                        _mappedMfc.StopRamp();
                        _timMfcRampTimeout.Stop();
                        EV.PostAlarmLog(Module,
                            $"Timeout to ramp {_mappedMfc.Name} to 0.0{_mappedMfc.Unit} when Turning {Name} {ValveStateToString(_isValveOnAfterMfcRamp)}");
                        _mappedMfc = null;
                    }
                }
            }
        }

        /// <summary>
        /// 关闭前置MFC。
        /// </summary>
        /// <param name="isOn"></param>
        /// <param name="_mfcName"></param>
        private void CloseMFC(bool isOn, string _mfcName)
        {
            lock (_mfcLock)
            {
                // 获取前置MFC
                _mappedMfc = DEVICE.GetDevice<IoMFC>($"{Module}.{_mfcName}");
                if (_mappedMfc == null)
                {
                    // 如果未获取到前置MFC，则直接操作蝶阀
                    EV.PostWarningLog(Module, $"Unable to find mapped MFC {_mfcName} of valve {Name}");
                    EV.PostInfoLog(Module, $"Turn {Display} {ValveStateToString(isOn)}");
                    SetPoint = isOn;
                    _operation = isOn;
                }
                else
                {
                    EV.PostInfoLog(Module, $"Start to ramp {_mfcName} to 0.0{_mappedMfc.Unit}");
                    _isValveOnAfterMfcRamp = isOn;
                    _mappedMfc.Ramp(0, 5 * 1000);
                    _timMfcRampTimeout.Start(10 * 1000);

                }

                _timer.Start(2000);
            }
        }

        private string ValveStateToString(bool isOn)
        {
            return isOn ? "On" : "Off";
        }

        public void Reset()
        {
            eventTrigger.RST = true;
            forceOpenTrigger.RST = true;

            lock (_mfcLock)
            {
                if (_mappedMfc != null)
                {
                    _mappedMfc.Reset();
                    _mappedMfc = null;
                    _timMfcRampTimeout.Stop();
                }
            }
        }


    }
    public static class ValveToMFCMap
    {
        /// <summary>
        /// Key= IoValve_id , Value=IoMFC_id
        /// </summary>
        public static Dictionary<string, string> ValveControlMFC = new Dictionary<string, string>()
        {
            //{ "TMVent","Mfc60" },
            { "V96",$"Mfc38" },
            { "V94",$"Mfc36" },
        };
    }

}
