using System;
using System.Xml;
using Aitex.Core.Common.DeviceData;
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 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, string ioModule = "")
        {

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

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

        }

        public bool Initialize()
        {
            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("Open Lid {0}", 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("Close Lid {0}", 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, "Open lid Failed for interlock, " + reason);
                            else
                                EV.PostMessage(Module, EventEnum.DefaultAlarm, "Lid hold close status");
                        }
                        else
                        {
                            string reason;
                            if (!_doOpen.Check(false, out reason))
                                EV.PostMessage(Module, EventEnum.DefaultAlarm, "Close lid Failed for interlock, " + reason);
                            else
                                EV.PostMessage(Module, EventEnum.DefaultAlarm, "Lid hold open status");
                        }
                    }
                    _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("Lid {0} was {1}，Reason：{2}", Display, "Close", reason));
                            else
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Lid {0} was {1}，Reason {2}", Display, "Close", "PLC kept"));
                        }
                        else
                        {
                            string reason;
                            if (!_doOpen.Check(false, out reason))
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Lid {0} was {1}，Reason：{2}", Display, "Open", reason));
                            else
                                EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Lid {0} was {1}，Reason {2}", Display, "Open", "PLC Kept"));
                        }
                        _operation = (LidState)SetPoint;
                    }
                }

                _trigError.CLK = Status == (int) LidState.Error;
                if (_trigError.Q)
                {
                    EV.PostMessage(Module, EventEnum.DefaultAlarm, "Lid in error status");
                }

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