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 Aitex.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Xml;

namespace Aitex.Core.RT.Device.Devices
{
    public class IoThrottleValve2 : BaseDevice, IDevice
    {
        public PressureCtrlMode PressureMode
        {
            get
            {
                if (_aoPressureMode != null)
                {
                    foreach (var ioMode in _ctrlModeIoValue)
                    {
                        if (_isFloatAioType)
                        {
                            if ((int)_aoPressureMode.Value == ioMode.Value)
                            {
                                return ioMode.Key;
                            }
                        }
                        else
                        {
                            if (_aoPressureMode.Value == ioMode.Value)
                            {
                                return ioMode.Key;
                            }
                        }

                    }
                    return PressureCtrlMode.Undefined;
                }

                return PressureCtrlMode.Undefined;
            }
            set
            {
                if (_isFloatAioType)
                    _aoPressureMode.Value = _ctrlModeIoValue[value];
                else
                {
                    _aoPressureMode.Value = (short)_ctrlModeIoValue[value];
                }
            }
        }

        public PressureCtrlMode ControlModeFeedback
        {
            get
            {
                if (_aiPressureMode != null)
                {
                    foreach (var ioMode in _ctrlModeIoValue)
                    {
                        if (_isFloatAioType)
                        {
                            if ((int)_aiPressureMode.Value == ioMode.Value)
                            {
                                return ioMode.Key;
                            }
                        }
                        else
                        {
                            if (_aiPressureMode.Value == ioMode.Value)
                            {
                                return ioMode.Key;
                            }
                        }

                    }
                    return PressureCtrlMode.Undefined;
                }

                return PressureCtrlMode.Undefined;
            }
        }

        public bool TVValveEnable
        {
            get
            {
                if (_doTVValveEnable!=null)
                {
                    return _doTVValveEnable.Value;
                }
                return false;
            }
        }

        public float PositionSetpoint
        {
            get
            {
                return _aoPositionSetPoint == null ? 0 : (_isFloatAioType? _aoPositionSetPoint.Value: _aoPositionSetPoint.Value);
            }
            set
            {
                if (_isFloatAioType)
                {
                    _aoPositionSetPoint.Value = value;
                }
                else
                {
                    _aoPositionSetPoint.Value = (short)value;
                }
            }
        }

        public float PositionFeedback
        {
            get
            {
                return _aiPositionFeedback == null ? 0 : (_isFloatAioType ? _aiPositionFeedback.Value: _aiPositionFeedback.Value);
            }
        }

        public float PressureSetpoint
        {
            get
            {
                return _aoPressureSetPoint == null ? 0 : (_isFloatAioType ? _aoPressureSetPoint.Value:_aoPressureSetPoint.Value);
            }
            set
            {
                if (_isFloatAioType)
                {
                    _aoPressureSetPoint.Value = value;
                }
                else
                {
                    _aoPressureSetPoint.Value = (short)value;
                }
            }
        }

        public float PressureFeedback
        {
            get
            {
                return _aiPressureFeedback == null ? 0 : (_isFloatAioType ? _aiPressureFeedback.Value:_aiPressureFeedback.Value);
            }
        }

        private float _pressureSetPointByUse;
        public float PressureSetpointByUse
        {
            get
            {
                return _pressureSetPointByUse;
            }
            set
            {
                _pressureSetPointByUse = value;
            }
        }

        private float _positionSetPointByUse;
        public float PositionSetpointByUse
        {
            get
            {
                return _positionSetPointByUse;
            }
            set
            {
                _positionSetPointByUse = value;
            }
        }

        public float PositionDefaultValue
        {
            get
            {
                if (_positionDefaultValue != null)
                {
                    return (float)_positionDefaultValue.DoubleValue;
                }
                else
                {
                    return 0;
                }
            }
        }

        public float PressureDefaultValue
        {
            get
            {
                if (_pressureDefaultValue != null)
                {
                    return (float)_pressureDefaultValue.DoubleValue;
                }
                else
                {
                    return 1100;
                }
            }
            //set
            //{
            //    _pressureDefaultValue2 = value;
            //}
        }

        public float ThrottlePositionModeMaxPressure
        {
            get
            {
                if (_throttlePositionModeMaxPressure != null)
                {
                    return (float)_throttlePositionModeMaxPressure.DoubleValue;
                }
                else
                {
                    return 400;
                }
            }
        }


        private SCConfigItem _positionDefaultValue;
        private SCConfigItem _pressureDefaultValue;
        private SCConfigItem _throttlePositionModeMaxPressure;

        private DeviceTimer _closeTimer = new DeviceTimer();
        private DeviceTimer _rampTimer = new DeviceTimer();
        private double _rampTarget;
        private double _rampInitValue;
        private int _rampTime;


        private DeviceTimer _rampTimer1 = new DeviceTimer();
        private double _rampTarget1;
        private double _rampInitValue1;
        private int _rampTime1;

        private DeviceTimer _resetTimer = new DeviceTimer();

        

        private AITThrottleValveData DeviceData
        {
            get
            {
                AITThrottleValveData data = new AITThrottleValveData()
                {
                    Module = Module,
                    DeviceName = Name,
                    DeviceSchematicId = DeviceID,
                    DisplayName = Display,
                    Mode = (int)ControlModeFeedback,
                    PositionFeedback = PositionFeedback,
                    PositionSetPoint = PositionSetpointByUse,
                    PositionSetPointCurrent = PositionSetpoint,

                    PressureFeedback = PressureFeedback,
                    PressureSetPoint = PressureSetpointByUse,
                    PressureSetPointCurrent = PressureSetpoint,

                    TVEnable = TVValveEnable,

                    MaxValuePosition = 100,
                    MaxValuePressure = _unitPressure == "mbar" ? 1500 : 1500,

                    UnitPressure = _unitPressure,
                };

                return data;
            }
        }

        private AIAccessor _aiPressureMode = null;

        private AOAccessor _aoPressureMode = null;

        private AIAccessor _aiPositionFeedback = null;

        private AOAccessor _aoPositionSetPoint = null;

        private AIAccessor _aiPressureFeedback = null;

        private AOAccessor _aoPressureSetPoint = null;

        private DOAccessor _doTVValveEnable = null;

        private DOAccessor _doTVReset = null;

        private string _unitPressure;

        private Dictionary<PressureCtrlMode, int> _ctrlModeIoValue = new Dictionary<PressureCtrlMode, int>();
        
        private bool _isFloatAioType = false;
        private R_TRIG _setTvCloseTrig = new R_TRIG();

        private IoValve _epv2;

        public IoThrottleValve2(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");

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

            _unitPressure = node.GetAttribute("unit");
            _isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float");

            _aiPressureMode = ParseAiNode("aiStatus", node, ioModule);
            _aoPressureMode = ParseAoNode("aoPressureMode", node, ioModule);

            _aiPositionFeedback = ParseAiNode("aiPositionFeedback", node, ioModule);
            _aoPositionSetPoint = ParseAoNode("aoPositionSetPoint", node, ioModule);
            _aiPressureFeedback = ParseAiNode("aiPressureFeedback", node, ioModule);
            _aoPressureSetPoint = ParseAoNode("aoPressureSetPoint", node, ioModule);
            _doTVValveEnable = ParseDoNode("doTVValveEnable", node, ioModule);
            _doTVReset= ParseDoNode("doTVReset", node, ioModule);

            _positionDefaultValue = ParseScNode("positionDefaultValue", node, ioModule, $"{scBasePath}.ThrottlePositionDefaultValue");
            _pressureDefaultValue = ParseScNode("pressureDefaultValue", node, ioModule, $"{scBasePath}.ThrottlePressureDefaultValue");
            _throttlePositionModeMaxPressure = ParseScNode("throttlePositionModeMaxPressure", node, ioModule, $"{scBasePath}.ThrottlePositionModeMaxPressure");

            
            EnumLoop<PressureCtrlMode>.ForEach(x => _ctrlModeIoValue[x] = -1);

            if (!string.IsNullOrEmpty(node.GetAttribute("modeValuePressure")))
            {
                _ctrlModeIoValue[PressureCtrlMode.TVPressureCtrl] = int.Parse(node.GetAttribute("modeValuePressure"));
            }
            if (!string.IsNullOrEmpty(node.GetAttribute("modeValuePosition")))
            {
                _ctrlModeIoValue[PressureCtrlMode.TVPositionCtrl] = int.Parse(node.GetAttribute("modeValuePosition"));
            }
            if (!string.IsNullOrEmpty(node.GetAttribute("modeValueOpen")))
            {
                _ctrlModeIoValue[PressureCtrlMode.TVOpen] = int.Parse(node.GetAttribute("modeValueOpen"));
            }
            if (!string.IsNullOrEmpty(node.GetAttribute("modeValueClose")))
            {
                _ctrlModeIoValue[PressureCtrlMode.TVClose] = int.Parse(node.GetAttribute("modeValueClose"));
            }
            //PressureDefaultValue = (float)_pressureDefaultValue.DoubleValue;
        }


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

            DATA.Subscribe($"{Module}.{Name}.PositionSetPoint", () => PositionSetpoint);
            DATA.Subscribe($"{Module}.{Name}.PositionFeedback", () => PositionFeedback);
            DATA.Subscribe($"{Module}.{Name}.PressureSetPoint", () => PressureSetpoint);
            DATA.Subscribe($"{Module}.{Name}.PressureFeedback", () => PressureFeedback);
            DATA.Subscribe($"{Module}.{Name}.Mode", () => ControlModeFeedback);
            DATA.Subscribe($"{Module}.{Name}.TVValveEnable", () => TVValveEnable);

            OP.Subscribe($"{Module}.{Name}.{AITThrottleValveOperation.SetMode}", SetMode);

            OP.Subscribe($"{Module}.{Name}.{AITThrottleValveOperation.SetPosition}", SetPosition);

            OP.Subscribe($"{Module}.{Name}.{AITThrottleValveOperation.SetPressure}", SetPressure);

            OP.Subscribe($"{Module}.{Name}.SetPositionToZero", SetPositionToZero);

            OP.Subscribe($"{Module}.{Name}.SetTVEnableRoutine", SetTVEnableRoutine);

            OP.Subscribe($"{Module}.{Name}.SetTVValveEnable", (function, args) =>
            {
                bool bOn = Convert.ToBoolean((string)args[0]);
                SetTVEnable(bOn, out string reason);
                return true;
            });

            OP.Subscribe($"{Module}.{Name}.SetTVReset", (function, args) =>
            {
                bool bOn = (bool)args[0];
                if (SetTVReset(bOn, out string reason))
                    _resetTimer.Start(1000);
                
                return true;
            });

            _epv2 = DEVICE.GetDevice<IoValve>($"{Module}.EPV2");

            return true;
        }

        

        public bool SetMode(PressureCtrlMode mode, out string reason)
        {
            reason = String.Empty;
            if (mode == ControlModeFeedback)
            {
                return true;
            }
            if (mode == PressureCtrlMode.TVClose)
            {
                _rampTimer.Stop();
                _rampTimer1.Stop();
                reason = $"{Display} set to {mode}";
                PressureMode = mode;
            }
            else
            {
                _rampTimer.Stop();
                _rampTimer1.Stop();

                if (!_rampTimer.IsIdle() || !_rampTimer1.IsIdle())
                {
                    //在压力正在调节过程中不允许切换模式
                    reason = "Can not set mode while pressure is ramping!";
                    return false;
                }

                if (mode == PressureCtrlMode.TVPositionCtrl)
                {
                    if(PressureFeedback > ThrottlePositionModeMaxPressure)
                    {
                        reason = $"Change Positon Mode Failed,Current Pressure is {PressureFeedback},More than change Positon Mode Max Pressure    {ThrottlePositionModeMaxPressure}!";
                        EV.PostWarningLog(Module, reason);
                        return false;
                    }
                }

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

                bool flag  = PressureMode != mode;

                PressureMode = mode;

                if(flag)
                {
                    if(PressureMode == PressureCtrlMode.TVPressureCtrl)
                    {
                        PressureSetpoint = PressureDefaultValue;
                    }
                    else if (PressureMode == PressureCtrlMode.TVPositionCtrl)
                    {
                        PositionSetpoint = PositionDefaultValue;
                    }
                }
            }

            EV.PostInfoLog(Module, reason);

            return true;
        }

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

        //private bool SetPosition(float position, out string reason)
        //{
        //    PositionSetpoint = position;

        //    reason = $"{Display} position set to {position}";

        //    EV.PostInfoLog(Module, reason);

        //    return true;
        //}

        public bool SetPosition(out string reason, int time, object[] param)
        {
            reason = String.Empty;
            if (ControlModeFeedback != PressureCtrlMode.TVPositionCtrl)
            {
                reason = "TV is not in position control mode";
                return false;
            }

            if(!_epv2.Status)
            {
                reason = "EPV2 is close,can not set TV Position";
                return false;
            }

            PositionSetpointByUse = (float)Convert.ToDouble(param[0]);

            //if (time > 0)
            //{
            //    _rampTimer1.Stop();
            //    Ramp(Convert.ToDouble(param[0]), time);
            //}
            //else
            //{
            //    _rampTimer1.Stop();
            //    double rate = SC.GetValue<double>($"PM.{Module}.ThrottlePositionRate");
            //    RampByRatePosition(Convert.ToDouble(param[0]), rate);
            //}

             _rampTimer1.Stop();
             double rate = SC.GetValue<double>($"PM.{Module}.ThrottlePositionRate");
             RampByRatePosition(Convert.ToDouble(param[0]), rate);

            return true;
        }

        public bool SetPressure(out string reason, int time, object[] param)
        {
            reason = String.Empty;

            if (ControlModeFeedback != PressureCtrlMode.TVPressureCtrl)
            {
                reason = "TV is not in pressure control mode";
                return false;
            }

            if (!_epv2.Status)
            {
                reason = "EPV2 is close,can not set TV Position";
                return false;
            }

            PressureSetpointByUse = (float)Convert.ToDouble(param[0]);

            _rampTimer.Stop();

            if (time > 0)
            {
                Ramp(Convert.ToDouble(param[0]), time);
            }
            else
            {
                double rate = SC.GetValue<double>($"PM.{Module}.ThrottlePressureRate");
                RampByRate(Convert.ToDouble(param[0]), rate);
            }
            return true;
        }

        //public bool SetPressureToDefault(string reason, object[] param)
        //{
        //    _rampTimer.Stop();

        //    if (PressureMode != PressureCtrlMode.TVPositionCtrl)
        //    {
        //        PressureMode = PressureCtrlMode.TVPositionCtrl;
        //    }

        //    PositionSetpoint = PositionDefaultValue;
        //    PressureSetpoint = PressureDefaultValue;

        //    PositionSetpointByUse = PositionDefaultValue;
        //    PressureSetpointByUse = PressureDefaultValue;
        //    return true;
        //}

        public bool SetPositionToZero(string reason, object[] param)
        {
            _rampTimer.Stop();
            _rampTimer1.Stop();

            if (ControlModeFeedback != PressureCtrlMode.TVPositionCtrl)
            {
                PressureMode = PressureCtrlMode.TVPositionCtrl;
            }

            PositionSetpoint = 0;
            //PressureSetpoint = PressureDefaultValue;

            PositionSetpointByUse = 0;
            //PressureSetpointByUse = PressureDefaultValue;

            return true;
        }

        //public bool SetPositionImmediately(string reason, object[] param)
        //{
        //    _rampTimer.Stop();
        //    reason = String.Empty;
        //    PositionSetpoint = 0;
        //    return true;
        //}

        //private bool SetPressure(float pressure, out string reason)
        //{
        //    PressureSetpoint = pressure;

        //    reason = $"{Display}  pressure set to {pressure} {_unitPressure}";

        //    EV.PostInfoLog(Module, reason);

        //    return true;
        //}

        public bool SetTVEnable(bool isEnable, out string reason)
        {
            reason = String.Empty;

            _rampTimer.Stop();
            _rampTimer1.Stop();
            //开阀时设置Ai的默认值
            if (isEnable)
            {
                if (!_doTVValveEnable.SetValue(isEnable, out reason))
                {
                    EV.PostWarningLog(Module, $"Turn {Display} On failed for Interlock!");
                    EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}，Reason：{2}", Display, "Closed", reason));
                    return false;
                }
                    

                Thread.Sleep(50);

                if (ControlModeFeedback != PressureCtrlMode.TVPressureCtrl)
                {
                    PressureMode = PressureCtrlMode.TVPressureCtrl;
                }
                
                Thread.Sleep(50);

                //PositionSetpoint = PositionDefaultValue;
                PressureSetpoint = PressureDefaultValue;

                //PositionSetpointByUse = PositionDefaultValue;
                PressureSetpointByUse = PressureDefaultValue;
            }
            else
            {
                if (ControlModeFeedback == PressureCtrlMode.TVPressureCtrl)
                {
                    PressureSetpoint = PressureDefaultValue;
                    PressureSetpointByUse = PressureDefaultValue;
                }
                else
                {
                    PositionSetpoint = PositionDefaultValue;
                    PositionSetpointByUse = PositionDefaultValue;
                }

                Thread.Sleep(50);

                if (ControlModeFeedback != PressureCtrlMode.TVClose)//强关模式
                {
                    PressureMode = PressureCtrlMode.TVClose;
                }

                if (!_doTVValveEnable.SetValue(isEnable, out reason))
                    return false;

            }            

            reason = $"{Display} set to {isEnable}";

            EV.PostInfoLog(Module, reason);
            return true;
        }

        public bool SetTVEnableState(bool isEnable, out string reason)
        {
            _rampTimer.Stop();
            _rampTimer1.Stop();

            if (!_doTVValveEnable.SetValue(isEnable, out reason))
                return false;

            reason = $"{Display} set to {isEnable}";
            EV.PostInfoLog(Module, reason);

            //无论开关蝶阀及当前模式,都设置位置和压力默认值
            PressureSetpoint = PressureDefaultValue;
            PositionSetpoint = PositionDefaultValue;

            return true;
        }

        public bool SetTVEnableRoutine(out string reason,int time,object[] param)
        {
            _rampTimer.Stop();
            _rampTimer1.Stop();

            bool isEnable = Convert.ToBoolean(param[0]);

            if (!_doTVValveEnable.SetValue(isEnable, out reason))
                return false;

            reason = $"{Display} set to {isEnable}";

            EV.PostInfoLog(Module, reason);
            return true;
        }

        public bool SetTVReset(bool reset, out string reason)
        {
            bool isResetOK = _doTVReset.SetValue(reset, out _);
            reason = $"{Display} reset to {reset} ";

            if (isResetOK)
                EV.PostInfoLog(Module, reason);
            else
                EV.PostAlarmLog(Module, reason);
            return isResetOK;
        }

        public void Terminate()
        {
        }

        public void StopRamp()
        {
            //Ramp(PressureSetpoint, 0);

            _rampTimer.Stop();
            _rampTimer1.Stop();
        }

        public void Monitor()
        {
            try
            {
                MonitorRamping();
                MonitorRampingPosition();
                //MonitorClose();
                MonitorSetRest();//根据电气要求，按钮复位按钮后，1秒后复位DO信号
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
        }

        private void MonitorClose()
        {
            //InterLock 
            if (!_doTVValveEnable.Value && ControlModeFeedback != PressureCtrlMode.TVClose)
            {
                PressureMode = PressureCtrlMode.TVClose;
            }

        }

        public void RampByRate(double target, double rate)
        {
            if (ControlModeFeedback == PressureCtrlMode.TVPressureCtrl)
            {
                _rampInitValue = PressureFeedback;// PressureSetpoint; //ramp 初始值取实际读取值
                _rampTime = 0;
                if (rate > 0)
                {
                    _rampTime = Math.Abs((int)((target - PressureFeedback) / rate) * 1000);   //根据速率计算超时时间
                }
                _rampTarget = target;
                _rampTimer.Start(_rampTime);
            }
        }

        private void RampByRatePosition(double target, double rate)
        {
            if (ControlModeFeedback == PressureCtrlMode.TVPositionCtrl)
            {
                _rampInitValue1 = PositionFeedback;//PositionSetpoint;
                _rampTime1 = 0;
                if (rate > 0)
                {
                    _rampTime1 = Math.Abs((int)((target - PositionFeedback) / rate) * 1000);
                }
                _rampTarget1 = target;
                _rampTimer1.Start(_rampTime1);
            }
        }


        private void Ramp(double target, int time)
        {
            if (ControlModeFeedback == PressureCtrlMode.TVPositionCtrl)
            {
                _rampInitValue1 = PositionFeedback; //PositionSetpoint;                              //ramp 初始值取实际读取值
                _rampTime1 = time;
                _rampTarget1 = target;
                _rampTimer1.Start(_rampTime1);
            }
            else
            {
                _rampInitValue = PressureFeedback;// PressureSetpoint;                              //ramp 初始值取当前设定值，而非实际读取值.零漂问题
                _rampTime = time;
                _rampTarget = target;
                _rampTimer.Start(_rampTime);
            }
        }

        private void MonitorRamping()
        {
            if (!_rampTimer.IsIdle())
            {
                if (ControlModeFeedback == PressureCtrlMode.TVPressureCtrl)
                {
                    //压力模式
                    if (_rampTimer.IsTimeout() || _rampTime == 0)
                    {
                        _rampTimer.Stop();
                        PressureSetpoint = (float)_rampTarget;
                    }
                    else
                    {
                        PressureSetpoint = (float)(_rampInitValue + (_rampTarget - _rampInitValue) * _rampTimer.GetElapseTime() / _rampTime);
                    }
                }
            }
        }


        private void MonitorRampingPosition()
        {
            if (!_rampTimer1.IsIdle())
            {
                if (ControlModeFeedback == PressureCtrlMode.TVPositionCtrl)
                {
                    if (_rampTimer1.IsTimeout() || _rampTime1 == 0)
                    {
                        _rampTimer1.Stop();
                        PositionSetpoint = (float)_rampTarget1;
                    }
                    else
                    {
                        PositionSetpoint = (float)(_rampInitValue1 + (_rampTarget1 - _rampInitValue1) * _rampTimer1.GetElapseTime() / _rampTime1);
                    }
                }               
            }
        }

        private void MonitorSetRest()
        {
            if (_resetTimer.IsTimeout())
            {
                _resetTimer.Stop();
                SetTVReset(false, out _);
            }    
        }


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