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 CyberX8_RT.Devices;
using CyberX8_RT.Devices.EFEM;
using CyberX8_Core;
using Aitex.Core.Common;
using Aitex.Core.Util;
using Aitex.Core.RT.Log;
namespace CyberX8_RT.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,
            CycleLoadUnloading
        }
        public enum MSG
        {
            Home,            // 0
            Map,             // 1
            ActionDone,      // 2
            Abort,           // 5
            Online,          // 6
            Error,           // 7
            Reset,
            EnterIdle,
            ReadCarrierId,
            WriteCarrierID,
            Load,
            Unload,
            Dock,
            Undock,
            Clamp,
            Unclamp,
            ToInit,
            ToIdle,
            ReadTagData,
            WriteTagData,
            CloseDoor,
            OpenDoor,
            CycleLoadUnload
        }
        public EfemBase JetEfem
        {
            get { return _efem; }
        }
        private readonly EfemBase _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; }
        }
        public override bool IsIdle
        {
            get { return FsmState == (int)STATE.Idle; }
        }
        public bool IsBusy
        {
            get { return !IsInit && !IsError && !IsIdle; }
        }
        /// 
        /// wafer尺寸
        /// 
        public WaferSize WaferSize
        {
            get { return _lpDevice.WaferSize; }
        }
        public bool IsRobotIdle
        {
            get { return Singleton.Instance.EFEM.RobotStatus == RState.End; }
        }
        private LoadPortHomeRoutine _home;
        private LoadPortMapRoutine _map;
        //private LoadPortCloseDoorRoutine _closeDoor;
        private LoadPortDockRoutine _dockRoutine;
        private LoadPortUnDockRoutine _undockRoutine;
        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 LoadPortCycleLoadUnloadRoutine _cycleLoadUnloadRoutine;
        //private bool _isInit;
        public LoadPortModule(ModuleName module, EfemBase efem) : base(SC.GetValue("EFEM.LoadPort.SlotNumber"))
        {
            Name = module.ToString();
            Module = module.ToString();
            _efem = efem;
            InitFsmMap();
            _lpDevice = new Loadport(module,efem);
            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}.Map", (cmd, args) => 
            {
                if (!Singleton.Instance.EFEM.IsHomed)
                {
                    LOG.WriteLog(eEvent.WARN_EFEM_ROBOT, Module, $"Efem is not homed,cannot map");
                    return true;
                }
                PostMsg(MSG.Map); 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; });
            OP.Subscribe($"{Name}.OpenDoor", (string cmd, object[] args) =>{return CheckToPostMessage((int)MSG.OpenDoor);});
            OP.Subscribe($"{Name}.CloseDoor", (string cmd, object[] args) =>{return CheckToPostMessage((int)MSG.CloseDoor);});
            OP.Subscribe($"{Module}.CycleLoadUnload", (string cmd, object[] args) => { return CheckToPostMessage((int)MSG.CycleLoadUnload,args); });
            DATA.Subscribe($"{Module}.Status", () => ((STATE)FsmState).ToString(), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.FsmState", () => ((STATE)FsmState).ToString(), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.FsmPrevState", () => ((STATE)FsmPreviousState).ToString(), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.FsmLastMessage", GetFsmLastMessage, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.State", () => ((STATE)FsmState).ToString(), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.IsError", ()=>IsError, SubscriptionAttribute.FLAG.SaveDB);
            DATA.Subscribe($"{Module}.IsIdle", () => IsIdle, SubscriptionAttribute.FLAG.SaveDB);
            DATA.Subscribe($"{Module}.IsBusy", () => IsBusy, SubscriptionAttribute.FLAG.SaveDB);
            InitRoutine();
        }
        private void InitRoutine()
        {
            _home = new LoadPortHomeRoutine(this);
            //_closeDoor = new LoadPortCloseDoorRoutine(this);
            _clamp = new LoadPortClampRoutine(this);
            //_dock = new LoadPortDockRoutine(this);
            _map = new LoadPortMapRoutine(this,_efem);
            //_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);
            _dockRoutine = new LoadPortDockRoutine(this);
            _undockRoutine = new LoadPortUnDockRoutine(this);
            _cycleLoadUnloadRoutine=new LoadPortCycleLoadUnloadRoutine(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.Idle);
            Transition(STATE.Error, MSG.Reset, FsmReset, STATE.Idle);
            Transition(STATE.Idle, MSG.Reset, FsmReset, STATE.Idle);
            AnyStateTransition(MSG.EnterIdle, FsmEnterIdle, 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);
            //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);
            //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);
            //Map
            Transition(STATE.Idle, MSG.Map, FsmStartMap, STATE.Mapping);
            Transition(STATE.Mapping, FSM_MSG.TIMER, FsmMonitorTask, STATE.Idle);
            Transition(STATE.Mapping, 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);
            //Cycle Load
            Transition(STATE.Idle, MSG.CycleLoadUnload, CycleLoadUnload, STATE.CycleLoadUnloading);
            Transition(STATE.CycleLoadUnloading, FSM_MSG.TIMER, CycleLoadUnloadMonitor, STATE.Idle);
            Transition(STATE.CycleLoadUnloading, MSG.Abort, CycleLoadUnloadAbort, STATE.Idle);
            EnumLoop.ForEach((item) =>
            {
                MapState((int)item, item.ToString());
            });
            EnumLoop.ForEach((item) =>
            {
                MapMessage((int)item, item.ToString());
            });
        }
        private bool FsmAbortTask(object[] param)
        {
            AbortRoutine();
            LPDevice.Abort();
            return true;
        }
        private bool FsmEnterIdle(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)
        {
            RState ret = MonitorRoutine();
            if (ret == RState.Failed || ret == RState.Timeout)
            {
                PostMsg(MSG.Error);
                return false;
            }
            return ret == RState.End;
        }
        private bool FsmStartHome(object[] param)
        {
            RState ret = StartRoutine(_home);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            //_isInit = false;
            return ret == RState.Running;
        }
        private bool FsmMonitorHomeTask(object[] param)
        {
            RState ret = MonitorRoutine();
            if (ret == RState.Failed||ret==RState.Timeout)
            {
                LPDevice.OnError();
                PostMsg(MSG.Error);
                return false;
            }
            if (ret == RState.End)
            {
                //_isInit = true;
                return true;
            }
            return false;
        }
        private bool FsmStartUndock(object[] param)
        {
            RState ret = StartRoutine(_undockRoutine);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            return ret == RState.Running;
        }
        private bool FsmStartDock(object[] param)
        {
            RState ret = StartRoutine(_dockRoutine);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            return ret == RState.Running;
        }
        private bool FsmStartMap(object[] param)
        {
            RState ret = StartRoutine(_map);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            return ret == RState.Running;
        }
        private bool FsmStartUnclamp(object[] param)
        {
            RState ret = StartRoutine(_unclamp);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            return ret == RState.Running;
        }
        private bool FsmStartClamp(object[] param)
        {
            _clamp.IsUnloadClamp = false;
            RState ret = StartRoutine(_clamp);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            return ret == RState.Running;
        }
        private bool FsmStartReadCarrierId(object[] param)
        {
            RState ret = StartRoutine(_readCarrierId);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            return ret == RState.Running;
        }
        private bool FsmStartWriteCarrierId(object[] param)
        {
            _writeCarrierId.CarrierIdSetPoint = (string) param[0];
            RState ret = StartRoutine(_writeCarrierId);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            return ret == RState.Running;
        }
        private bool FsmStartReadTagData(object[] param)
        {
            RState ret = StartRoutine(_readTag);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            return ret == RState.Running;
        }
        private bool FsmStartWriteTagData(object[] param)
        {
            _writeTag.TagDataSetPoint = (string)param[0];
            RState ret = StartRoutine(_writeTag);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            return ret == RState.Running;
        }
        private bool FsmStartUnload(object[] param)
        {
            RState ret = StartRoutine(_unload);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            if(ret == RState.Running)
            {
                _lpDevice.UnloadStart();
            }
            return ret == RState.Running;
        }
        private bool FsmStartLoad(object[] param)
        {
            RState ret = StartRoutine(_load);
            if (ret == RState.Failed || ret == RState.End)
                return false;
            if (ret == RState.Running)
            {
                _lpDevice.LoadStart();
            }
            return ret == RState.Running;
        }
        private bool CycleLoadUnload(object[] param)
        {
            return _cycleLoadUnloadRoutine.Start(param) == RState.Running;
        }
        private bool CycleLoadUnloadMonitor(object[] param)
        {
            RState state = _cycleLoadUnloadRoutine.Monitor();
            if(state==RState.Failed||state==RState.Timeout)
            {
                PostMsg(MSG.Error);
                return false;
            }
            return state == RState.End;
        }
        private bool CycleLoadUnloadAbort(object[] param)
        {
            _cycleLoadUnloadRoutine.Abort();
            return true;
        }
        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();
        }
    }
}