using System;
using System.Diagnostics;
using System.Xml;
using Aitex.Core.Common.DeviceData;
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 MECF.Framework.Common.Event;
using MECF.Framework.Common.SicMath;

namespace Aitex.Core.RT.Device.Devices
{
    public abstract class IoSensorAnalog : BaseDevice, IDevice
    {
        #region Variables

        private readonly AIAccessor _ai;
        private readonly AOAccessor _ao;
        private readonly DOAccessor _doWarningLow;
        private readonly DOAccessor _doWarningHigh;
        private readonly DOAccessor _doAlarmLow;
        private readonly DOAccessor _doAlarmHigh;

        private readonly bool _doActiveLevel;

        private readonly TimeDomainArithmeticMeanFilter _tdamFilter;

        private int _warningDelaySec;
        private int _alarmDelaySec;

        protected readonly string _scNameWarnLow; 
        protected readonly string _scNameWarnHigh;
        protected readonly string _scNameAlarmLow;
        protected readonly string _scNameAlarmHigh; 
        protected readonly string _scNameWarnDelay; 
        protected readonly string _scNameAlarmDelay;
        protected readonly string _scNameFilterDuration;

        protected double _warningLow;
        protected double _warningHigh;
        protected double _alarmLow;
        protected double _alarmHigh;
        protected int _filterDurationS;
        
        /// <summary>
        /// 误差超限警告检查器
        /// </summary>
        protected readonly ToleranceChecker _warningChecker;
        
        /// <summary>
        /// 误差超限报警检查器
        /// </summary>
        protected readonly ToleranceChecker _alarmChecker;
        
        protected readonly AlarmEventItem _alarmEvent;
        protected readonly AlarmEventItem _warningEvent;

        private readonly AITSensorAnalogData _devData;
        
        #endregion

        /// <summary>
        /// 获取Sensor实时反馈值。
        /// <para>注意：如果绑定的AI或者AO为空，返回<see cref="Double.NaN"/></para>
        /// </summary>
        public double Value
        {
            get
            {
                if (_ai != null)
                    return _ai.Value;

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

                return double.NaN;
            }
        }

        protected AITSensorAnalogData DeviceData
        {
            get
            {
                _devData.DeviceName = Name;
                _devData.DeviceSchematicId = DeviceID;
                _devData.DisplayName = Display;
                _devData.WarningLimitLow = _warningLow;
                _devData.WarningLimitHigh = _warningHigh;
                _devData.AlarmLimitLow = _alarmLow;
                _devData.AlarmLimitHigh = _alarmHigh;
                _devData.Value = Value;
                _devData.IsWarning = _warningEvent.IsTriggered;
                _devData.IsAlarm = _alarmEvent.IsTriggered;
                return _devData;
            }
        }



        public IoSensorAnalog(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
        {
            _devData = new AITSensorAnalogData();
            
            var scWarningLow = node.GetAttribute("scWarningLow");
            var scWarningHigh = node.GetAttribute("scWarningHigh");
            var scAlarmLow = node.GetAttribute("scAlarmLow");
            var scAlarmHigh = node.GetAttribute("scAlarmHigh");
            var scFilterDuration = node.GetAttribute("scFilterDuration");
            
            _scNameWarnLow = (string.IsNullOrEmpty(scWarningLow) || string.IsNullOrWhiteSpace(scWarningLow))? "" : $"{ScBasePath}.{scWarningLow}";
            _scNameWarnHigh = (string.IsNullOrEmpty(scWarningHigh) || string.IsNullOrWhiteSpace(scWarningHigh)) ? "" : $"{ScBasePath}.{scWarningHigh}";
            _scNameAlarmLow = (string.IsNullOrEmpty(scAlarmLow) || string.IsNullOrWhiteSpace(scAlarmLow)) ? "" : $"{ScBasePath}.{scAlarmLow}";
            _scNameAlarmHigh = (string.IsNullOrEmpty(scAlarmHigh) || string.IsNullOrWhiteSpace(scAlarmHigh)) ? "" : $"{ScBasePath}.{scAlarmHigh}";
            _scNameWarnDelay = $"{ScBasePath}.WarningDelay";
            _scNameAlarmDelay = $"{ScBasePath}.AlarmDelay";
            _scNameFilterDuration = (string.IsNullOrEmpty(scFilterDuration) || string.IsNullOrWhiteSpace(scFilterDuration)) ? "" : $"{ScBasePath}.{scFilterDuration}";
            
            _ai = ParseAiNode("ai", node, ioModule);
            _ao = ParseAoNode("ao", node, ioModule);

            // DO输出的有效电平，当Warn或Alarm触发时，相应的DO输出此电平
            var activeLevel = node.GetAttribute("doActiveLevel");
            if (bool.TryParse(activeLevel, out var bLevel))
                _doActiveLevel = bLevel;
            else
                _doActiveLevel = true;

            _doWarningLow = ParseDoNode("doWarningLow", node, ioModule);
            _doWarningHigh = ParseDoNode("doWarningHigh", node, ioModule);
            _doAlarmLow = ParseDoNode("doAlarmLow", node, ioModule);
            _doAlarmHigh = ParseDoNode("doAlarmHigh", node, ioModule);

            // AI和AO不能同时配置
            Debug.Assert((_ai != null && _ao == null) || (_ai == null && _ao != null), $"{Name},{Display} Incorrect configuration.");
            _warningChecker = new ToleranceChecker();
            _alarmChecker = new ToleranceChecker();
            _warningEvent = SubscribeAlarm($"{Module}.{Display}.ToleranceWarning", "", ResetWarningChecker,
                EventLevel.Warning);
            _alarmEvent = SubscribeAlarm($"{Module}.{Display}.ToleranceAlarm", "", ResetAlarmChecker);
            
            _tdamFilter = new TimeDomainArithmeticMeanFilter(_filterDurationS);

            LoadConfig();
        }
        
        public virtual bool Initialize()
        {
            // DATA.Subscribe($"{Module}.{Name}.Value", () => Value);
            return true;
        }

        public virtual void Terminate()
        {
        }

        protected override void HandleMonitor()
        {
            try
            {
                // 对输入量滤波
                _tdamFilter.Feed(Value);
                var filtered = _tdamFilter.Filter();

                _alarmChecker.Monitor(filtered, _alarmLow, _alarmHigh, _alarmDelaySec);
                if (_alarmChecker.Trig)
                {
                    // 根据条件设定指定的DO
                    if (filtered < _alarmLow)
                    {
                        _alarmEvent.Description =
                            $"{Display} feedback ({filtered:F1}{Unit}) is below {_alarmLow:F1} in {_alarmDelaySec:F0} seconds";
                        _doAlarmLow?.SetValue(_doActiveLevel, out _);
                        _doAlarmHigh?.SetValue(!_doActiveLevel, out _);

                        _alarmEvent.Set();
                    }
                    else if (filtered > _alarmHigh)
                    {
                        _alarmEvent.Description =
                            $"{Display} feedback ({filtered:F1}{Unit}) is above {_alarmHigh:F1} in {_alarmDelaySec:F0} seconds";
                        _doAlarmLow?.SetValue(!_doActiveLevel, out _);
                        _doAlarmHigh?.SetValue(_doActiveLevel, out _);

                        _alarmEvent.Set();
                    }
                }
            
                // 如果报Alarm，不再检查Warning。
                if (_alarmEvent.IsTriggered)
                    return;
                
                // Out-of-rang warning detection
                _warningChecker.Monitor(filtered, _warningLow, _warningHigh, _warningDelaySec);
                if (_warningChecker.Trig)
                {
                    // 根据条件设定指定的DO
                    if (filtered < _warningLow)
                    {
                        _warningEvent.Description =
                            $"{Display} feedback ({filtered:F1}{Unit}) is below {_warningLow:F1} in {_warningDelaySec:F0} seconds";
                        _doWarningLow?.SetValue(_doActiveLevel, out _);
                        _doWarningHigh?.SetValue(!_doActiveLevel, out _);
                        _warningEvent.Set();
                    }
                    else if (filtered > _warningHigh)
                    {
                        _warningEvent.Description =
                            $"{Display} feedback ({filtered:F1}{Unit}) is above {_warningHigh:F1} in {_warningDelaySec:F0} seconds";
                        _doWarningLow?.SetValue(!_doActiveLevel, out _);
                        _doWarningHigh?.SetValue(_doActiveLevel, out _);

                        _warningEvent.Set();
                    }
                }
                
            }
            catch (Exception ex)
            {
                LOG.Write(ex);

            }
        }

        public virtual void Reset()
        {
            _warningChecker.Reset(_warningDelaySec);
            _alarmChecker.Reset(_alarmDelaySec);
            _warningEvent.Reset();
            _alarmEvent.Reset();
            
            _doAlarmLow?.SetValue(!_doActiveLevel, out _);
            _doAlarmHigh?.SetValue(!_doActiveLevel, out _);
            
            
        }

        #region Private Methods

        
        private bool ResetWarningChecker()
        {
            _warningChecker.Reset(_warningDelaySec);
            _doWarningLow?.SetValue(!_doActiveLevel, out _);
            _doWarningHigh?.SetValue(!_doActiveLevel, out _);
            return true;
        }

        private bool ResetAlarmChecker()
        {
            _alarmChecker.Reset(_alarmDelaySec);
            _doAlarmLow?.SetValue(!_doActiveLevel, out _);
            _doAlarmHigh?.SetValue(!_doActiveLevel, out _);
            return true;
        }

        protected virtual void LoadConfig()
        {
            if (!string.IsNullOrEmpty(_scNameWarnLow))
            {
                _warningLow = SC.SafeGetValue(_scNameWarnLow, double.NegativeInfinity);
                SC.RegisterValueChangedCallback(_scNameWarnLow, v => _warningLow = (double)v);
            }

            if (!string.IsNullOrEmpty(_scNameWarnHigh))
            {
                _warningHigh = SC.SafeGetValue(_scNameWarnHigh, double.PositiveInfinity);
                SC.RegisterValueChangedCallback(_scNameWarnHigh, v => _warningHigh = (double)v);
            }

            if (!string.IsNullOrEmpty(_scNameAlarmLow))
            {
                _alarmLow = SC.SafeGetValue(_scNameAlarmLow, double.NegativeInfinity);
                SC.RegisterValueChangedCallback(_scNameAlarmLow, v => _alarmLow = (double)v);
            }

            if (!string.IsNullOrEmpty(_scNameAlarmHigh))
            {
                _alarmHigh = SC.SafeGetValue(_scNameAlarmHigh, double.PositiveInfinity);
                SC.RegisterValueChangedCallback(_scNameAlarmHigh, v => _alarmHigh = (double)v);
            }

            if (!string.IsNullOrEmpty(_scNameFilterDuration))
            {
                _filterDurationS = SC.SafeGetValue(_scNameFilterDuration, 0);
                _tdamFilter.FilterDurationInSec = _filterDurationS;
                SC.RegisterValueChangedCallback(_scNameFilterDuration, v =>
                {
                    _filterDurationS = (int)v;
                    _tdamFilter.FilterDurationInSec = _filterDurationS;
                });
            }

            _warningDelaySec = SC.GetValue(_scNameWarnDelay, 0);
            SC.RegisterValueChangedCallback(_scNameWarnDelay, v => _warningDelaySec = (int)v);

            _alarmDelaySec = SC.GetValue(_scNameAlarmDelay, 0);
            SC.RegisterValueChangedCallback(_scNameAlarmDelay, v => _alarmDelaySec = (int)v);
            
        }

        #endregion
    }
}