using Aitex.Core.RT.Routine;
using Aitex.Core.RT.SCCore;
using Aitex.Sorter.Common;
using Venus_RT.Devices;
using Aitex.Core.Common;
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 Venus_RT.Modules.PMs;
using MECF.Framework.Common.Schedulers;
using System.Collections.Generic;

namespace Venus_RT.Modules.TM
{
    class MFPMSwapRoutine : ModuleRoutineBase, IRoutine
    {
        private enum SwapStep
        {
            WaitPMReady,
            PreRotation,
            OpenSlitDoor,
            PickPrepare,
            PickExtend,
            DropDownWafer,
            PickDelay,
            PickRetract,
            PlacePrepare,
            PlaceExtend,
            LiftUpWafer,
            PlaceDelay,
            PlaceRetract,
            CloseSlitDoor,
            NotifyDone,
        }
        private enum SwapWithHeaterStep
        {
            WaitPMReady,
            PreRotation,
            OpenSlitDoor,
            PickPrepare,
            Picking,
            PlacePrepare,
            Placing,
            CloseSlitDoor,
            NotifyDone,
        }
        private readonly JetTM _JetTM;
        private readonly ITransferRobot _robot;

        private int _swapingTimeout = 120 * 1000;
        private int _placeDelayTime = 0;
        private int _pickDelayTime = 0;
        private ModuleName _targetModule;
        private PMEntity _pmModule;
        private int _targetSlot;
        private Hand _pickHand;
        private Hand _placeHand;

        public MFPMSwapRoutine(JetTM tm, ITransferRobot robot) : base(ModuleName.TMRobot)
        {
            _JetTM = tm;
            _robot = robot;
            Name = "Swap with PM";
        }
        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;
            }

            var swapItem = (Queue<MoveItem>)objs[0];
            _targetModule = swapItem.Peek().SourceModule;
            _targetSlot = swapItem.Peek().SourceSlot;
            _pickHand = swapItem.Peek().RobotHand;
            _placeHand = _pickHand == Hand.Blade2 ? Hand.Blade1 : Hand.Blade2;

            if (ModuleHelper.IsPm(_targetModule) && ModuleHelper.IsInstalled(_targetModule))
            {
                _pmModule = Singleton<RouteManager>.Instance.GetPM(_targetModule);
            }
            else
            {
                LOG.Write(eEvent.ERR_TM, Module, $"Invalid target module : {_targetModule} for swap action");
                return RState.Failed;
            }

            if (WaferManager.Instance.CheckNoWafer(ModuleName.TMRobot, (int)_placeHand))
            {
                LOG.Write(eEvent.ERR_TM, Module, $"Cannot Swap Wafer as TM Robot Arm: {_placeHand} has no wafer");
                return RState.Failed;
            }

            if (WaferManager.Instance.CheckHasWafer(ModuleName.TMRobot, (int)_pickHand))
            {
                LOG.Write(eEvent.ERR_TM, Module, $"Cannot Swap Wafer as TM Robot Arm: {_pickHand} has a wafer");
                return RState.Failed;
            }

            if (WaferManager.Instance.CheckNoWafer(_targetModule, _targetSlot))
            {
                LOG.Write(eEvent.ERR_TM, Module, $"Cannot Swap Wafer as {_targetModule} Slot {_targetSlot + 1} has no wafer");
                return RState.Failed;
            }

            var wafer = WaferManager.Instance.GetWafer(_targetModule, _targetSlot);
            if (wafer.ChuckState == EnumWaferChuckStatus.Chucked)
            {
                LOG.Write(eEvent.ERR_TM, Module, $"Cannot pick the wafer in {_targetModule} as the wafer is chucked");
                return RState.Failed;
            }

            Reset();
            _swapingTimeout = SC.GetValue<int>($"TM.SwapTimeout") * 1000;
            _pickDelayTime = SC.GetValue<int>($"{_targetModule}.PickDelayTime");
            _placeDelayTime = SC.GetValue<int>($"{_targetModule}.PlaceDelayTime");

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

        public RState Monitor()
        {
            //Runner.Wait(SwapStep.WaitPMReady,       () => _pmModule.IsIdle,                             _delay_60s)
            //    .RunIf(SwapStep.PreRotation,        _JetTM.PreRotateModules.ContainsKey(_targetModule), RotateArm,              WaitRotateDone)
            //    .Run(SwapStep.PickPrepare,          PickPrepare,                                        IsModuleReadyForPick)
            //    .Run(SwapStep.PickExtend,           PickExtend,                                         WaitRobotExtendDone)
            //    .Run(SwapStep.DropDownWafer,        NotifyPMPickWafer,                                  WaitPMWaferDropDown)
            //    .Delay(SwapStep.PickDelay,          _pickDelayTime)
            //    .Run(SwapStep.PickRetract,          PickRetract,                                        WaitRobotRetractDone)
            //    .Run(SwapStep.PlacePrepare,         PlacePrepare,                                       IsModuleReadyForPlace)
            //    .Run(SwapStep.PlaceExtend,          PlaceExtend,                                        WaitRobotExtendDone)
            //    .Run(SwapStep.LiftUpWafer,          NotifyLiftUpWafer,                                  WaitPMWaferLiftUp)
            //    .Delay(SwapStep.PlaceDelay,         _placeDelayTime)
            //    .Run(SwapStep.PlaceRetract,         PlaceRetract,                                       WaitRobotRetractDone)
            //    .End(SwapStep.NotifyDone,           NotifyPMDone,                                       _delay_50ms);


            switch (_pmModule.ChamberType)
            {
                case JetChamber.Venus:
                case JetChamber.Kepler2300:

                    Runner.Wait(SwapStep.WaitPMReady,     () => _pmModule.IsIdle,                             _delay_60s)
                          .RunIf(SwapStep.PreRotation,    _JetTM.PreRotateModules.ContainsKey(_targetModule), RotateArm, WaitRotateDone)
                          .Run(SwapStep.OpenSlitDoor,     OpenPMSlitDoor,                                     OpenPMSlitDoorIsOK)
                          .Run(SwapStep.PickPrepare,      PickPrepare,                                        IsModuleReadyForPick)
                          .Run(SwapStep.PickExtend,       PickExtend,                                         WaitRobotExtendDone)
                          .Run(SwapStep.DropDownWafer,    NotifyPMPickWafer,                                  WaitPMWaferDropDown)
                          .Delay(SwapStep.PickDelay,                                                          _pickDelayTime)
                          .Run(SwapStep.PickRetract,      PickRetract,                                        WaitRobotRetractDone)
                          .Run(SwapStep.PlacePrepare,     PlacePrepare,                                       IsModuleReadyForPlace)
                          .Run(SwapStep.PlaceExtend,      PlaceExtend,                                        WaitRobotExtendDone)
                          .Run(SwapStep.LiftUpWafer,      NotifyLiftUpWafer,                                  WaitPMWaferLiftUp)
                          .Delay(SwapStep.PlaceDelay,     _placeDelayTime)
                          .Run(SwapStep.PlaceRetract,     PlaceRetract,                                       WaitRobotRetractDone)
                          .Run(SwapStep.CloseSlitDoor,    ClosePMSlitDoor,                                    ClosePMSlitDoorIsOK)

                          .End(SwapStep.NotifyDone, NotifyPMDone, _delay_50ms);
                    break;

                case JetChamber.Kepler2200A:
                case JetChamber.Kepler2200B:

                    Runner.Wait(SwapWithHeaterStep.WaitPMReady,    () => _pmModule.IsIdle,              _delay_60s)
                          .RunIf(SwapWithHeaterStep.PreRotation,   _JetTM.PreRotateModules.ContainsKey(_targetModule), RotateArm, WaitRotateDone)
                          .Run(SwapWithHeaterStep.OpenSlitDoor,    OpenPMSlitDoor,                      OpenPMSlitDoorIsOK)
                          .Run(SwapWithHeaterStep.PickPrepare,     PickPrepare,                         IsModuleReadyForPick)
                          .Run(SwapWithHeaterStep.Picking,         Picking,                             WaitPickDone)
                          .Run(SwapWithHeaterStep.PlacePrepare,    PlacePrepare,                        IsModuleReadyForPlace)
                          .Run(SwapWithHeaterStep.Placing,         Placing,                             WaitPlaceDone)
                          .Run(SwapWithHeaterStep.CloseSlitDoor,   ClosePMSlitDoor,                     ClosePMSlitDoorIsOK)
                          .End(SwapStep.NotifyDone,                NotifyPMDone,                        _delay_50ms);
                    break;
            }
            return Runner.Status;
        }

        private bool PickPrepare()
        {
            _pmModule.PostMsg(PMEntity.MSG.PreparePick);
            return true;
        }
        private bool OpenPMSlitDoor()
        {
            return _JetTM.TurnMFSlitDoor(_targetModule, true, out _);
        }
        private bool OpenPMSlitDoorIsOK()
        {
            return _JetTM.IsPMSlitdoorOpened(_targetModule);
        }
        private bool ClosePMSlitDoor()
        {
            return _JetTM.TurnMFSlitDoor(_targetModule, false, out _);
        }
        private bool ClosePMSlitDoorIsOK()
        {
            return _JetTM.IsPMSlitdoorClosed(_targetModule);
        }
        private bool IsModuleReadyForPick()
        {
            return _pmModule.Status == PMEntity.PMStatus.Ready_For_Pick && _pmModule.IsSlitDoorOpen;
            //return _pmModule.Status == PMEntity.PMStatus.Ready_For_Pick;

        }

        private bool PickExtend()
        {
            return _robot.PickExtend(_targetModule, _targetSlot, _pickHand);
        }
        private bool PickRetract()
        {
            return _robot.PickRetract(_targetModule, _targetSlot, _pickHand);
        }

        private bool PlacePrepare()
        {
            _pmModule.PostMsg(PMEntity.MSG.PreparePlace);
            return true;
        }

        private bool IsModuleReadyForPlace()
        {
            return _pmModule.Status == PMEntity.PMStatus.Ready_For_Place && _pmModule.IsSlitDoorOpen;
            //return _pmModule.Status == PMEntity.PMStatus.Ready_For_Place;

        }

        private bool PlaceExtend()
        {
            return _robot.PlaceExtend(_targetModule, _targetSlot, _placeHand);
        }
        private bool PlaceRetract()
        {
            return _robot.PlaceRetract(_targetModule, _targetSlot, _placeHand);
        }

        private bool WaitRobotExtendDone()
        {
            if (_robot.Status == RState.Running)
            {
                return false;
            }
            else if (_robot.Status == RState.End)
            {
                return true;
            }
            else
            {
                Runner.Stop($"TM Robot Place Extend failed, {_robot.Status}");
                return true;
            }
        }
        private bool Picking()
        {
            return _robot.Pick(_targetModule, _targetSlot, _pickHand);
        }

        private bool WaitPickDone()
        {
            if (_robot.Status == RState.Running)
            {
                return false;
            }
            else if (_robot.Status == RState.End)
            {
                
                

                //WaferManager.Instance.WaferMoved(ModuleName.TM, (int)_hand, _targetModule, _targetSlot);
                WaferManager.Instance.WaferMoved(_targetModule, _targetSlot, ModuleName.TMRobot, (int)_pickHand);

                return true;
            }
            else
            {
                Runner.Stop($"TM Robot Picking failed, {_robot.Status}");
                return true;
            }
        }
        private bool Placing()
        {
            return _robot.Place(_targetModule, _targetSlot, _placeHand);
        }

        private bool WaitPlaceDone()
        {
            if (_robot.Status == RState.Running)
            {
                return false;
            }
            else if (_robot.Status == RState.End)
            {
                WaferManager.Instance.WaferMoved(ModuleName.TMRobot, (int)_placeHand, _targetModule, _targetSlot);
                return true;
            }
            else
            {
                Runner.Stop($"TM Robot Place failed, {_robot.Status}");
                return true;
            }
        }

        private bool RotateArm()
        {
            _pmModule.PostMsg(PMEntity.MSG.PreparePick); // Notify PM to Serv pressure in advance for throughput enhancement
            return _robot.Goto(_JetTM.PreRotateModules[_targetModule], 0, _placeHand);
        }

        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 NotifyPMPickWafer()
        {
            _pmModule.PostMsg(PMEntity.MSG.DropDownWafer);
            return true;
        }

        private bool WaitPMWaferDropDown()
        {
            if (_pmModule.Status == PMEntity.PMStatus.Exchange_Ready)
            {
                WaferManager.Instance.WaferMoved(_targetModule, _targetSlot, ModuleName.TMRobot, (int)_pickHand);
                return true;
            }

            return false;
        }

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

        private bool NotifyLiftUpWafer()
        {
            _pmModule.PostMsg(PMEntity.MSG.LiftUpWafer);
            return true;
        }
        private bool WaitPMWaferLiftUp()
        {
            if (_pmModule.Status == PMEntity.PMStatus.Exchange_Ready)
            {
                WaferManager.Instance.WaferMoved(ModuleName.TMRobot, (int)_placeHand, _targetModule, _targetSlot);
                return true;
            }

            return false;
        }

        private bool NotifyPMDone()
        {
            _pmModule.PostMsg(PMEntity.MSG.PlaceReady);
            return true;
        }

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