using System;
using System.Xml;
using Aitex.Core.Common.DeviceData;
using Aitex.Core.Properties;
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 enum LidState
    {
        Close = 0,
        Open = 1,
        Unknown = 2,
        Error = 3,
    }

    public class IoLid : BaseDevice, IDevice
    {
        private DIAccessor _diOpened;
        private DIAccessor _diClosed;

        private DOAccessor _doOpen;
        private DOAccessor _doClose;

        private LidState _operation;
        private DeviceTimer _timer = new DeviceTimer();
        private R_TRIG _trigReset = new R_TRIG();
        private R_TRIG _trigError = new R_TRIG();

        public int SetPoint   
        {
            get
            {
                if (_doOpen.Value && _doClose.Value) return (int) LidState.Error;
                if (_doOpen.Value && !_doClose.Value) return (int) LidState.Open;
                if (!_doOpen.Value && _doClose.Value) return (int) LidState.Close;
                if (!_doOpen.Value && !_doClose.Value) return (int) LidState.Unknown;

                return (int) LidState.Unknown;
            }
        }

        public int Status 
        {
            get
            {
                if (_diOpened.Value && _diClosed.Value)
                    return (int)LidState.Error;
                if (_diOpened.Value && !_diClosed.Value)
                    return (int)LidState.Open;
                if (!_diOpened.Value && _diClosed.Value)
                    return (int)LidState.Close;
                if (!_diOpened.Value && !_diClosed.Value)
                    return (int)LidState.Unknown;

                return (int)LidState.Unknown;
            }
        }
 
        public IoLid(string module, XmlElement node)
        {

            base.Module = module;
            base.Name = node.GetAttribute("id");
            base.Display = node.GetAttribute("display");
            base.DeviceID = node.GetAttribute("schematicId");

            _diOpened = ParseDiNode("diOpen", node);
            _diClosed = ParseDiNode("diClose", node);
            _doOpen = ParseDoNode("doOpen", node);
            _doClose = ParseDoNode("doClose", node);

        }

        public bool Initialize()
        {
            DATA.Subscribe(string.Format("Device.{0}.{1}", Module , Name), 
                () =>
                {
                     AITLidData data = new AITLidData()
                                         {
                                             DeviceName = Name,
                                             DeviceSchematicId = DeviceID,
                                             DisplayName = Display,
                                             Status = Status,
                                             SetPoint = SetPoint,
                                         };

                     return data;
                 }, SubscriptionAttribute.FLAG.IgnoreSaveDB);

            DEVICE.Register(String.Format("{0}.{1}", Name, AITLidOperation.OpenLid), (out string reason, int time, object[] param) =>
            {
                bool ret = SetLid(true, out reason);
                if (ret)
                {
                    reason = string.Format(Resources.IoLid_Initialize_OpenLid0, Name);
                    return true;
                }

                return false;
            });

            DEVICE.Register(String.Format("{0}.{1}", Name, AITLidOperation.CloseLid), (out string reason, int time, object[] param) =>
            {
                bool ret = SetLid(false, out reason);
                if (ret)
                {
                    reason = string.Format(Resources.IoLid_Initialize_CloseLid0, Name);
                    return true;
                }

                return false;
            });

            return true;
        }

        public void Terminate()
        {
            _doOpen.Value = false;
            _doClose.Value = false;
        }

        public bool SetLid(bool isOpen, out string reason)
        {
            if (!_doOpen.Check(isOpen, out reason))
                return false;
            if (!_doClose.Check(!isOpen, out reason))
                return false;

            if (!_doOpen.SetValue(isOpen, out reason))
                return false;
            if (!_doClose.SetValue(!isOpen, out reason))
                return false;
            
            _operation = isOpen ? LidState.Open : LidState.Close;
            _timer.Start(1000 * 60 * 3);
            return true;
        }

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

                    if (Status != (int)_operation)
                    {
                        if (_operation == LidState.Open)
                        {
                            string reason;
                            if (!_doOpen.Check(true, out reason))
                                EV.PostMessage(Module, EventEnum.DefaultAlarm, Resources.IoLid_Monitor_OpenLidFailedForInterlock + reason);
                            else
                                EV.PostMessage(Module, EventEnum.DefaultAlarm, Resources.IoLid_Monitor_LidHoldCloseStatus);
                        }
                        else
                        {
                            string reason;
                            if (!_doOpen.Check(false, out reason))
                                EV.PostMessage(Module, EventEnum.DefaultAlarm, Resources.IoLid_Monitor_CloseLidFailedForInterlock + reason);
                            else
                                EV.PostMessage(Module, EventEnum.DefaultAlarm, Resources.IoLid_Monitor_LidHoldOpenStatus);
                        }
                    }
                    _operation = (LidState)SetPoint;
                }
                else if (_timer.IsIdle())
                {
                    _trigReset.CLK = SetPoint != (int)_operation;   // fire event only check at first, SetPoint set by interlock
                    if (_trigReset.Q)
                    {
                        if (_operation == LidState.Open)
                        {
                            string reason;
                            if (!_doOpen.Check(true, out reason))
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format(Resources.IoLid_Monitor_Lid0Was1Reason2, Display, Resources.IoLid_Monitor_Close, reason));
                            else
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format(Resources.IoLid_Monitor_Lid0Was1Reason, Display, Resources.IoLid_Monitor_Close, Resources.IoLid_Monitor_PLCKept));
                        }
                        else
                        {
                            string reason;
                            if (!_doOpen.Check(false, out reason))
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format(Resources.IoLid_Monitor_Lid0Was1Reason2, Display, Resources.IoLid_Monitor_Open, reason));
                            else
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format(Resources.IoLid_Monitor_Lid0Was1Reason, Display, Resources.IoLid_Monitor_Open, Resources.IoLid_Monitor_PLCKept1));
                        }
                        _operation = (LidState)SetPoint;
                    }
                }

                _trigError.CLK = Status == (int) LidState.Error;
                if (_trigError.Q)
                {
                    EV.PostMessage(Module, EventEnum.DefaultAlarm, Resources.IoLid_Monitor_LidInErrorStatus);
                }

                if ((SetPoint == Status) && (SetPoint == (int)LidState.Open || SetPoint == (int)LidState.Close))
                {
                    _doClose.Value = false;
                    _doOpen.Value = false;
                }


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

        }

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