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;
namespace Aitex.Core.RT.Device.Unit
{
    public interface IValve
    {
        bool TurnValve(bool isOn, out string reason);
    }
    public class IoValve : BaseDevice, IDevice, IValve
    {
        public string GVName { get { return Name; } }
        public string GVDeviceID { get { return DeviceID; } }
        public bool GVIsDefaultOpen { get { return _isDefaultOpen; } }
        public AlarmEventItem InterlockAlarm;
        public AlarmEventItem InterlockWarning;
        public DOAccessor DOOpen => _doOpen;
        public DOAccessor DOClose => _doClose;
        private bool _setPoint;
        public bool IsILKOK = true;
        [Subscription(AITValveDataPropertyName.SetPoint)]
        public bool SetPoint    //True:open| False:close
        {
            get
            {
                return _setPoint;
                //if (_doOpen == null)
                //    return false;
                //return _isNc ? _doOpen.Value : !_doOpen.Value;
            }
            set
            {
                if (_doOpen != null)
                {
                    if (_doOpen.SetValue(_isNc ? value : !value, out _, false))
                    {
                        _setPoint = value;
                    }
                }
                if (_doClose != null)
                {
                    if (_doClose.SetValue(_isNc ? !value : value, out _, false))
                    {
                        _setPoint = value;
                    }
                }
            }
        }
        [Subscription(AITValveDataPropertyName.Status)]
        public bool Status    //True:open | False:close
        {
            get
            {
                if (_diOpenSensor != null && _diCloseSensor != null)
                    return _diOpenSensor.Value && !_diCloseSensor.Value;
                if (_diCloseSensor != null)
                    return !_diCloseSensor.Value;
                if (_diOpen != null)
                    return _isNc ? _diOpen.Value : !_diOpen.Value;
                if (_doOpen != null)
                    return _isNc ? _doOpen.Value : !_doOpen.Value;
                if (_doClose != null)
                    return _isNc ? !_doClose.Value : _doClose.Value;
                return false;
            }
        }
        private AITValveData DeviceData
        {
            get
            {
                AITValveData data = new AITValveData()
                {
                    UniqueName = _uniqueName,
                    DeviceName = GVName,
                    DefaultValue = GVIsDefaultOpen,
                    DeviceSchematicId = DeviceID,
                    DisplayName = Display,
                    Feedback = Status,
                    SetPoint = SetPoint,
                    ILKDiValue = ILKDiSensor == null ? true : ILKDiSensor.Value,
                    IsILKOK = this.IsILKOK,
                    VirtualFeedback = VirtualStatus,
                };
                return data;
            }
        }
        /// 
        /// normal closed, 0 关闭,1打开
        /// 
        public bool _isNc;
        /// 
        /// default open
        /// 
        public bool _isDefaultOpen;
        private DIAccessor _diOpenSensor;
        private DIAccessor _diCloseSensor;
        private DIAccessor _diOpen;
        private DOAccessor _doOpen;
        private DOAccessor _doClose;
        private DIAccessor ILKDiSensor;
        private bool _operation;
        public bool IsILKNot { get; set; } = false;
        R_TRIG eventTrigger = new R_TRIG();
        R_TRIG _mutexSignalTrigger = new R_TRIG();
        DeviceTimer _timer = new DeviceTimer();
        DeviceTimer _mutexSignalTimer = new DeviceTimer();
        private string _uniqueName;
        public bool VirtualStatus;
        private string _writeLog = "";
        private SCConfigItem _interlockNoneOrExist;
        private SCConfigItem _interlockNormaly0nOrOff;
        private SCConfigItem _interlockDelayOnTime;
        private SCConfigItem _interlockDelayOffTime;
        private SCConfigItem _interlockILKTime;
        private float _interlockDelayTimeInMS;
        private float _ilkDelayTimeInMS;
        private Stopwatch _interlockDelayTimer = new Stopwatch();
        private Stopwatch _ilkDelayTimer = new Stopwatch();
        public IoValve(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");
            _isNc = Convert.ToBoolean(node.GetAttribute("isNc"));
            _isDefaultOpen = Convert.ToBoolean(node.GetAttribute("isDefaultOpen"));
            _diOpenSensor = ParseDiNode("diOpenSensor", node, ioModule);
            _diCloseSensor = ParseDiNode("diCloseSensor", node, ioModule);
            _doOpen = ParseDoNode("doOpen", node, ioModule);
            _diOpen = ParseDiNode("diOpen", node, ioModule);
            _doClose = ParseDoNode("doClose", node, ioModule);
            ILKDiSensor = ParseDiNode("ILKDi", node, ioModule);
            var attIsILKNot = node.GetAttribute("IsILKNot");
            IsILKNot = string.IsNullOrEmpty(attIsILKNot) ? false : Convert.ToBoolean(attIsILKNot);
            _uniqueName = $"{Module}.{Name}";
            InterlockAlarm= SubscribeAlarm(new AlarmEventItem()
            {
                EventEnum = $"{Module}.{Name} InterlockAlarm",
                Description = $"{Name} Interlock alarm",
                Solution = "No information available. Press[Clear] to delete alarm message.",
                Explaination = "No information available.",
                AutoRecovery = false,
                Level = EventLevel.Alarm,
                Action = EventAction.Clear,
                Category = "TubeAlarm",
            }, () => { Reset(); return true; });
        }
        public bool Initialize()
        {
            _operation = _isNc ? false : true;
            if (_isNc == false && _isDefaultOpen == false)
            {
                TurnValve(_isDefaultOpen, out string tempReason);
            }
            DATA.Subscribe($"Device.{Module}.{GVName}", () => DeviceData);
            DATA.Subscribe($"{_uniqueName}.DeviceData", () => DeviceData);
            DATA.Subscribe($"{Module}.{Name}.SetPoint", () => SetPoint);
            DATA.Subscribe($"{Module}.{Name}.Feedback", () => Status);
            OP.Subscribe($"{_uniqueName}.{AITValveOperation.GVTurnValve}", InvokeOpenCloseValve);
            OP.Subscribe($"{_uniqueName}.{AITValveOperation.GVVirtualTurnValve}", InvokeOpenCloseVirtualValve);
            DEVICE.Register(String.Format("{0}.{1}", Name, AITValveOperation.GVTurnValve), (out string reason, int time, object[] param) =>
            {
                bool bOn = Convert.ToBoolean((string)param[0]);
                bool ret = TurnValve(bOn, out reason);
                if (ret)
                {
                    reason = string.Format("Valve {0}{1}", Name, bOn ? "Open" : "Close");
                    return true;
                }
                return false;
            });
            //for recipe
            DEVICE.Register(String.Format("{0}", Name), (out string reason, int time, object[] param) =>
            {
                bool bOn = Convert.ToBoolean((string)param[0]);
                bool ret = TurnValve(bOn, out reason);
                if (ret)
                {
                    reason = string.Format("Valve {0}{1}", Name, bOn ? "Open" : "Close");
                    return true;
                }
                return false;
            });
            //for recipe
            OP.Subscribe($"{Module}.{Name}", (out string reason, int time, object[] param) =>
            {
                reason = string.Empty;
                if (param[0].ToString() == "Continue")
                {
                    EV.PostInfoLog(Module, $"Turn {Display} Continue");
                    return true;
                }
                bool bOn = Convert.ToBoolean((string)param[0]);
                bool ret = TurnValve(bOn, out reason);
                if (!ret)
                {
                    reason = string.Format("Valve {0}{1} failed", Name, bOn ? "Open" : "Close");
                    return false;
                }
                return true;
            });
            var valveIndex = Name.Replace("ValveAV", "");
            _interlockNoneOrExist = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.NoneOrExist");
            _interlockNormaly0nOrOff = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.Normaly0nOrOff");
            _interlockDelayOnTime = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.DelayOnTime");
            _interlockDelayOffTime = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.DelayOffTime");
            _interlockILKTime = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.ILKTime");
            return true;
        }
        public void Terminate()
        {
            string reason;
            TurnValve(_isDefaultOpen, out reason);
        }
        public bool InvokeOpenCloseValve(string method, object[] args)
        {
            string reason;
            bool op = Convert.ToBoolean(args[0]);
            string name = op ? "Open" : "Close";
            if (!TurnValve(op, out reason))
            {
                if (InterlockWarning != null)
                {
                    InterlockWarning.Description = $"{reason}";
                    InterlockWarning.Set();
                }
                else
                {
                    EV.PostWarningLog(Module, $"Can not {name} valve {Module}.{Name}, {reason}");
                }
                return false;
            }
            EV.PostInfoLog(Module, $"{name} valve {Module}.{Name}");
            return true;
        }
        public bool InvokeOpenCloseVirtualValve(string method, object[] args)
        {
            bool op = Convert.ToBoolean(args[0]);
            VirtualStatus = op;
            return true;
        }
        private bool _openOperation;
        public void Monitor()
        {
            if (!string.IsNullOrEmpty(_writeLog))
            {
                LOG.Write(_writeLog);
                _writeLog = "";
            }
           
            if (_interlockDelayTimer.IsRunning)
            {
                if (_interlockDelayTimer.ElapsedMilliseconds >= _interlockDelayTimeInMS)
                {
                    _interlockDelayTimer.Stop();
                    _interlockDelayTimer.Reset();
                    bool bValue = _operation;
                    _openOperation = _operation;
                    if (_doOpen != null)
                    {
                        if (!_doOpen.Check(bValue, out var reason))
                        {
                            if (!_doOpen.SetValue(bValue, out reason, false))
                            {
                                EV.PostWarningLog("System", $"interlock set DO {Module} to {bValue}:{reason}, {reason}");
                            }
                            // EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(_operation ? "Open" : "Close")}", ":Failed for interlock " + reason);
                            _operation = SetPoint; //还原状态 防止定时检查状态再次写日志
                            return;
                        }
                    }
                    if (_doClose != null)
                    {
                        if (!_doClose.Check(bValue, out var reason))
                        {
                            if (!_doClose.SetValue(bValue, out reason, false))
                            {
                                EV.PostWarningLog("System", $"interlock set DO {Module} to {bValue}:{reason}, {reason}");
                            }
                            //EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(_operation ? "Open" : "Close")}", ":Failed for interlock " + reason); ;
                            _operation = SetPoint;//还原状态 防止定时检查状态再次写日志
                            return;
                        }
                    }
                    SetPoint = _isNc ? _operation : !_operation;
                }
                else
                {
                    return;
                }
            }
            try
            {
                if (_diOpenSensor != null && _diCloseSensor != null && _doOpen != null)
                {
                    if (_diOpenSensor.Value != _diCloseSensor.Value)
                    {
                        _mutexSignalTimer.Start(2000);
                    }
                    _mutexSignalTrigger.CLK = _mutexSignalTimer.IsTimeout();
                    if (_mutexSignalTrigger.Q)
                    {
                        EV.PostWarningLog(Module, $"Valve {Name} was abnormal,Reason:diOpenSensor's value is {_diOpenSensor.Value} and diCloseSensor's value is {_diCloseSensor.Value} too.");
                    }
                }
                if (_timer.IsTimeout() && _doOpen != null)
                {
                    _timer.Stop();
                    if (Status != _operation)
                    {
                        if (_operation)
                        {
                            string reason;
                            if (!_doOpen.Check(_isNc ? true : false, out reason))
                                EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Open", ":Failed for interlock " + reason);
                            else
                                EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Open", $":Valve {Name} keep closed ");
                        }
                        else
                        {
                            string reason;
                            if (!_doOpen.Check(_isNc ? true : false, out reason))
                                EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Close", ":Failed for interlock " + reason);
                            else
                                EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Close", $":Valve {Name} keep open");
                        }
                    }
                    _operation = SetPoint;
                }
                else if (_timer.IsIdle() && _doOpen != null)
                {
                    eventTrigger.CLK = SetPoint != _operation;   // fire event only check at first, SetPoint set by interlock
                    if (eventTrigger.Q)
                    {
                        if (_operation)
                        {
                            string reason;
                            if (!_doOpen.Check(_isNc ? true : false, out reason))
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason:{2}", Display, "Close", reason));
                            else
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason {2}", Display, "Close", "PLC kept"));
                        }
                        else
                        {
                            string reason;
                            if (!_doOpen.Check(_isNc ? true : false, out reason))
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason:{2}", Display, "Open", reason));
                            else
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason {2}", Display, "Open", "PLC Kept"));
                        }
                        _operation = SetPoint;
                    }
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
            if (ILKDiSensor == null)
            {
                IsILKOK = true;
            }
            else
            {
                if (!_isNc)
                {
                    IsILKOK = true;
                    return;
                }
                if (ILKDiSensor.Value)
                {
                    IsILKOK = true;
                }
                else if (!ILKDiSensor.Value)
                {
                    if (!IsILKNot)
                    {
                        IsILKOK = false;
                    }
                }
            }
            //ILK判断
            if (_ilkDelayTimer.IsRunning)
            {
                if (_ilkDelayTimer.ElapsedMilliseconds >= _ilkDelayTimeInMS)
                {
                    _ilkDelayTimer.Stop();
                    _ilkDelayTimer.Reset();
                    if (_doOpen != null)
                    {
                        if (!ILKDiSensor.Value)
                        {
                            InterlockAlarm.Set($"interlock delay ends. The value of the {Name} valve is incorrect.");
                           // EV.PostWarningLog(Module,  $"interlock delay ends. The value of the {Name} valve is incorrect.");
                            return;
                        }
                    }
                }
                else
                {
                    return;
                }
            }
        }
        public bool TurnValve(bool isOn, out string reason)
        {
            bool bValue = _isNc ? isOn : !isOn;
            var stringOnOff = isOn ? "On" : "Off";
            //EV.PostInfoLog(Module, $"Turn {Display} {stringOnOff}");
            _writeLog = $"Turn {Display} {stringOnOff}";
            reason = "";
            _interlockDelayTimeInMS = 0;
            _ilkDelayTimeInMS = 0;
            //开始计时ILK
            if (_interlockNoneOrExist != null && _interlockILKTime != null && _interlockNoneOrExist.BoolValue && bValue)
            {
                var timeParas = _interlockILKTime.StringValue.Split(':');//00:00.0
                if (timeParas.Length > 1)
                {
                    float.TryParse(timeParas[0], out float min);
                    float.TryParse(timeParas[1], out float sec);
                    _ilkDelayTimeInMS = min * 60 * 1000 + sec * 1000;
                }
                _ilkDelayTimer.Restart();
                _operation = bValue;
                if (_ilkDelayTimeInMS > 0)
                {
                    _writeLog += $"ilk delay time={_ilkDelayTimeInMS} ms";
                    _timer.Start(2000 + _ilkDelayTimeInMS);
                }
            }
            if (_interlockNoneOrExist != null && _interlockDelayOnTime != null && _interlockNoneOrExist.BoolValue && bValue)
            {
                var timeParas = _interlockDelayOnTime.StringValue.Split(':');//00:00.0
                if (timeParas.Length > 1)
                {
                    float.TryParse(timeParas[0], out float min);
                    float.TryParse(timeParas[1], out float sec);
                    _interlockDelayTimeInMS = min * 60 * 1000 + sec * 1000;
                }
                _interlockDelayTimer.Restart();
                _operation = bValue;
                if (_interlockDelayTimeInMS > 0)
                {
                    _writeLog += $" delay time={_interlockDelayTimeInMS} ms";
                    _timer.Start(2000 + _interlockDelayTimeInMS);
                }
                else
                {
                     _timer.Start(2000);         //2 seconds to monitor
                }
            }
            else if (_interlockNoneOrExist != null && _interlockDelayOffTime != null && _interlockNoneOrExist.BoolValue && !bValue)
            {
                var timeParas = _interlockDelayOffTime.StringValue.Split(':');//00:00.0
               
                if (timeParas.Length > 1)
                {
                    float.TryParse(timeParas[0], out float min);
                    float.TryParse(timeParas[1], out float sec);
                    _interlockDelayTimeInMS = min * 60 * 1000 + sec * 1000;
                }
                _interlockDelayTimer.Restart();
                _operation = bValue;
                if (_interlockDelayTimeInMS > 0)
                {
                    _writeLog += $" delay time={_interlockDelayTimeInMS} ms";
                    _timer.Start(2000 + _interlockDelayTimeInMS);
                }
                else
                {
                     _timer.Start(2000);         //2 seconds to monitor
                }
            }
            else
            {
                if (_doOpen != null)
                {
                    if (!_doOpen.Check(bValue, out reason))
                    {
                        if (!_isNc && !IsILKNot)
                        {
                            IsILKOK = false;
                        }
                        _doOpen.SetValue(bValue, out reason, false);
                        //EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(isOn ? "Open" : "Close")}", ":Failed for interlock " + reason); ;
                        return false;
                    }
                }
                if (_doClose != null)
                {
                    if (!_doClose.Check(!bValue, out reason))
                    {
                        _doClose.SetValue(bValue, out reason, false);
                        //EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(isOn ? "Open" : "Close")}", ":Failed for interlock " + reason); ;
                        return false;
                    }
                }
                SetPoint = isOn;
                _operation = isOn;
                _timer.Start(2000);         //2 seconds to monitor
            }
            return true;
        }
        public void Reset()
        {
            eventTrigger.RST = true;
        }
    }
}