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.RT.OperationCenter;
using Aitex.Core.Util;
using DocumentFormat.OpenXml.Wordprocessing;

namespace Aitex.Core.RT.Device.Unit
{
    public class IoValve : BaseDevice, IDevice
    {
        public string GVName { get { return Name; } }

        public string GVDeviceID { get { return DeviceID; } }

        public bool GVIsDefaultOpen { get { return _isDefaultOpen; } }

        [Subscription(AITValveDataPropertyName.SetPoint)]
        public bool SetPoint    //True:open| False:close
        {
            get
            {
                return _isNc ? _doOpen.Value : !_doOpen.Value;
            }
            set
            {
                if (_doOpen != null)
                {
                    _doOpen.Value = _isNc ? value : !value;


                }
                if (_doClose != null)
                {
                    _doClose.Value = _isNc ? !value : value;

                }
            }
        }

        [Subscription(AITValveDataPropertyName.Status)]
        public bool Status    //True:open | False:close
        {
            get
            {
                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,
                };

                return data;
            }
        }

        /// <summary>
        /// normal closed, 0 关闭,1打开
        /// </summary>
        public bool _isNc;

        /// <summary>
        /// default open
        /// </summary>
        public bool _isDefaultOpen;

        private DIAccessor _diOpenSensor;
        private DIAccessor _diCloseSensor;

        private DIAccessor _diOpen;
        private DOAccessor _doOpen;
        private DOAccessor _doClose;

        private bool _operation;

        private R_TRIG eventTrigger = new R_TRIG();
        private DeviceTimer _timer = new DeviceTimer();

        private string _uniqueName;

        private object _lockerOperation = new object();

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

            _uniqueName    = $"{Module}.{Name}";
        }

        public bool Initialize()
        {
            DATA.Subscribe($"{Module}.{GVName}", () => DeviceData);
            DATA.Subscribe($"{Module}.{GVName}.IsOpen", () => Status);
            //DATA.Subscribe($"{_uniqueName}.DeviceData", () => DeviceData);

            OP.Subscribe($"{_uniqueName}.{AITValveOperation.GVTurnValve}", InvokeOpenCloseValve);

            DEVICE.Register($"{Module}.{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($"{Module}.{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;
            });

            return true;
        }

        public void Terminate()
        {
            TurnValve(_isDefaultOpen, out string reason);
        }

        private bool InvokeOpenCloseValve(string method, object[] args)
        {
            bool op = (bool)args[0];
            string name = op ? "Open" : "Close";

            if (!TurnValve(op, out string reason))
            {
                LOG.Write(eEvent.WARN_IO_VALVE, Module, $"Can not {name} valve {Module}.{Name}, {reason}");
                return false;
            }

            LOG.Write(eEvent.INFO_IO_VALVE, Module, $"{name} valve {Module}.{Name}");

            return true;
        }

        public void Monitor()
        {
            try
            {
                lock (_lockerOperation)
                {
                    if (_timer.IsTimeout())
                    {
                        _timer.Stop();

                        if (Status != _operation)
                        {
                            if (_operation)
                            {
                                LOG.Write(eEvent.ERR_IO_VALVE, Module, _doOpen.Check(_isNc ? true : false, out string reason) ?
                                        $"{Display} open: valve keep closed" :
                                        $"{Display} Fail to open due to interlock {reason}");
                            }
                            else
                            {
                                LOG.Write(eEvent.ERR_IO_VALVE, Module, _doOpen.Check(_isNc ? true : false, out string reason) ?
                                    $"{Display} Close : Valve keep open" :
                                    $"{Display} Close : Failed for interlock {reason}");
                            }
                        }
                        _operation = SetPoint;
                    }
                    else if (_timer.IsIdle())
                    {
                        eventTrigger.CLK = SetPoint != _operation;   // fire event only check at first, SetPoint set by interlock
                        if (eventTrigger.Q)
                        {
                            if (_operation)
                            {
                                LOG.Write(eEvent.ERR_IO_VALVE, Module, _doOpen.Check(_isNc ? true : false, out string reason) ?
                                    $"Valve {Display} was Close,Reason PLC kept" :
                                    $"Valve {Display} was Close,Reason {reason}");
                            }
                            else
                            {
                                LOG.Write(eEvent.ERR_IO_VALVE, Module, _doOpen.Check(_isNc ? true : false, out string reason) ?
                                    $"Valve {Display} was Open,Reason PLC Kept" :
                                    $"Valve {Display} was Open,Reason:{reason}");
                            }
                            _operation = SetPoint;
                        }
                    }
                }

            }
            catch (Exception ex)
            {
                LOG.WriteExeption(ex);
                 
            }
        }

        public bool TurnValve(bool bOperation, out string reason)
        {
            lock (_lockerOperation)
            {
                bool bValue = _isNc ? bOperation : !bOperation;

                reason = "";
                SetPoint = bValue;

                if (Name == "ValveN2")
                {
                    reason = Name;
                }

                _operation = bOperation;
                _timer.Start(2000);         //2 seconds to monitor             
            }


            return true;
        }

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