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.Log;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.Common.Event;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Pumps;

namespace Aitex.Core.RT.Device.Unit
{
    /// <summary>
    /// 泵2对应的IO要求：
    /// diRunning   【如果未定义，默认泵为常开】
    /// diOverloadAlarm【可以不定义】
    ///
    /// doResetError【可以不定义】
    /// doStartStop【可以不定义】
    /// doPowerOn【可以不定义，默认主电源常供】
    /// </summary>
    public class IoPump2 : BaseDevice, IDevice, IPump
    {
        public bool IsRunning
        {
            get
            {
                if (_diRunning != null)
                    return _diRunning.Value;
                return true;
            }
        }

        public bool IsPumpOverloadAlarm
        {
            get
            {
                return _diOverloadAlarm != null && _diOverloadAlarm.Value;
            }
        }

        public bool ResetErrorSetPoint
        {
            get
            {
                return _doResetError != null && _doResetError.Value;
            }
            set
            {
                if (_doResetError!=null)
                    _doResetError.Value = value;
            }
        }

        public bool StartSetPoint
        {
            get
            {
                return _doStart != null && _doStart.Value;
            }
            set
            {
                if (_doStart != null)
                    _doStart.Value = value;
            }
        }
 
        public bool MainPowerOnSetPoint
        {
            get
            {
                return _doPowerOn != null && _doPowerOn.Value;
            }
            set
            {
                if (_doPowerOn != null)
                    _doPowerOn.Value = value;
            }
        }

        private AITPumpData DeviceData
        {
            get
            {
                AITPumpData data = new AITPumpData()
                {
                    DeviceName = Name,
                    DeviceSchematicId = DeviceID,
                    DisplayName = Display,
                    DeviceModule = Module,
                    Module = Module,

                    IsOn = IsRunning,
                    IsError = HasAlarm,
                    IsOverLoad = IsPumpOverloadAlarm,
                };

                return data;
            }
        }
 

        private DIAccessor _diRunning = null;
        private DIAccessor _diOverloadAlarm;
 
        private DOAccessor _doStart;
        private DOAccessor _doPowerOn;
        private DOAccessor _doResetError;

        private R_TRIG _trigOverload = new R_TRIG();

        public AlarmEventItem AlarmOverload { get; set; }
        public AlarmEventItem AlarmFailedStartStop { get; set; }

        private Stopwatch _timerResetError = new Stopwatch();
        private Stopwatch _timerStartStop = new Stopwatch();

        private SCConfigItem _scStartTimeout;
        private SCConfigItem _scResetErrorTimeout;

        public IoPump2(string module, XmlElement node, string 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");

            _diRunning = ParseDiNode("diRunning", node, ioModule);
            _diOverloadAlarm = ParseDiNode("diOverloadAlarm", node, ioModule);
 
            _doStart = ParseDoNode("doStartStop", node, ioModule);
            _doPowerOn = ParseDoNode("doPowerOn", node, ioModule);
            _doResetError = ParseDoNode("doReset", node, ioModule);

            string scBasePath = node.GetAttribute("scBasePath");
            if (string.IsNullOrEmpty(scBasePath))
                scBasePath = $"{Module}.{Name}";
            else
            {
                scBasePath = scBasePath.Replace("{module}", Module);
            }

            _scStartTimeout = ParseScNode("", node, ioModule, $"{scBasePath}.{Name}.StartTimeout");
            _scResetErrorTimeout = ParseScNode("", node, ioModule, $"{scBasePath}.{Name}.ResetErrorTimeout");

            System.Diagnostics.Debug.Assert(_scResetErrorTimeout != null, "SC not defined");
            System.Diagnostics.Debug.Assert(_scStartTimeout != null, "SC not defined");
        }


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

            DATA.Subscribe($"{Module}.{Name}.IsOverload", () => IsPumpOverloadAlarm);
            DATA.Subscribe($"{Module}.{Name}.IsRunning", () => IsRunning);
            DATA.Subscribe($"{Module}.{Name}.StartSetPoint", () => StartSetPoint);

            OP.Subscribe($"{Module}.{Name}.{AITPumpOperation.SetOnOff}" , SetPumpOnOff);
            OP.Subscribe($"{Module}.{Name}.{AITPumpOperation.PumpOn}", SetPumpOn);
            OP.Subscribe($"{Module}.{Name}.{AITPumpOperation.PumpOff}", SetPumpOff);

            AlarmOverload = SubscribeAlarm($"{Module}.{Name}.OverloadAlarm", "", ResetOverload);
            AlarmFailedStartStop = SubscribeAlarm($"{Module}.{Name}.FailedStartStopAlarm", "", null);

            return true;
        }

        private bool SetPumpOn(out string reason, int time, object[] param)
        {
            return SetPump(out reason, time, true);
        }

        private bool SetPumpOff(out string reason, int time, object[] param)
        {
            return SetPump(out reason, time, false);
        }

        private bool SetPumpOnOff(out string reason, int time, object[] param)
        {
            return SetPump(out reason, time, Convert.ToBoolean((string)param[0]));
        }

        public bool SetMainPowerOnOff(bool isOn, out string reason)
        {
            MainPowerOnSetPoint = isOn;
            reason = string.Empty;
            return true;
        }

        public bool ResetOverload()
        {
            if (IsPumpOverloadAlarm)
            {
                if (!ResetErrorSetPoint)
                {
                    _timerResetError.Restart();
                    ResetErrorSetPoint = true;
                }
            }

            return !IsPumpOverloadAlarm;
        }

        public bool SetPump(out string reason, int time, bool isOn)
        {
            if (HasAlarm)
            {
                reason = $"{Display} Has active alarm, reset error first.";
                return false;
            }

            reason = string.Empty;

            _timerStartStop.Restart();
            StartSetPoint = isOn;

            return true;
        }
 
        public void Terminate()
        {
        }
 
        public void Monitor()
        {
            try
            {
                if (StartSetPoint != IsRunning)
                {
                    if (_timerStartStop.IsRunning &&
                        _timerStartStop.ElapsedMilliseconds > _scStartTimeout.IntValue * 1000)
                    {
                        var onoff = StartSetPoint ? "start up" : "shut down";
                        _timerStartStop.Stop();
                        AlarmFailedStartStop.Description =
                            $"{Display} can not {onoff} in {_scStartTimeout.IntValue} seconds";
                        AlarmFailedStartStop.Set();
                        StartSetPoint = IsRunning;
                    }
                }

                _trigOverload.CLK = IsPumpOverloadAlarm;
                if (_trigOverload.Q)
                {
                    AlarmOverload.Set("Pump Overload or Error");
                    StartSetPoint = false;
                }

                if (ResetErrorSetPoint)
                {
                    if (!IsPumpOverloadAlarm)
                    {
                        AlarmOverload.Reset();
                        ResetErrorSetPoint = false;
                        _timerResetError.Stop();
                    }

                    if (IsPumpOverloadAlarm && _timerResetError.IsRunning &&
                        _timerResetError.ElapsedMilliseconds > _scResetErrorTimeout.IntValue * 1000)
                    {
                        _timerResetError.Stop();
                        EV.PostWarningLog(Module, $"Can not reset {Display} error in {_scResetErrorTimeout.IntValue} seconds");
                        ResetErrorSetPoint = false;
                    }
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
        }

        public void Reset()
        {
            AlarmFailedStartStop.Reset();
            AlarmOverload.Reset();


        }
    }
}
