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 System;
using System.Text;
using System.Collections.Generic;
using MECF.Framework.Common.Schedulers;

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

        private readonly JetTM _JetTM;
        private readonly ITransferRobot _robot;

        private int _moveTimeout = 30 * 1000;
        private ModuleName _targetModule;
        private LLEntity _llModule;

        private int _autoVentOptInWafer = 0;
        private int _autoVentOptOutWafer = 4;

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


        Queue<MoveItem> _actionList = new Queue<MoveItem>();
        MoveItem _currentAction;

        public MFSwapRoutine(JetTM tm, ITransferRobot robot) : base(ModuleName.TMRobot)
        {
            _JetTM = tm;
            _robot = robot;

            Name = "Swap";
        }
        public RState Start(params object[] objs)
        {
            if (!_robot.IsHomed)
            {
                LOG.Write(eEvent.ERR_TM, Module, $"TM Robot 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_TM, 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_TM, Module, $"Invalid Loadlock: {_targetModule}, maybe not installed");
                return RState.Failed;
            }

            Reset();

            _autoVentOptInWafer = SC.GetValue<int>("TM.LLAutoVentInWaferOpt");
            _autoVentOptOutWafer = SC.GetValue<int>("TM.LLAutoVentOutWaferOpt");

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

            return Runner.Start(Module, $"Swap with {_targetModule}");
        }

        public RState Monitor()
        {
            Runner.Wait(SwapStep.WaitModuleReady,   () => _llModule.IsIdle,         _delay_60s)
                .RunIf(SwapStep.PreRotation,        _JetTM.PreRotateModules.ContainsKey(_targetModule),         RotateArm,  WaitRotateDone)
                .Run(SwapStep.ModulePrepare,        ModulePrepare,                  IsModulePrepareReady)
                .Run(SwapStep.OpenSlitDoor,         OpenSlitDoor,                   IsSlitDoorOpen)
                .LoopStart(SwapStep.MoveWafer,      loopName(),                     _actionList.Count,          MoveWafer)
                .LoopEnd(SwapStep.WaitMaferMoved,   NullFun,                        WaitWaferMoved)
                .Run(SwapStep.CloseSlitDoor,        CloseSlitDoor,                  IsSlitDoorClosed)
                .End(SwapStep.NotifyDone,           NotifyLLDone,                   _delay_50ms);
                
            return Runner.Status;
        }

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

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

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

        private bool OpenSlitDoor()
        {
            return _JetTM.TurnMFSlitDoor(_targetModule, true, out _);
        }

        private bool CloseSlitDoor()
        {
            return _JetTM.TurnMFSlitDoor(_targetModule, false, out _);
        }

        private bool IsSlitDoorOpen()
        {
            if (_targetModule == ModuleName.LLA)
                return _JetTM.IsLLASlitDoorOpen;
            else
                return _JetTM.IsLLBSlitDoorOpen;
        }

        private bool IsSlitDoorClosed()
        {
            if (_targetModule == ModuleName.LLA)
                return _JetTM.IsLLASlitDoorClosed;
            else
                return _JetTM.IsLLBSlitDoorClosed;
        }

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

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

            return true;
        }

        private bool MoveWafer()
        {
            _currentAction = _actionList.Dequeue();

            if (!VerifyWaferExistence(_currentAction))
                return false;

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

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

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

                return false;
            }
            else if (_robot.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($"TM Robot moving wafer failed, {_robot.Status}");
                return true;
            }
        }

        private bool RotateArm()
        {
            ModuleName preModule = _targetModule;
            Hand rotateHand = Hand.Blade1;
            if (ModuleHelper.IsLoadLock(_actionList.Peek().DestinationModule) && ModuleHelper.IsTMRobot(_actionList.Peek().SourceModule))
            {
                rotateHand = (Hand)_actionList.Peek().SourceSlot;
                preModule = _JetTM.PreRotateModules[_targetModule];
            }
            else
            {
                rotateHand = (Hand)_actionList.Peek().DestinationSlot;
            }
                
            _llModule.PostMsg(LLEntity.MSG.Prepare_TM); // Notify Loadlock to Serv pressure in advance for throughput enhancement
            return _robot.Goto(preModule, 0, rotateHand);
        }

        private bool WaitRotateDone()
        {
            if (_robot.Status == RState.Running)
            {
                return false;
            }
            else if (_robot.Status == RState.End)
            {
                return true;
            }
            else
            {
                Runner.Stop($"TM Robot Rotate Arm failed, {_robot.Status}");
                return true;
            }
        }

        private bool NotifyLLDone()
        {
            bool bAutoVent = false;
            var waferStatus = _llModule.GetWaferProcessStatus();
            if(_bAutoMode)
            {
                if (((_sequencePattern == SequenceLLInOutPath.AInBOut && _targetModule == ModuleName.LLA) || 
                     (_sequencePattern == SequenceLLInOutPath.BInAOut && _targetModule == ModuleName.LLB)) &&
                      waferStatus.unprocessed <= _autoVentOptInWafer)
                {
                    bAutoVent = true;
                }
                else if (((_sequencePattern == SequenceLLInOutPath.AInBOut && _targetModule == ModuleName.LLB) ||
                          (_sequencePattern == SequenceLLInOutPath.BInAOut && _targetModule == ModuleName.LLA)) &&
                           waferStatus.processed >= _autoVentOptOutWafer)
                {
                    bAutoVent = true;
                }
                else if(_sequencePattern == SequenceLLInOutPath.DInDOut && 
                    waferStatus.processed   >= _autoVentOptOutWafer && 
                    waferStatus.unprocessed <= _autoVentOptInWafer)
                {
                    bAutoVent = true;
                }
            }

            LOG.Write(eEvent.INFO_TM, Module, $"NotifyLLDone() => {_targetModule}, Sequence Pattern{_sequencePattern}, unprocessed wafer:{waferStatus.unprocessed}, processed wafer: {waferStatus.processed},bAutoVent  = {bAutoVent}, Config Option:{_autoVentOptInWafer},{_autoVentOptOutWafer}");
            _llModule.PostMsg(bAutoVent ? LLEntity.MSG.AutoVent : LLEntity.MSG.TM_Exchange_Ready);
            return true;
        }

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