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.SCCore;
using Aitex.Core.Util;
using System;
using System.Xml;

namespace Aitex.Core.RT.Device.Devices
{
    public class IoSensor : BaseDevice, IDevice
    {
        private DIAccessor _di = null;
        private DOAccessor _do = null;

        public DIAccessor SensorDI => _di;
        public DOAccessor SensorDO => _do;

        private R_TRIG _trigTextOut = new R_TRIG();

        private bool _textOutTrigValue;

        public bool AlarmTrigValue
        {
            get { return _textOutTrigValue && !string.IsNullOrEmpty(_alarmText); }
        }

        private string _warningText;
        private string _alarmText;
        private string _infoText;

        private bool _disabledInServiceMode = false;
        private bool _disabledFromSc = false;
        private readonly R_TRIG _rTrigDisabledInSc = new();

        public Action WarningAction
        {
            get;
            set;
        }

        public event Action<IoSensor, bool> OnSignalChanged;

        public bool Value
        {
            get
            {
                if (_di != null)
                    return _di.Value;

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

                return false;
            }
        }

        private AITSensorData DeviceData
        {
            get
            {
                AITSensorData data = new AITSensorData()
                {
                    DeviceName = Name,
                    DeviceSchematicId = DeviceID,
                    DisplayName = Display,
                    Value = Value,
                };

                return data;
            }
        }

        private bool _previous;

        public IoSensor(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
        {
            
            _di = ParseDiNode("di", node, ioModule);// IO.DI[node.GetAttribute("di")];
            _do = ParseDoNode("do", node, ioModule);

            _infoText = node.GetAttribute("infoText");
            _warningText = node.GetAttribute("warningText");
            _alarmText = node.GetAttribute("alarmText");
            _disabledInServiceMode = (node.GetAttribute("DisInServiceMode") ?? "") == "true";
            if(_disabledInServiceMode)
                LOG.Warning($"{UniqueName} will be disabled in service mode");

            if (Name == "SensorMFC07Offline" || Name == "SensorMFC08Offline")
            {
                if (!SC.GetValue<bool>($"PM.{Module}.TMAEnable"))
                {
                    _alarmText = "";
                }
            }
            _textOutTrigValue = Convert.ToBoolean(string.IsNullOrEmpty(node.GetAttribute("textOutTrigValue")) ? "false" : node.GetAttribute("textOutTrigValue"));

            if (!string.IsNullOrEmpty(node.GetAttribute("scBasePath"))) //配置ScBasePath时，使用配置信息
            {
                GetTextValue();

                SC.RegisterValueChangedCallback($"{ScBasePath}.{Name}.AlarmType", (obj) => GetTextValue());
                SC.RegisterValueChangedCallback($"{ScBasePath}.{Name}.Text", (obj) => GetTextValue());
                SC.RegisterValueChangedCallback($"{ScBasePath}.{Name}.TrigValue", (obj) => GetTextValue());
                SC.RegisterValueChangedCallback($"{ScBasePath}.{Name}.IsEnable", (obj) => GetTextValue());

                // e.g. TM.IoSensor.{Name}.Disable
                var scPath = $"{ScBasePath}.{Name}.Disable";
                _disabledFromSc = SC.SafeGetValue(scPath, false);
                SC.RegisterValueChangedCallback(scPath, (obj) =>
                {
                    if (obj is bool isDisable)
                        _disabledFromSc = isDisable;
                    else
                        _disabledFromSc = false;

                });
            }
        }

        private string GetTrigValue(XmlElement node, string xmlNodeName, string scName)
        {
            var xmlNodeValue = node.GetAttribute(xmlNodeName);

            return string.IsNullOrEmpty(xmlNodeValue) ? SC.GetStringValue($"{ScBasePath}.{Name}.{scName}") : xmlNodeValue;
        }

        private void GetTextValue()
        {
            var typ = SC.GetStringValue($"{ScBasePath}.{Name}.AlarmType");
            var text = SC.GetStringValue($"{ScBasePath}.{Name}.Text");
            _textOutTrigValue = SC.GetValue<bool>($"{ScBasePath}.{Name}.TrigValue");
            if (!SC.SafeGetValue($"{ScBasePath}.{Name}.IsEnable", false))
                text = "";

            _ = typ switch
            {
                "Info" => _infoText = text,
                "Warning" => _warningText = text,
                "Alarm" => _alarmText = text,
                _ => ""
            };

            
        }

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

            return true;
        }

        public void Terminate()
        {
        }

        protected override void HandleMonitor()
        {
            try
            {
                #region 系统配置中禁用了该Sensor

                if (_disabledFromSc)
                {
                    _rTrigDisabledInSc.CLK = true;
                    if (_rTrigDisabledInSc.Q)
                        LOG.Warning($"{UniqueName} is disabled in system config, please check '{ScBasePath}.{Name}.Disabled'");
                    return; // 当前IoSenser设置为不侦测报警
                }
                else
                {
                    _rTrigDisabledInSc.CLK = false;
                }

                #endregion

                #region Service模式Bypass报警

                if (_disabledInServiceMode)
                {
                    // 检查{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)
                    {
                        return;
                    }
                }

                #endregion

                _trigTextOut.CLK = (Value == _textOutTrigValue);

                if (_trigTextOut.Q)
                {
                    if (WarningAction != null)
                    {
                        WarningAction();
                    }
                    else if (!string.IsNullOrEmpty(_warningText.Trim()))
                    {
                        EV.PostWarningLog(Module, _warningText);
                    }
                    else if (!string.IsNullOrEmpty(_alarmText.Trim()))
                    {
                        EV.PostAlarmLog(Module, _alarmText);
                    }
                    else if (!string.IsNullOrEmpty(_infoText.Trim()))
                    {
                        EV.PostInfoLog(Module, _infoText);
                    }
                }

                if (_previous != Value)
                {
                    if (OnSignalChanged != null)
                        OnSignalChanged(this, Value);
                    _previous = Value;
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
        }

        public void Reset()
        {
            _trigTextOut.RST = true;
        }
    }
}