using System;
using System.Xml;
using Aitex.Core.Common.DeviceData;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device.Unit;
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;

namespace Aitex.Core.RT.Device.Devices
{
    public class IoPressure : ErrorDetectableBaseDevice, IPressureMeter
    {
        public double FeedBack => _isFloatAioType ? _aiValue.Value  : _aiValue.Value;

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

        public double OpenDegree
        {
            get
            {
                if (_aiOpenDegree != null)
                {
                    return _aiOpenDegree.Value;
                }

                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;
                }
            }
        }


        public double DefaultValue
        {
            get
            {
                return _scDefaultSetPoint == null ? 1000 : _scDefaultSetPoint.DoubleValue;
            }
        }

        private AITPressureMeterData DeviceData
        {
            get
            {
                AITPressureMeterData data = new AITPressureMeterData()
                {
                    Module =Module,
                    DeviceName = Name,
                    DeviceSchematicId = DeviceID,
                    DisplayName = Display,
                    SetPoint = SetPoint,
                    FeedBack = FeedBack,
                    Unit = Unit,
                    FormatString = _formatString,
                    DisplayWithUnit = true,
                    IsError = IsError,
                    IsWarning = IsWarning,
                    //Precision = Precision,
                    Scale = MaxPressure,
                    ActMode = ActMode,
                    SetMode = SetMode,
                    OpenDegree = OpenDegree,
                    DefaultValue = DefaultValue
                };

                return data;
            }
        }

        public bool IsWarning => ErrWarningChecker.Result;

        public bool IsError
        {
            get
            {
                return ErrAlarmChecker.Result;
            }
        }

        public double MaxPressure => _scMaxValue?.DoubleValue ?? 100;

        public bool IsOutOfRange => (FeedBack > MaxPressure);

        private readonly AIAccessor _aiValue = null;
        private readonly AOAccessor _aoValue = null;
        private readonly AIAccessor _aiActMode;
        private readonly AOAccessor _aoSetMode;
        private readonly AIAccessor _aiOpenDegree;

        private readonly string _formatString = "0.0";

        private SCConfigItem _scUnitValue;
        private readonly SCConfigItem _scMaxValue;
        private readonly SCConfigItem _scDefaultSetPoint;
 
        private readonly bool _isFloatAioType = false;

        private readonly DeviceTimer _rampTimer = new DeviceTimer();
        private double _rampTarget;
        private double _rampInitValue;
        private int _rampTime;
        private string _infoText = "";

        public IoPressure(string module, XmlElement node, string ioModule = ""): base(module, node, ioModule)
        {
            _isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float");

            _aiValue = ParseAiNode("aiValue", node, ioModule);
            _aoValue = ParseAoNode("aoValue", node, ioModule);
            _aiActMode = ParseAiNode("aiActMode", node, ioModule);
            _aoSetMode = ParseAoNode("aoSetMode", node, ioModule);
            _aiOpenDegree = ParseAiNode("aiOpenDegree", node, ioModule);
           
            if (node.HasAttribute("formatString"))
                _formatString = string.IsNullOrEmpty(node.GetAttribute("formatString")) ? "F5" : node.GetAttribute("formatString");
            
            _scMaxValue = ParseScNode("", node, "", $"{ScBasePath}.{Display}.Scale");
            _scDefaultSetPoint = ParseScNode("scDefaultSetPoint", node, ioModule, $"{ScBasePath}.{Display}.DefaultSetPoint");
        }

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

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

                if (target < 0 || target > MaxPressure)
                {
                    reason = $"set {Display} value {target} out of range [0, {MaxPressure}] {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);
            return true;
        }

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

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

            }
            reason = $"{Display} set to {mode}";
            return true;
        }


        public void Ramp(double target)
        {
            _aoValue.Value = (float)target;
        }

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

        public override void Terminate()
        {
            _aoValue.Value = (float)DefaultValue;
        }

        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;
                }
            }

        }

        protected override void HandleMonitor()
        {
            MonitorRamping();

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