using Aitex.Core.RT.Routine;
using Aitex.Core.RT.SCCore;
using Aitex.Sorter.Common;
using Venus_RT.Devices;
using MECF.Framework.Common.Jobs;
using MECF.Framework.Common.Routine;
using MECF.Framework.Common.Equipment;
using MECF.Framework.Common.SubstrateTrackings;
using Venus_Core;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using MECF.Framework.Common.Schedulers;
using System.Collections.Generic;
using Venus_RT.Devices.EFEM;

namespace Venus_RT.Modules.EFEM
{
    class EfemSwapRoutine : ModuleRoutineBase, IRoutine
    {
        private enum SwapStep
        {
            WaitModuleReady,
            ModulePrepare,
            OpenSlitDoor,
            MoveWafer,
            WaitMaferMoved,
            CloseSlitDoor,
            NotifyDone,
        }

        private readonly EfemBase _efem;

        private int _moveTimeout = 20 * 1000;
        private ModuleName _targetModule;
        private LLEntity _llModule;
        private TMEntity _tmModule;


        Queue<MoveItem> _actionList = new Queue<MoveItem>();
        MoveItem _currentAction;
        private int _actionCount = 0;

        private int _autoPumpOptInWafer = 4;
        private int _autoPumpOptOutWafer = 0;

        private SequenceLLInOutPath _sequencePattern = SequenceLLInOutPath.DInDOut;
        private bool _bAutoMode = true;

        public EfemSwapRoutine(EfemBase efem) : base(ModuleName.EfemRobot)
        {
            _efem = efem;

            Name = "Swap";
        }
        public RState Start(params object[] objs)
        {
            if (!_efem.IsHomed)
            {
                LOG.Write(eEvent.ERR_EFEM_COMMON_FAILED, Module, $"EFEM is not homed, please home it first");
                return RState.Failed;
            }

            _actionList.Clear();
            foreach (var item in (Queue<MoveItem>)objs[0])
            {
                _actionList.Enqueue(new MoveItem(item.SourceModule, item.SourceSlot, item.DestinationModule, item.DestinationSlot, item.RobotHand));
            }

            if (!WaferManager.Instance.CheckDuplicatedWafersBeforeMove(_actionList))
                return RState.Failed;

            var firtItem = _actionList.Peek();
            if (ModuleHelper.IsLoadLock(firtItem.SourceModule))
                _targetModule = firtItem.SourceModule;
            else if (ModuleHelper.IsLoadLock(firtItem.DestinationModule))
                _targetModule = firtItem.DestinationModule;
            else
            {
                LOG.Write(eEvent.ERR_EFEM_ROBOT, Module, $"Invalid move parameter: {firtItem.SourceModule},{firtItem.SourceSlot + 1} => {firtItem.DestinationModule},{firtItem.DestinationSlot + 1} ");
                return RState.Failed;
            }

            _llModule = Singleton<RouteManager>.Instance.GetLL(_targetModule);
            if (_llModule == null)
            {
                LOG.Write(eEvent.ERR_EFEM_ROBOT, Module, $"Invalid Loadlock: {_targetModule}, maybe not installed");
                return RState.Failed;
            }

            _tmModule = Singleton<RouteManager>.Instance.GetTM();
            if(_tmModule == null)
            {
                LOG.Write(eEvent.ERR_EFEM_ROBOT, Module, $"Get TM Entity failed.");
                return RState.Failed;
            }
                

            _moveTimeout = SC.GetValue<int>("EFEM.MotionTimeout") * 1000;
            _autoPumpOptInWafer = SC.GetValue<int>("EFEM.LLAutoPumpInWaferOpt");
            _autoPumpOptOutWafer = SC.GetValue<int>("EFEM.LLAutoPumpOutWaferOpt");

            _sequencePattern = Singleton<RouteManager>.Instance.LLInOutPath;
            _bAutoMode = Singleton<RouteManager>.Instance.IsAutoMode;

            _actionCount = _actionList.Count;
            return Runner.Start(Module, $"EFEM Swap with {_targetModule}");
        }

        public RState Monitor()
        {
            Runner.Wait(SwapStep.WaitModuleReady,  () => _llModule.IsIdle,     _delay_60s)
                .Run(SwapStep.ModulePrepare,       ModulePrepare,              IsModulePrepareReady)
                .Run(SwapStep.OpenSlitDoor,        OpenSlitDoor,               IsSlitDoorOpen)
                .LoopStart(SwapStep.MoveWafer,     loopName(),                 _actionCount,           MoveWafer)
                .LoopEnd(SwapStep.WaitMaferMoved,  NullFun,                    WaitWaferMoved,         _moveTimeout + _delay_1s)
                .Run(SwapStep.CloseSlitDoor,       CloseSlitDoor,              IsSlitDoorClosed)
                .End(SwapStep.NotifyDone,          NotifyLLDone,               _delay_50ms);

            return Runner.Status;
        }

        private bool ModulePrepare()
        {
            _llModule.PostMsg(LLEntity.MSG.Prepare_EFEM);
            return true;
        }

        private string loopName()
        {
            return "EFEM Swap";
        }

        private bool IsModulePrepareReady()
        {
            return _llModule.Status == LLEntity.LLStatus.Ready_For_EFEM;
        }

        private bool OpenSlitDoor()
        {
            return _tmModule.TurnEFEMSlitDoor(_targetModule, true, out _);
        }

        private bool CloseSlitDoor()
        {
            return _tmModule.TurnEFEMSlitDoor(_targetModule, false, out _);
        }

        private bool IsSlitDoorOpen()
        {
            return _tmModule.IsLLSlitDoorOpen(_targetModule);
        }

        private bool IsSlitDoorClosed()
        {
            return _tmModule.IsLLSlitDoorClosed(_targetModule);
        }

        private bool VerifyWaferExistence(MoveItem item)
        {
            if (WaferManager.Instance.CheckHasWafer(item.DestinationModule, item.DestinationSlot))
            {
                LOG.Write(eEvent.ERR_EFEM_ROBOT, Module, $"Cannot move wafer as desitination {item.DestinationModule},{item.DestinationSlot} already a wafer: ");
                return false;
            }

            if (WaferManager.Instance.CheckNoWafer(item.SourceModule, item.SourceSlot))
            {
                LOG.Write(eEvent.ERR_EFEM_ROBOT, Module, $"Cannot move wafer as source {item.SourceModule}, {item.SourceSlot} has no wafer");
                return false;
            }

            return true;
        }

        private bool MoveWafer()
        {
            if(_actionList.Count <= 0)
            {
                Runner.Stop("no action");
                return true;
            }
            _currentAction = _actionList.Dequeue();

            if (!VerifyWaferExistence(_currentAction))
                return false;

            var wafer = WaferManager.Instance.GetWafer(_currentAction.SourceModule, _currentAction.SourceSlot);
            LOG.Write(eEvent.EV_EFEM_ROBOT, ModuleName.EfemRobot, $"{wafer.WaferOrigin} will be move from {_currentAction.SourceModule} {_currentAction.SourceSlot + 1}  to {_currentAction.DestinationModule} {_currentAction.DestinationSlot + 1}");

            if (ModuleHelper.IsLoadLock(_currentAction.SourceModule) && ModuleHelper.IsEFEMRobot(_currentAction.DestinationModule))
            {
                return _efem.Pick(_currentAction.SourceModule, _currentAction.SourceSlot, (Hand)_currentAction.DestinationSlot);
            }
            else if (ModuleHelper.IsEFEMRobot(_currentAction.SourceModule) && ModuleHelper.IsLoadLock(_currentAction.DestinationModule))
            {
                return _efem.Place(_currentAction.DestinationModule, _currentAction.DestinationSlot, (Hand)_currentAction.SourceSlot);
            }
            else
            {
                LOG.Write(eEvent.ERR_EFEM_ROBOT, ModuleName.EfemRobot, $"Invalid move parameter, source:{_currentAction.SourceModule},{_currentAction.SourceSlot}, destination: {_currentAction.DestinationModule}, {_currentAction.DestinationSlot}");
                return false;
            }
        }

        private bool WaitWaferMoved()
        {
            if (_efem.Status == RState.Running)
            {
                if(Runner.StepElapsedMS > _moveTimeout)
                {
                    WaferManager.Instance.CreateDuplicatedWafer(_currentAction.SourceModule, _currentAction.SourceSlot, _currentAction.DestinationModule, _currentAction.DestinationSlot);
                    Runner.Stop($"EFEM Robot moving wafer from {_currentAction.SourceModule}.{_currentAction.SourceSlot + 1} to {_currentAction.DestinationModule}.{_currentAction.DestinationSlot + 1}, {_moveTimeout}ms");
                    return true;
                }

                return false;
            }
            else if (_efem.Status == RState.End)
            {
                WaferManager.Instance.WaferMoved(_currentAction.SourceModule, _currentAction.SourceSlot, _currentAction.DestinationModule, _currentAction.DestinationSlot);
                return true;
            }
            else
            {
                WaferManager.Instance.CreateDuplicatedWafer(_currentAction.SourceModule, _currentAction.SourceSlot, _currentAction.DestinationModule, _currentAction.DestinationSlot);
                Runner.Stop($"EFEM Robot moving wafer failed, {_efem.Status}");
                return true;
            }
        }

        private bool NotifyLLDone()
        {
            var waferStatus = _llModule.GetWaferProcessStatus();

            bool bAutoPump = false;
            if (_bAutoMode)
            {
                if (((_sequencePattern == SequenceLLInOutPath.AInBOut && _targetModule == ModuleName.LLA) ||
                     (_sequencePattern == SequenceLLInOutPath.BInAOut && _targetModule == ModuleName.LLB)) &&
                      waferStatus.unprocessed >= _autoPumpOptInWafer)
                {
                    bAutoPump = true;
                }
                else if (((_sequencePattern == SequenceLLInOutPath.AInBOut && _targetModule == ModuleName.LLB) ||
                          (_sequencePattern == SequenceLLInOutPath.BInAOut && _targetModule == ModuleName.LLA)) &&
                           waferStatus.processed <= _autoPumpOptOutWafer)
                {
                    bAutoPump = true;
                }
                else if (_sequencePattern == SequenceLLInOutPath.DInDOut &&
                    waferStatus.processed   <= _autoPumpOptOutWafer &&
                    waferStatus.unprocessed >= _autoPumpOptInWafer)
                {
                    bAutoPump = true;
                }
            }

            LOG.Write(eEvent.EV_EFEM_ROBOT, Module, $"NotifyLLDone() => {_targetModule}, Sequence Pattern{_sequencePattern}, unprocessed wafer:{waferStatus.unprocessed}, processed wafer: {waferStatus.processed},bAutoPump  = {bAutoPump}, Config Option:{_autoPumpOptInWafer},{_autoPumpOptOutWafer}");
            _llModule.PostMsg(bAutoPump ? LLEntity.MSG.AutoPump : LLEntity.MSG.EFEM_Exchange_Ready);
            
            return true;
        }

        public void Abort()
        {
            _efem.Halt();
        }
    }
}