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 System;
using MECF.Framework.Common.DBCore;

namespace Venus_RT.Modules.TM
{
    class MFPlaceRoutine : ModuleRoutineBase, IRoutine
    {
        private enum PlaceStep
        {
            WaitModuleReady,
            PreRotation,
            ModulePrepare,
            WaitPressreDifference,
            OpenSlitDoor,
            Placing,
            QueryAwc,
            CloseSlitDoor,
            NotifyDone,
        }

        private readonly JetTM _JetTM;
        private readonly ITransferRobot _robot;

        private int _placingTimeout = 20 * 1000;
        private ModuleName _targetModule;
        private LLEntity _llModule;
        int _targetSlot;
        Hand _hand;

        private DateTime _starttime;
        private bool _queryAwc;

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

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

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

            Name = "Place";
            if (SC.GetValue<int>($"TM.QueryAWCOption") == 2 || SC.GetValue<int>($"TM.QueryAWCOption") == 3)
                _queryAwc = true;
            else
                _queryAwc = false;

            maxPressureDifference = SC.GetValue<double>("System.TMLLMaxPressureDifference");
        }
        public RState Start(params object[] objs)
        {
            _starttime = DateTime.Now;
            if (!_robot.IsHomed)
            {
                LOG.Write(eEvent.ERR_TM, Module, $"TM Robot is not homed, please home it first");
                return RState.Failed;
            }

            var placeItem = (Queue<MoveItem>)objs[0];
            if (!WaferManager.Instance.CheckDuplicatedWafersBeforeMove(placeItem))
                return RState.Failed;
            _targetModule = placeItem.Peek().DestinationModule;
            _targetSlot = placeItem.Peek().DestinationSlot;
            _hand = placeItem.Peek().RobotHand;

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

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

            if (WaferManager.Instance.CheckHasWafer(_targetModule, _targetSlot))
            {
                LOG.Write(eEvent.ERR_TM, Module, $"Cannot place as {_targetModule} Slot {_targetSlot + 1} already has a wafer");
                return RState.Failed;
            }

            Reset();
            _placingTimeout = SC.GetValue<int>($"TM.PlaceTimeout") * 1000;

            _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, $"Place to {_targetModule}");
        }

        public RState Monitor()
        {
            Runner.Wait(PlaceStep.WaitModuleReady,     () => _llModule.IsIdle,      _delay_60s)
                .RunIf(PlaceStep.PreRotation,          _JetTM.PreRotateModules.ContainsKey(_targetModule),      RotateArm,      WaitRotateDone)
                .Run(PlaceStep.ModulePrepare,          ModulePrepare,               IsModulePrepareReady)
                .Wait(PlaceStep.WaitPressreDifference, TMLLPressureIsOK, _delay_60s)
                .Run(PlaceStep.OpenSlitDoor,           OpenSlitDoor,                IsSlitDoorOpen)
                .Run(PlaceStep.Placing,                Placing,                     WaitPlaceDone)
                .Run(PlaceStep.QueryAwc,               QueryAwc,                    WaitQueryDoneAndRecord)
                .Run(PlaceStep.CloseSlitDoor,          CloseSlitDoor,               IsSlitDoorClosed)
                .End(PlaceStep.NotifyDone,             NotifyLLDone,                _delay_50ms);          
            return Runner.Status;
        }

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

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

        private bool OpenSlitDoor()
        {
            return _JetTM.TurnMFSlitDoor(_targetModule, true, out _);
        }
        private bool TMLLPressureIsOK()
        {
            double llPressure = 0;
            if (_targetModule == ModuleName.LLA)
            {
                llPressure = _JetTM.LLAPressure;
            }
            else if (_targetModule == ModuleName.LLB)
            {
                llPressure = _JetTM.LLBPressure;
            }
            if (Math.Abs((llPressure - _JetTM.ChamberPressure)) < maxPressureDifference)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        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 Placing()
        {
            return _robot.Place(_targetModule, _targetSlot, _hand);
        }

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

        private bool RotateArm()
        {
            _llModule.PostMsg(LLEntity.MSG.Prepare_TM); // Notify Loadlock to Serv pressure in advance for throughput enhancement
            return _robot.Goto(_JetTM.PreRotateModules[_targetModule], 0, _hand);
        }

        private bool WaitRotateDone()
        {
            if (_robot.Status == RState.Running)
            {
                if (Runner.StepElapsedMS > _placingTimeout)
                {
                    WaferManager.Instance.CreateDuplicatedWafer(ModuleName.TMRobot, (int)_hand, _targetModule, _targetSlot);
                    Runner.Stop($"TM Robot place wafer to {_targetModule}.{_targetSlot + 1} timeout, {_placingTimeout}ms");
                    return true;
                }

                return false;
            }
            else if (_robot.Status == RState.End)
            {
                return true;
            }
            else
            {
                WaferManager.Instance.CreateDuplicatedWafer(ModuleName.TMRobot, (int)_hand, _targetModule, _targetSlot);
                Runner.Stop($"TM Robot Rotate Arm failed, {_robot.Status}");
                return true;
            }
        }

        private bool QueryAwc()
        {
            if (!_queryAwc)
                return true;

            if (_robot.QueryAwc())
                return true;
            else
                return false;
        }

        private bool WaitQueryDoneAndRecord()
        {
            if (!_queryAwc)
                return true;

            if (_robot.Status == RState.Running)
            {
                return false;
            }
            else if (_robot.Status == RState.End)
            {
                //已经move后的数据
                string _origin_module = $"LP{WaferManager.Instance.GetWafer(_targetModule, _targetSlot).OriginStation}";
                int _origin_slot = WaferManager.Instance.GetWafer(_targetModule, _targetSlot).OriginSlot;
                //查询完毕 插入数据
                OffsetDataRecorder.RecordOffsetData(
                    Guid.NewGuid().ToString(),
                    ModuleName.TMRobot, 0,
                    _targetModule, _targetSlot,
                    _origin_module, _origin_slot,
                    _hand, RobotArmPan.None,
                    _robot.Offset_X, _robot.Offset_Y, _robot.Offset_D,
                    _starttime, DateTime.Now);
                return true;
            }
            else
            {
                Runner.Stop($"TM Robot Query Awc 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();
        }
    }
}