using System;
using System.Diagnostics;
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.OperationCenter;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.Common.Aitex.Core.RT.Device;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.MFCs;

namespace Aitex.Core.RT.Device.Devices
{
   public class IoMFC : ErrorDetectableBaseDevice, IMfc
    {

        #region Variables

        private readonly DeviceTimer _rampTimer = new();
        private double _rampTarget;
        private double _rampInitValue;
        private double _rampSetMode;
        private int _rampTime;


        private readonly DIAccessor _diAlarm;
        private readonly AIAccessor _aiFlow;
        private readonly AIAccessor _aiActMode;
        private readonly AOAccessor _aoFlow;
        private readonly AOAccessor _aoSetMode;
        
        #region 当以下任意DO为1时，监测SP和FB误差，其余时间不用监测

        protected readonly DOAccessor MaskDOPreProcess;
        protected readonly DOAccessor MaskDOProcess;
        protected readonly DOAccessor MaskDOPostProcess;
        protected readonly DOAccessor MaskDOProcessIdle;
        
        #endregion



        protected SCConfigItem _scGasName;
        protected SCConfigItem _scEnable;

        private readonly SCConfigItem _scN2Scale;
        private readonly SCConfigItem _scDefaultSetPoint;
        private readonly bool _isFloatAioType = false;

        /// <summary>
        /// 当检测到前置Valve关闭时，停止并重置当前报警检测，以避免下次Valve开启式可能立即报警的问题。
        /// </summary>
        private readonly R_TRIG _valveCloseTrigger = new ();

        /// <summary>
        /// 当前MFC前置或后置阀门ID
        /// </summary>
        private readonly string _valve;

        /// <summary>
        /// 是否根据Valve的开光状态决定是否进行误差检测。
        /// 如果该参数为true，当Valve关闭时，不进行误差检测。
        /// </summary>
        private readonly bool _disableFbSpErrMonWhenValveClosed;

        #endregion

        #region Constructors

        public IoMFC(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
        {
            _aiFlow = ParseAiNode("aiFlow", node, ioModule);
            _aoFlow = ParseAoNode("aoFlow", node, ioModule);
            _aiActMode = ParseAiNode("aiActMode", node, ioModule);
            _aoSetMode = ParseAoNode("aoSetMode", node, ioModule);
            _diAlarm = ParseDiNode("diAlarm", node, ioModule);

            MaskDOProcessIdle = ParseDoNode("maskDOProcessIdle", node, ioModule);
            MaskDOPreProcess = ParseDoNode("maskDOPreProcess", node, ioModule);
            MaskDOPostProcess = ParseDoNode("maskDOPostProcess", node, ioModule);
            MaskDOProcess = ParseDoNode("maskDOProcess", node, ioModule);

            
            _valve =  node.HasAttribute("Valve") ? node.GetAttribute("Valve") : "";
            _disableFbSpErrMonWhenValveClosed = node.HasAttribute("DisableFbSpErrMonWhenValveClosed") && Convert.ToBoolean(node.GetAttribute("DisableFbSpErrMonWhenValveClosed"));
            _isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float");

            _scGasName = SC.GetConfigItem($"{ScBasePath}.{Name}.GasName");
            _scN2Scale = ParseScNode("scN2Scale", node, ioModule, $"{ScBasePath}.{Name}.N2Scale");
            _scDefaultSetPoint = ParseScNode("scDefaultSetPoint", node, ioModule, $"{ScBasePath}.{Name}.DefaultSetPoint");

        }

        #endregion

        #region Properties

        public string DisplayName
        {
            get
            {
                if (_scGasName != null)
                    return _scGasName.StringValue;
                return Display;
            }
        }

        public double Scale
        {
            get
            {
                if (_scN2Scale == null )
                    return 0;
                return _scN2Scale.DoubleValue ;
            }
        }

        public double SetPoint
        {
            get
            {
                if (_aoFlow != null)
                {
                    return _aoFlow.Value;
                }
                return 0;
            }
            set
            {
                if (_aoFlow != null)
                {
                    if (_isFloatAioType)
                        _aoFlow.Value = (float)value;
                    else
                        _aoFlow.Value = (short)value;
                }
            }
        }

        public double DefaultSetPoint
        {
            get
            {
                if (_scDefaultSetPoint != null)
                    return _scDefaultSetPoint.DoubleValue;
                return 0;
            }
        }

        public double FeedBack
        {
            get
            {
                if (_aiFlow != null)
                {
                    double aiValue = _isFloatAioType ? _aiFlow.Value : _aiFlow.Value;

                    return aiValue; // (_scRegulationFactor != null && _scRegulationFactor.DoubleValue > 0.001) ? aiValue / _scRegulationFactor.DoubleValue : aiValue;
                }
                return 0;
            }
        }

        public double ActMode
        {
            get
            {
                if (_aiActMode != null)
                {
                    return _aiActMode.Value;
                }
                return 0;
            }
        }

        public double SetMode
        {
            get
            {
                if (_aoSetMode != null)
                {
                    return _aoSetMode.Value;
                }
                return 0;
            }
            set
            {
                if (_aoSetMode != null)
                {
                    if (_isFloatAioType)
                        _aoSetMode.Value = (float)value;
                    else
                        _aoSetMode.Value = (short)value;
                }
            }
        }

        private AITMfcData DeviceData
        {
            get
            {
                //原DisplayName = DisplayName  改成DisplayName = $"{Name}-->{DisplayName}"
                AITMfcData data = new AITMfcData()
                {
                    UniqueName = $"{Module}.{Name}",
                    Type = "MFC",
                    Module = Module,
                    DeviceName = Name,
                    DeviceSchematicId = DeviceID,
                    DisplayName = $"{Name}-->{DisplayName}",
                    FeedBack = FeedBack,
                    SetPoint = SetPoint,
                    Scale = Scale,
                    IsWarning = ErrWarningChecker.Result,
                    IsError = ErrAlarmChecker.Result,
                    DefaultValue = DefaultSetPoint,
                    ActMode = ActMode,
                    SetMode = SetMode,
                };
                //if (Module.Equals("PM1") && Name.Equals("Mfc1"))
                //    Console.WriteLine("Mfc3:" + FeedBack);
                return data;
            }
        }

        #endregion

        #region Methods

        public override bool Initialize()
        {
            DATA.Subscribe($"{Module}.{Name}.DeviceData", () => DeviceData);

            DATA.Subscribe($"{Module}.{Name}.FeedBack", () => FeedBack);
            DATA.Subscribe($"{Module}.{Name}.SetPoint", () => SetPoint);
            DATA.Subscribe($"{Module}.{Name}.SetMode", () => SetMode);
           
            OP.Subscribe($"{Module}.{Name}.Ramp", (out string reason, int time, object[] param) =>
            {
                double target = Convert.ToDouble(param[0].ToString());

                if (target < 0 || target > Scale)
                {
                    reason = $"set {Display} value {target} out of range [0, {Scale}] {Unit}";
                    return false;
                }

                Ramp(target, time);

                if (time > 0)
                {
                    reason = $"{Display} ramp to {target} {Unit} in {time} seconds";
                }
                else
                {
                    reason = $"{Display} ramp to {target} {Unit}";
                }
                return true;
            });
            OP.Subscribe($"{Module}.{Name}.SetMode", SetContrlMode);
            //OP.Subscribe($"{Module}.{Name}.SetMode", (function, args) =>
            //{
            //    if (!Enum.TryParse((string)args[0], out PressureCtrlMode mode))
            //    {
            //        EV.PostWarningLog(Module, $"Argument {args[0]}not valid");
            //        return false;
            //    }
            //    SetMode();
            //    return true;
            //});

            return true;
        }

        private bool SetContrlMode(out string reason, int time, object[] param)
        {
            return SetMfcMode((MfcCtrlMode)Enum.Parse(typeof(MfcCtrlMode), (string)param[0], true),
                out reason);
        }

        public bool SetMfcMode(MfcCtrlMode mode, out string reason)
        {
            switch (mode)
            {
                case MfcCtrlMode.Normal:
                    SetMode = (double)MfcCtrlMode.Normal;
                    break;
                case MfcCtrlMode.Close:
                    SetMode = (double)MfcCtrlMode.Close;
                    break;
                case MfcCtrlMode.Open:
                    SetMode = (double)MfcCtrlMode.Open;
                    break;
                case MfcCtrlMode.Hold:
                    SetMode = (double)MfcCtrlMode.Hold;
                    break;
                default:
                    break;

            }
            reason = $"{Display} set to {mode}";
            EV.PostInfoLog(Module, reason);
            return true;
        }


        protected override void HandleMonitor()
        {
            MonitorRamping();

            var isAllowErrMon = true;
            if (!string.IsNullOrEmpty(_valve) && _disableFbSpErrMonWhenValveClosed)
            {
                // 如果配置为Valve关闭时不检测Feedback-SetPoint误差，获取Valve状态
                var devData = DATA.Poll($"{Module}.{_valve}.DeviceData");
                if (devData is AITValveData valveData)
                {
                    // 如果Valve关闭，则不检测。
                    isAllowErrMon = valveData.Feedback;

                    _valveCloseTrigger.CLK = !valveData.IsOpen;
                    if (_valveCloseTrigger.Q)
                    {
                        Reset();
                    }
                }
                else
                {
                    Debug.Assert(false, $"Unable to poll the {Module}.{_valve}.DeviceData.");
                }
            }

            // 根据MaskDO再次决定是否监测误差
            var isInProcess = false;
            if ((MaskDOProcess != null && MaskDOProcess.Value)
                || (MaskDOProcessIdle != null && MaskDOProcessIdle.Value)
                || (MaskDOPreProcess != null && MaskDOPreProcess.Value)
                || (MaskDOPostProcess != null && MaskDOPostProcess.Value))
            {
                // 仅在工艺中时监测MFC误差
                isInProcess = true;
            }

            if (isAllowErrMon && isInProcess)
            {
                // 当SetPoint大于0.01，并且没有Ramp时，允许检测误差。
                MonitorSpFbError(SetPoint >= 0.01 && _rampTimer.IsIdle(), SetPoint, FeedBack);
            }
        }

        public void SetToDefaultByRamp(int time)
        {
            Ramp(DefaultSetPoint, time * 1000);
        }


        public override void Terminate()
        {
            Ramp(DefaultSetPoint, 5000);
            base.Terminate();
        }

        public bool Ramp(double flowSetPoint, int time, out string reason)
        {
            if (HasAlarm)
            {
                reason = $"{DisplayName} in error status, can not flow";
                return false;
            }

            if (flowSetPoint < 0 || flowSetPoint > Scale)
            {
                reason = $"{DisplayName} range is [0, {Scale}], can not flow {flowSetPoint}";
                return false;
            }

            if (time > 0)
            {
                EV.PostInfoLog(Module, $"Set {DisplayName} flow to {flowSetPoint} {Unit} in {time / 1000:F0} seconds");
            }
            else
            {
                EV.PostInfoLog(Module, $"Set {DisplayName} flow to {flowSetPoint} {Unit}");
            }

            Ramp(flowSetPoint, time);
            reason = string.Empty;
            return true;
        }

        public void Ramp(int time)
        {
            Ramp(DefaultSetPoint, time);
        }

        public void Ramp(double target, int time)
        {
            _rampTimer.Stop();
            target = Math.Max(0, target);
            target = Math.Min(Scale, target);
            _rampInitValue = SetPoint;    //ramp 初始值取当前设定值，而非实际读取值.零漂问题
            _rampTime = time;
            _rampTarget = target;
            _rampTimer.Start(_rampTime);
            _rampSetMode = SetMode;

            //EV.PostInfoLog(Module, $"{Name}.Ramp {_rampTarget} in {_rampTime} senconds,Mode {_rampSetMode/1000}");

        }

        public void StopRamp()
        {
            if (!_rampTimer.IsIdle())
            {
                if (_rampTime != 0)
                {
                    Ramp(SetPoint, 0);
                }
            }
        }

        private void MonitorRamping()
        {
            if (!_rampTimer.IsIdle())
            {
                if (_rampTimer.IsTimeout() || _rampTime == 0)
                {
                    _rampTimer.Stop();
                    SetPoint = _rampTarget;
                }
                else
                {
                    SetPoint = _rampInitValue + (_rampTarget - _rampInitValue) * _rampTimer.GetElapseTime() / _rampTime;
                }

                if(_rampSetMode == 0 || _rampSetMode == 1 || _rampSetMode == 2 || _rampSetMode == 3)
                {
                    SetMode = _rampSetMode;
                }
            }
        }

        #endregion
    }
}
