using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.SCCore;
using Aitex.Core.RT.Tolerance;
using Aitex.Core.Util;
using MECF.Framework.Common.Event;
using System;
using System.Xml;

namespace MECF.Framework.Common.Aitex.Core.RT.Device
{
    /// <summary>
    /// 具有SetPoint、FeedBack误差实时检测功能的设备基类。
    /// <para>注意：在子类中注册FeedbackGetter、SetPointGetter、RampTimerGetter</para>
    /// </summary>
    public class ErrorDetectableBaseDevice : BaseDevice, IDevice
    {

        #region Variable

        private readonly R_TRIG _alarmTrigger;

        /// <summary>
        /// 是否启用误差检测
        /// </summary>
        private bool _isEnableErrDetection;

        /// <summary>
        /// 误差报警超限阈值
        /// </summary>
        private double _alarmRange;

        /// <summary>
        /// 误差报警超限持续时间
        /// </summary>
        private double _alarmTime;

        /// <summary>
        /// 误差警告超限阈值
        /// </summary>
        private  double _warningRange;

        /// <summary>
        /// 误差警告超限持续时间
        /// </summary>
        private double _warningTime;

        protected readonly DIAccessor DiAlarm;
        protected readonly string AlarmText;

        /// <summary>
        /// 误差超限警告检查器
        /// </summary>
        protected readonly ToleranceChecker ErrWarningChecker;

        /// <summary>
        /// 误差超限报警检查器
        /// </summary>
        protected readonly ToleranceChecker ErrAlarmChecker;

        private readonly AlarmEventItem _alarmEvent;
        private readonly AlarmEventItem _warningEvent;

        #endregion

        #region Constructors

        public ErrorDetectableBaseDevice(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
        {
            // 获取diAlarm
            DiAlarm = ParseDiNode("diAlarm", node, ioModule);

            // 获取Alarm Text
            AlarmText = node.GetAttribute("AlarmText");

            _isEnableErrDetection =
                ParseScNode("scEnableAlarm", node, ioModule, $"{ScBasePath}.{Display}.EnableAlarm")?.BoolValue ?? false;
            _alarmRange =
                ParseScNode("scAlarmRange", node, ioModule, $"{ScBasePath}.AlarmRange")?.DoubleValue ??
                double.NaN;
            _alarmTime = ParseScNode("scAlarmTime", node, ioModule, $"{ScBasePath}.AlarmTime")?.IntValue ??
                         int.MinValue;

            _warningRange = SC.GetConfigItem($"{ScBasePath}.WarningRange")?.DoubleValue ?? double.NaN;
            _warningTime = SC.GetConfigItem($"{ScBasePath}.WarningTime")?.IntValue ?? int.MinValue;

            _alarmTrigger = new R_TRIG();
            ErrWarningChecker = new ToleranceChecker();
            ErrAlarmChecker = new ToleranceChecker();

            _warningEvent = SubscribeAlarm($"{Module}.{Display}.ToleranceWarning", "", ResetWarningChecker,
                EventLevel.Warning);
            _alarmEvent = SubscribeAlarm($"{Module}.{Display}.ToleranceAlarm", "", ResetAlarmChecker);


            SC.RegisterValueChangedCallback($"{ScBasePath}.{Display}.EnableAlarm", (newValue) =>
            {
                if (!bool.TryParse(newValue.ToString(), out _isEnableErrDetection))
                {
                    _isEnableErrDetection = false;
                    LOG.Error($"Unable to convert {ScBasePath}.{Display}.EnableAlarm value {newValue} to bool");
                }
                
            });
            SC.RegisterValueChangedCallback($"{ScBasePath}.AlarmRange", (newValue) =>
            {
                if (!double.TryParse(newValue.ToString(), out _alarmRange))
                {
                    _alarmRange = double.NaN;
                    LOG.Error($"Unable to convert {ScBasePath}.{Display}.AlarmRange value {newValue} to double");
                }
            });
            SC.RegisterValueChangedCallback($"{ScBasePath}.AlarmTime", (newValue) =>
            {
                if (!double.TryParse(newValue.ToString(), out _alarmTime))
                {
                    _alarmTime = int.MinValue;
                    LOG.Error($"Unable to convert {ScBasePath}.{Display}.AlarmRange value {newValue} to double");
                }
            });
            SC.RegisterValueChangedCallback($"{ScBasePath}.WarningRange", (newValue) =>
            {
                if (!double.TryParse(newValue.ToString(), out _warningRange))
                {
                    _warningRange = double.NaN;
                    LOG.Error($"Unable to convert {ScBasePath}.{Display}.WarningRange value {newValue} to double");
                }
            });
            SC.RegisterValueChangedCallback($"{ScBasePath}.WarningTime", (newValue) =>
            {
                if (!double.TryParse(newValue.ToString(), out _warningTime))
                {
                    _warningTime = int.MinValue;
                    LOG.Error($"Unable to convert {ScBasePath}.{Display}.WarningTime value {newValue} to double");
                }
            });
        }

        #endregion

        #region Properties

        #endregion

        #region Methods

        private bool ResetWarningChecker()
        {
            ErrWarningChecker.Reset(_warningTime);
            return true;
        }

        private bool ResetAlarmChecker()
        {
            ErrAlarmChecker.Reset(_alarmTime);
            return true;
        }

        /// <summary>
        /// 监测设置值和反馈值误差
        /// </summary>
        public virtual void MonitorSpFbError(bool allowErrorCheck, double setPoint, double feedback)
        {
            #region Service模式Bypass报警

            // 检查{Module}.IsService数据是否不存在，如果不存在，跳过下面步骤
            var dataPath = $"{Module}.IsService";
            var isServiceMode = false;

            lock (LockNonExistedServiceData)
            {
                if (!NonExistedServiceData.Contains(dataPath))
                {
                    // 检查所属Module是否为Service模式，如果是，不报警
                    var objIsService = DATA.Poll(dataPath);
                    if (objIsService is not bool bValue)
                    {
                        NonExistedServiceData.Add(dataPath);
                        LOG.Warning(
                            $"{UniqueName} Unable to poll data '{Module}.IsService', service mode detection will be discontinued in future monitoring");
                    }
                    else
                    {
                        isServiceMode = bValue;
                    }
                }
            }

            // 如果是Service模式，不报警
            if (isServiceMode)
            {
                // Service模式下暂停误差检测，避免退出Service后沿用旧的检测计时状态。
                // 不重置_alarmEvent/_warningEvent，保留历史报警状态。
                ErrWarningChecker.RST = true;
                ErrAlarmChecker.RST = true;
                return;
            }

            #endregion

            _alarmTrigger.CLK = DiAlarm != null && DiAlarm.Value;
            if (_alarmTrigger.Q)
            {
                if (!string.IsNullOrEmpty(AlarmText.Trim()))
                {
                    EV.PostAlarmLog(Module, AlarmText);
                }   
            }

            if (!_isEnableErrDetection || !allowErrorCheck)
            {
                ErrWarningChecker.RST = true;
                ErrAlarmChecker.RST = true;
                return;
            }


            // Out-of-rang Alarm detection
            var alarmLow = (setPoint * Math.Abs(100 - _alarmRange) / 100);
            var alarmHigh = (setPoint * Math.Abs(100 + _alarmRange) / 100);
            ErrAlarmChecker.Monitor(feedback, alarmLow, alarmHigh, _alarmTime);
            if (ErrAlarmChecker.Trig)
            {
                _alarmEvent.Description = $"{Display} feedback ({feedback:F1}{Unit}) out of range {alarmLow} - {alarmHigh} {Unit}  in {_alarmTime:F0} seconds";
                _alarmEvent.Set();
            }

            // 如果报Alarm，不再检查Warning。
            if (_alarmEvent.IsTriggered)
                return;

            // Out-of-rang warning detection
            var warnLow = (setPoint * Math.Abs(100 - _warningRange) / 100);
            var warnHigh = (setPoint * Math.Abs(100 + _warningRange) / 100);

            ErrWarningChecker.Monitor(feedback, warnLow, warnHigh, _warningTime);
            if (ErrWarningChecker.Trig)
            {
                _warningEvent.Description = $"{Display} feedback ({feedback:F1}{Unit}) out of range {warnLow} - {warnHigh} {Unit} in {_warningTime:F0} seconds";
                _warningEvent.Set();
            }

        }
        
        /// <inheritdoc/>
        public virtual bool Initialize()
        {
            return true;
        }

        /// <inheritdoc />
        public new void Monitor()
        {
            base.Monitor();
        }

        /// <inheritdoc/>
        public virtual void Terminate()
        {

        }

        /// <inheritdoc />
        public virtual void Reset()
        {
            _alarmTrigger.RST = true;
            ErrAlarmChecker.Reset(_alarmTime);
            ErrWarningChecker.Reset(_warningTime);
            _warningEvent.Reset();
            _alarmEvent.Reset();
           
        }

        #endregion
    }
}
