using System;
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.Util;

namespace Aitex.Core.RT.Device.Unit
{
    public class IoThrottleValve : BaseDevice, IDevice
    {
        public struct Context
        {
            public string tvName;

            public string aoPressureModeName;

            public string aoPressureSetPointName;
            public string aoPositionSetPointName;
            public string aiPressureFeedbackName;
            public string aiPositionFeedbackName;

            public string aiStateName;
        };

        public PressureCtrlMode PressureMode
        {
            get
            {
                if (_aoPressureMode == null)
                    return PressureCtrlMode.TVPositionCtrl;

                return Math.Abs(_aoPressureMode.Value - 2) < 0.1 ? PressureCtrlMode.TVPositionCtrl : PressureCtrlMode.TVPressureCtrl;
            }
            set
            {
                if (_aoPositionSetPoint == null || _aoPressureSetPoint == null || _aoPressureMode == null)
                    return;

                float setpoint = value == PressureCtrlMode.TVPositionCtrl ? 2 : 1;
                if (Math.Abs(_aoPressureMode.Value - setpoint) > 0.01)
                {
                    if (value == PressureCtrlMode.TVPositionCtrl)
                    {
                        _aoPositionSetPoint.Value = (short)PositionFeedback;
                    }
                    else
                    {
                        _aoPressureSetPoint.Value = (short)PressureFeedback;
                    }
                    _aoPressureMode.Value = (short)setpoint;
                }
            }
        }


        public int State
        {
            get
            {
                return _aiState == null ? 1 : (int)_aiState.Value;
            }
        }

        [Subscription(AITThrottleValvePropertyName.TVPositionSetPoint)]
        public float PositionSetpoint
        {
            get
            {
                return _aoPositionSetPoint == null ? 0 : _aoPositionSetPoint.Value;
            }
            set
            {
                if (_aoPositionSetPoint == null)
                    return;

                _aoPositionSetPoint.Value = (short)value;
            }
        }

        [Subscription(AITThrottleValvePropertyName.TVPosition)]
        public float PositionFeedback
        {
            get
            {
                return _aiPositionFeedback == null ? 0 : _aiPositionFeedback.Value;
            }
        }

        [Subscription(AITThrottleValvePropertyName.TVPressureSetPoint)]
        public float PressureSetpoint
        {
            get
            {
                return _aoPressureSetPoint == null ? 0 : _aoPressureSetPoint.Value;
            }
            set
            {
                if (_aoPressureSetPoint == null)
                    return;

                _aoPressureSetPoint.Value = (short)value;
            }
        }


        private AITThrottleValveData DeviceData
        {
            get
            {
                AITThrottleValveData data = new AITThrottleValveData()
                {
                    DeviceName = Name,
                    DeviceSchematicId = DeviceID,
                    DisplayName = Display,
                    Mode = (int)PressureMode,
                    PositionFeedback = PositionFeedback,
                    PositionSetPoint = PositionSetpoint,
                    PressureFeedback = PressureFeedback,
                    PressureSetPoint = PressureSetpoint,
                    State = State,

                };


                return data;
            }
        }

        [Subscription(AITThrottleValvePropertyName.TVPressure)]
        public float PressureFeedback
        {
            get
            {
                return _aiPressureFeedback == null ? 0 : _aiPressureFeedback.Value;
            }
        }

        public bool IsPumpMode
        {
            get; set;
        }

        public bool IsIndependent
        {
            get; set;
        }

        public bool IsOffline
        {
            get
            {
                return _diOffline != null && _diOffline.Value;
            }
        }

        private DIAccessor _diOffline;

        private AIAccessor _aiPressureFeedback = null;
        private AIAccessor _aiPositionFeedback = null;

        private AOAccessor _aoPressureSetPoint = null;
        private AOAccessor _aoPositionSetPoint = null;

        private AOAccessor _aoPressureMode = null;
        private AIAccessor _aiState = null;

        private R_TRIG _tvStatusAlmTrig = new R_TRIG();
        private R_TRIG _trigOffline = new R_TRIG();

        public IoThrottleValve(string module, XmlElement node, string ioModule = "")
        {
            base.Module = module;
            base.Name = node.GetAttribute("id");
            base.Display = node.GetAttribute("display");
            base.DeviceID = node.GetAttribute("schematicId");

            _aiPositionFeedback = ParseAiNode("aiPositionFeedback", node);
            _aiPressureFeedback = ParseAiNode("aiPressureFeedback", node);
            _aoPositionSetPoint = ParseAoNode("aoPositionSetPoint", node);
            _aoPressureSetPoint = ParseAoNode("aoPressureSetPoint", node);

            _aiState = ParseAiNode("aiState", node);

            _aoPressureMode = ParseAoNode("aoPressureMode", node);

            _diOffline = ParseDiNode("diOffline", node);
        }


        public bool Initialize()
        {
            DATA.Subscribe(string.Format("Device.{0}.{1}", Module, Name), () => DeviceData);
            DATA.Subscribe($"{Module}.{Name}.DeviceData", () => DeviceData);

            PressureMode = PressureCtrlMode.TVPressureCtrl;

            DEVICE.Register(String.Format("{0}.{1}", Name, AITThrottleValveOperation.SetMode), (out string reason, int time, object[] param) =>
            {

                PressureMode = (PressureCtrlMode)Enum.Parse(typeof(PressureCtrlMode), (string)param[0], true);
                reason = string.Format("Throttle valve set to {0} mode", PressureMode);

                return true;

            });

            DEVICE.Register(String.Format("{0}.{1}", Name, AITThrottleValveOperation.SetPosition), (out string reason, int time, object[] param) =>
            {
                double target = Convert.ToDouble((string)param[0]);

                if (PressureMode != PressureCtrlMode.TVPositionCtrl)
                {
                    reason = "Throttle valve in pressure mode, can not set position";
                    return false;
                }

                PositionSetpoint = (float)target;
                reason = string.Format("position set to {0}%", target.ToString("F1"));
                return true;
            });


            DEVICE.Register(String.Format("{0}.{1}", Name, AITThrottleValveOperation.SetPressure), (out string reason, int time, object[] param) =>
            {
                if (PressureMode == PressureCtrlMode.TVPositionCtrl)
                {
                    reason = "Throttle valve is in positon conrol mode, can not set pressure";
                    return false;
                }

                double target = Convert.ToDouble((string)param[0]);

                PressureSetpoint = (float)target;
                reason = string.Format("pressure set {0} mTorr", target);
                return true;
            });

            return true;
        }

        public void Terminate()
        {
        }

        public void Monitor()
        {
            try
            {
                _tvStatusAlmTrig.CLK = State != 1;
                if (_tvStatusAlmTrig.Q)
                {
                    EV.PostMessage(Module, EventEnum.ThrottleValveAbnormal, Module);
                }

                _trigOffline.CLK = IsOffline;
                if (_trigOffline.Q)
                {
                    EV.PostMessage(Module, EventEnum.DefaultAlarm, "Throttle Valve Offline");
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }

        }

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

        public void SetPositionMode(int position)
        {
            PressureMode = PressureCtrlMode.TVPositionCtrl;
            PositionSetpoint = (float)position;
        }

    }
}
