using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Fsm;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.Routine;
using Aitex.Core.Utilities;
using Aitex.Sorter.Common;
using MECF.Framework.Common.Equipment;
using MECF.Framework.Common.Schedulers;
using MECF.Framework.RT.ModuleLibrary.LPModules;
using System;
using Aitex.Core.RT.SCCore;
using VirgoRT.Devices.EFEM;
namespace VirgoRT.Modules.LPs
{
    /// 
    /// LP entity
    /// 
    sealed class LoadPortModule : LoadPortModuleBase
    {
        public event Action OnEnterError;
        private enum STATE
        {
            Unknown,
            Idle,            // 1
            Init,
            Error,
            Homing,    // 2
            Mapping,         // 4
            Loading,
            ReadingCarrierId,
            Unloading,
            Docking,
            Undocking,
            Clamping,
            Unclamping,
            WritingCarrierId,
            ReadingTagData,
            WritingTagData,
        }
        public enum MSG
        {
            Home,            // 0
            Map,             // 1
            ActionDone,      // 2
            Abort,           // 5
            Online,          // 6
            Error,           // 7
            Reset,
            ReadCarrierId,
            WriteCarrierID,
            Load,
            Unload,
            Dock,
            Undock,
            Clamp,
            Unclamp,
            ToInit,
            ToIdle,
            ReadTagData,
            WriteTagData,
        }
        private readonly Efem _efem;
        private Loadport _lpDevice;
        public Loadport LPDevice
        {
            get
            {
                return _lpDevice;
            }
        }
        public override bool IsReady
        {
            get { return FsmState == (int)STATE.Idle && CheckAllMessageProcessed(); }
        }
        public override bool IsError
        {
            get { return FsmState == (int)STATE.Error; }
        }
        public override bool IsInit
        {
            get { return FsmState == (int)STATE.Init; }
        }
        private LoadPortHomeRoutine _home;
        //private LoadPortCloseDoorRoutine _closeDoor;
        private LoadPortClampRoutine _clamp;
        private LoadPortReadCarrierIdRoutine _readCarrierId;
        private LoadPortWriteCarrierIdRoutine _writeCarrierId;
        private LoadPortReadTagDataRoutine _readTag;
        private LoadPortWriteTagDataRoutine _writeTag;
        private LoadPortUnclampRoutine _unclamp;
        //private LoadPortUndockRoutine _undock;
        private LoadPortLoadRoutine _load;
        private LoadPortUnloadRoutine _unload;
        //private bool _isInit;
        public LoadPortModule(ModuleName module, Efem efem) : base(SC.GetValue("EFEM.LoadPort.SlotNumber"))
        {
            Name = module.ToString();
            Module = module.ToString();
            _efem = efem;
            InitFsmMap();
            OP.Subscribe($"{Module}.ReadCarrierId", (cmd, args) => { PostMsg(MSG.ReadCarrierId); return true; });
            OP.Subscribe($"{Module}.WriteCarrierID", (cmd, args) => { PostMsg(MSG.WriteCarrierID, args[0]); return true; });
            OP.Subscribe($"{Module}.ReadTagData", (cmd, args) => { PostMsg(MSG.ReadTagData); return true; });
            OP.Subscribe($"{Module}.WriteTagData", (cmd, args) => { PostMsg(MSG.WriteTagData, args[0]); return true; });
            OP.Subscribe($"{Module}.Load", (cmd, args) => { PostMsg(MSG.Load); return true; });
            OP.Subscribe($"{Module}.Unload", (cmd, args) => { PostMsg(MSG.Unload); return true; });
            OP.Subscribe($"{Module}.Clamp", (cmd, args) => { PostMsg(MSG.Clamp); return true; });
            OP.Subscribe($"{Module}.Unclamp", (cmd, args) => { PostMsg(MSG.Unclamp); return true; });
            OP.Subscribe($"{Module}.Dock", (cmd, args) => { PostMsg(MSG.Dock); return true; });
            OP.Subscribe($"{Module}.Undock", (cmd, args) => { PostMsg(MSG.Undock); return true; });
            OP.Subscribe($"{Module}.Home", (cmd, args) => { PostMsg(MSG.Home); return true; });
            OP.Subscribe($"{Module}.Abort", (cmd, args) => { PostMsg(MSG.Abort); return true; });
            OP.Subscribe($"{Module}.Online", (cmd, args) => { PostMsg(MSG.Online); return true; });
            DATA.Subscribe($"{Module}.Status", () => ((STATE)FsmState).ToString());
            DATA.Subscribe($"{Module}.FsmState", () => ((STATE)FsmState).ToString());
            DATA.Subscribe($"{Module}.FsmPrevState", () => ((STATE)FsmPreviousState).ToString());
            DATA.Subscribe($"{Module}.FsmLastMessage", GetFsmLastMessage);
            _lpDevice = efem[module] as Loadport;
            InitRoutine();
        }
        private void InitRoutine()
        {
            _home = new LoadPortHomeRoutine(this);
            //_closeDoor = new LoadPortCloseDoorRoutine(this);
            _clamp = new LoadPortClampRoutine(this);
            //_dock = new LoadPortDockRoutine(this);
            //_map = new LoadPortMapRoutine(this);
            //_openDoor = new LoadPortOpenDoorRoutine(this);
            _readCarrierId = new LoadPortReadCarrierIdRoutine(this);
            _writeCarrierId = new LoadPortWriteCarrierIdRoutine(this);
            _readTag = new LoadPortReadTagDataRoutine(this);
            _writeTag = new LoadPortWriteTagDataRoutine(this);
            _unclamp = new LoadPortUnclampRoutine(this);
            //_undock = new LoadPortUndockRoutine(this);
            //_MapDT = new LoadPortGetMapInfoRoutine(this);
            _load = new LoadPortLoadRoutine(this);
            _unload = new LoadPortUnloadRoutine(this);
        }
        private string GetFsmLastMessage()
        {
            int msg = FsmLastMessage;
            if (msg >= (int)MSG.Home && msg <= (int)MSG.Error)
                return ((MSG)msg).ToString();
            if (msg == (int)FSM_MSG.TIMER)
                return "Timer";
            return msg.ToString();
        }
        private void InitFsmMap()
        {
            EnableFsm(50, STATE.Idle);
            //online
            AnyStateTransition(MSG.Online, fnOnline, FSM_STATE.SAME);
            //Error
            AnyStateTransition(MSG.Error, FsmOnError, STATE.Error);
            Transition(STATE.Error, MSG.Reset, FsmReset, STATE.Idle);
            EnterExitTransition(STATE.Error, FsmEnterError, FSM_MSG.NONE, FsmExitError);
            //Init
            Transition(STATE.Init, MSG.Home, FsmStartHome, STATE.Homing);
            Transition(STATE.Error, MSG.Home, FsmStartHome, STATE.Homing);
            Transition(STATE.Idle, MSG.Home, FsmStartHome, STATE.Homing);
            Transition(STATE.Homing, FSM_MSG.TIMER, FsmMonitorHomeTask, STATE.Idle);
            Transition(STATE.Homing, MSG.Error, null, STATE.Init);
            Transition(STATE.Homing, MSG.Abort, FsmAbortTask, STATE.Init);
            //load
            Transition(STATE.Idle, MSG.Load, FsmStartLoad, STATE.Loading);
            Transition(STATE.Loading, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.Loading, MSG.Abort, FsmAbortTask, STATE.Idle);
            //unload
            Transition(STATE.Idle, MSG.Unload, FsmStartUnload, STATE.Unloading);
            Transition(STATE.Unloading, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.Unloading, MSG.Abort, FsmAbortTask, STATE.Idle);
            //read carrier id
            Transition(STATE.Idle, MSG.ReadCarrierId, FsmStartReadCarrierId, STATE.ReadingCarrierId);
            Transition(STATE.ReadingCarrierId, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.ReadingCarrierId, MSG.Abort, FsmAbortTask, STATE.Idle);
            //clamp
            Transition(STATE.Idle, MSG.Clamp, FsmStartClamp, STATE.Clamping);
            Transition(STATE.Clamping, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.Clamping, MSG.Abort, FsmAbortTask, STATE.Idle);
            //unclamp
            Transition(STATE.Idle, MSG.Unclamp, FsmStartUnclamp, STATE.Unclamping);
            Transition(STATE.Unclamping, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.Unclamping, MSG.Abort, FsmAbortTask, STATE.Idle);
            //dock
            Transition(STATE.Idle, MSG.Dock, FsmStartDock, STATE.Docking);
            Transition(STATE.Docking, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.Docking, MSG.Abort, FsmAbortTask, STATE.Idle);
            //undock
            Transition(STATE.Idle, MSG.Undock, FsmStartUndock, STATE.Undocking);
            Transition(STATE.Undocking, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.Undocking, MSG.Abort, FsmAbortTask, STATE.Idle);
            //write carrier id
            Transition(STATE.Idle, MSG.WriteCarrierID, FsmStartWriteCarrierId, STATE.WritingCarrierId);
            Transition(STATE.WritingCarrierId, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.WritingCarrierId, MSG.Abort, FsmAbortTask, STATE.Idle);
            //read tag data
            Transition(STATE.Idle, MSG.ReadTagData, FsmStartReadTagData, STATE.ReadingTagData);
            Transition(STATE.ReadingTagData, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.ReadingTagData, MSG.Abort, FsmAbortTask, STATE.Idle);
            //write tag data
            Transition(STATE.Idle, MSG.WriteTagData, FsmStartWriteTagData, STATE.WritingTagData);
            Transition(STATE.WritingTagData, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.WritingTagData, MSG.Abort, FsmAbortTask, STATE.Idle);
            EnumLoop.ForEach((item) =>
            {
                MapState((int)item, item.ToString());
            });
            EnumLoop.ForEach((item) =>
            {
                MapMessage((int)item, item.ToString());
            });
        }
        private bool FsmAbortTask(object[] param)
        {
            return true;
        }
        private bool FsmReset(object[] param)
        {
            //if (!_isInit)
            //{
            //    PostMsg(MSG.ToInit);
            //    return false;
            //}
            return true;
        }
        private bool FsmOnError(object[] param)
        {
            if (FsmState == (int)STATE.Error)
            {
                return false;
            }
            AbortRoutine();
            if (FsmState == (int)STATE.Init)
                return false;
            return true;
        }
        private bool FsmExitError(object[] param)
        {
            return true;
        }
        private bool FsmEnterError(object[] param)
        {
            if (OnEnterError != null)
                OnEnterError(Module);
            return true;
        }
        private bool FsmMonitorTask(object[] param)
        {
            Result ret = MonitorRoutine();
            if (ret == Result.FAIL)
            {
                PostMsg(MSG.Error);
                return false;
            }
            return ret == Result.DONE;
        }
        private bool FsmStartHome(object[] param)
        {
            Result ret = StartRoutine(_home);
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            //_isInit = false;
            return ret == Result.RUN;
        }
        private bool FsmMonitorHomeTask(object[] param)
        {
            Result ret = MonitorRoutine();
            if (ret == Result.FAIL)
            {
                PostMsg(MSG.Error);
                return false;
            }
            if (ret == Result.DONE)
            {
                //_isInit = true;
                return true;
            }
            return false;
        }
        private bool FsmStartUndock(object[] param)
        {
            QueueRoutine.Clear();
            QueueRoutine.Enqueue(_unload);
            _clamp.IsUnloadClamp = true;
             QueueRoutine.Enqueue(_clamp);
             Result ret = StartRoutine();
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            return ret == Result.RUN;
        }
        private bool FsmStartDock(object[] param)
        {
            //Result ret = StartRoutine(_dock);
            //if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            //return ret == Result.RUN;
        }
        private bool FsmStartUnclamp(object[] param)
        {
            Result ret = StartRoutine(_unclamp);
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            return ret == Result.RUN;
        }
        private bool FsmStartClamp(object[] param)
        {
            _clamp.IsUnloadClamp = false;
            Result ret = StartRoutine(_clamp);
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            return ret == Result.RUN;
        }
        private bool FsmStartReadCarrierId(object[] param)
        {
            Result ret = StartRoutine(_readCarrierId);
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            return ret == Result.RUN;
        }
        private bool FsmStartWriteCarrierId(object[] param)
        {
            _writeCarrierId.CarrierIdSetPoint = (string) param[0];
            Result ret = StartRoutine(_writeCarrierId);
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            return ret == Result.RUN;
        }
        private bool FsmStartReadTagData(object[] param)
        {
            Result ret = StartRoutine(_readTag);
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            return ret == Result.RUN;
        }
        private bool FsmStartWriteTagData(object[] param)
        {
            _writeTag.TagDataSetPoint = (string)param[0];
            Result ret = StartRoutine(_writeTag);
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            return ret == Result.RUN;
        }
        private bool FsmStartUnload(object[] param)
        {
            Result ret = StartRoutine(_unload);
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            return ret == Result.RUN;
        }
        private bool FsmStartLoad(object[] param)
        {
            Result ret = StartRoutine(_load);
            if (ret == Result.FAIL || ret == Result.DONE)
                return false;
            return ret == Result.RUN;
        }
        private bool fnOnline(object[] param)
        {
            bool online = (bool)param[0];
            _efem.SetOnline(ModuleHelper.Converter(Module), online);
            return true;
        }
        public bool IsPrepareTransferReady(ModuleName module, EnumTransferType type, int slot)
        {
            if (type == EnumTransferType.Pick)
            {
                //需要补充:判断LP 放好了,而且已经map过。
                return _efem[module].HasCassette && _efem[module].IsMapped && _efem[module].IsThicknessValid;
            }
            else if (type == EnumTransferType.Place)
            {
                //需要补充:判断LP 放好了,而且已经map过。
                return _efem[module].HasCassette && _efem[module].IsMapped && _efem[module].IsThicknessValid;
            }
            return false;
        }
        internal bool CheckReadyRunNewJob(ModuleName module)
        {
            return _efem[module].HasCassette && _efem[module].IsMapped && _efem[module].IsThicknessValid;
        }
        internal bool CheckReadyTransfer(ModuleName module)
        {
            return _efem[module].HasCassette && _efem[module].IsMapped && _efem[module].IsThicknessValid;
        }
        public override bool Home(out string reason)
        {
            throw new NotImplementedException();
        }
        public override void NoteJobStart()
        {
            throw new NotImplementedException();
        }
        public override void NoteJobComplete()
        {
            throw new NotImplementedException();
        }
        public override bool PrepareTransfer(ModuleName robot, Hand blade, int targetSlot, EnumTransferType transferType, out string reason)
        {
            throw new NotImplementedException();
        }
        public override bool TransferHandoff(ModuleName robot, Hand blade, int targetSlot, EnumTransferType transferType, out string reason)
        {
            throw new NotImplementedException();
        }
        public override bool PostTransfer(ModuleName robot, Hand blade, int targetSlot, EnumTransferType transferType, out string reason)
        {
            throw new NotImplementedException();
        }
        public override bool CheckReadyForTransfer(ModuleName robot, Hand blade, int targetSlot, EnumTransferType transferType,
            out string reason)
        {
            throw new NotImplementedException();
        }
        public override void NoteTransferStart(ModuleName robot, Hand blade, int targetSlot, EnumTransferType transferType)
        {
            throw new NotImplementedException();
        }
        public override void NoteTransferStop(ModuleName robot, Hand blade, int targetSlot, EnumTransferType transferType)
        {
            throw new NotImplementedException();
        }
        public override bool CheckReadyForMap(ModuleName robot, Hand blade, out string reason)
        {
            throw new NotImplementedException();
        }
    }
}