using System;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Routine;
using Aitex.Core.RT.SCCore;
using MECF.Framework.Common.Equipment;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.LoadLocks;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.UnLoad;
using SicModules.LLs;
using SicModules.LLs.Routines;
using SicModules.TMs;
using SicModules.UnLoads;
using SicModules.UnLoads.Routines;

namespace SicModules.EFEMs.Routines
{
    public class EfemSlitValveRoutine : EfemBaseRoutine
    {
        enum RoutineStep
        {
            ServoRoutine,
            LiftRoutine,
            CheckPressureCondition,
            CheckPressure,
            SetSlitValve,
            CheckPressureCondition2,
            TimeDlay1
        }

        private int _motionTimeout = 30;
        private ModuleName _paramTarget;    //目标腔体
        private ModuleName _robot;          //机械手
        private bool _paramIsOpen;          //是否开门
        private bool _bothPressureIsATM;    //判断两边压力是否都为ATM
        private SicTM _tm;
        private LoadLock _loadLock;
        private UnLoad _unLoad;
        private LoadLockServoToRoutine _llServoToRoutine;
        private UnLoadServoToRoutine _unLoadServoToRoutine;
        private LoadLockLiftRoutine _llLiftRoutine;
        private UnLoadLiftRoutine _unLoadLiftRoutine;


        private double _maxPressureDiffOpenSlitValve = SC.GetValue<double>("EFEM.MaxPressureDiffOpenSlitValve");

        public void Init(ModuleName targetModule,ModuleName robot, bool isOpen)
        {
            _paramTarget = targetModule;
            _robot = robot;
            _paramIsOpen = isOpen;
            Name = isOpen ? "Open Slit Valve" : "Close Slit Valve";
        }

        public EfemSlitValveRoutine()
        {
            Module = ModuleName.EFEM.ToString();
            Name = "EFEMSlitValue";
            _tm = DEVICE.GetDevice<SicTM>($"{ ModuleName.System.ToString()}.{ ModuleName.TM.ToString()}");
            _loadLock= DEVICE.GetDevice<SicLoadLock>($"{ ModuleName.LoadLock.ToString()}.{ ModuleName.LoadLock.ToString()}");
            _unLoad = DEVICE.GetDevice<SicUnLoad>($"{ ModuleName.UnLoad.ToString()}.{ ModuleName.UnLoad.ToString()}");
            _llServoToRoutine = new LoadLockServoToRoutine(ModuleName.LoadLock);
            _unLoadServoToRoutine = new UnLoadServoToRoutine(ModuleName.UnLoad);

            _llLiftRoutine = new LoadLockLiftRoutine();
            _unLoadLiftRoutine = new UnLoadLiftRoutine();
        }

        public override Result Start(params object[] objs)
        {
            Reset();

            //只有LoadLock和UnLoad需要开门
            if (_robot == ModuleName.WaferRobot)
            {
                if (_paramTarget != ModuleName.LoadLock  && _paramTarget != ModuleName.Load && _paramTarget != ModuleName.UnLoad)
                {
                    return Result.DONE;
                }
            }
            else if (_robot == ModuleName.TrayRobot)
            {
                if (_paramTarget != ModuleName.LoadLock)
                {
                    return Result.DONE;
                }
            }
            else
            {
                return Result.DONE;
            }

            if (_paramIsOpen)
            {
                if (EfemDevice.CheckSlitValveOpen(_paramTarget, _robot))
                {
                    return Result.DONE;
                }

                //判断TM到LoadLock或者UnLoad的门是否关上
                if (!_tm.CheckSlitValveClose(_paramTarget))
                {
                    EV.PostWarningLog(Module, $"can not open slit valve {_paramTarget},  {_paramTarget} to TM slit valve not closed, can not open at same time");
                    return Result.FAIL;
                }

                if (!EfemDevice.CheckSlitValveClose(ModuleName.LoadLock, ModuleName.WaferRobot)) //此门是必须要关上的
                {
                    EV.PostWarningLog(Module, $"can not open slit valve {_paramTarget},  {ModuleName.LoadLock} to Efem slit valve not closed, can not open at same time");
                    return Result.FAIL;
                }

                if (!EfemDevice.CheckSlitValveClose(ModuleName.UnLoad, _robot))
                {
                    EV.PostWarningLog(Module, $"can not open slit valve {_paramTarget},  {ModuleName.UnLoad} to Efem slit valve not closed, can not open at same time");
                    return Result.FAIL;
                }

                if (!EfemDevice.CheckSlitValveClose(ModuleName.LoadLock, _robot))
                {
                    EV.PostWarningLog(Module, $"can not open slit valve {_paramTarget},  {ModuleName.LoadLock} to Efem slit valve not closed, can not open at same time");
                    return Result.FAIL;
                }

                _motionTimeout = SC.GetValue<int>("System.SlitValveMotionTimeout");

                _llServoToRoutine.Init(SC.GetValue<double>("LoadLock.AtmPressureBase"));
                _unLoadServoToRoutine.Init(SC.GetValue<double>("UnLoad.AtmPressureBase"));
                _llLiftRoutine.Init(false);
                _unLoadLiftRoutine.Init(false);
            }
            else
            {
                if (EfemDevice.CheckSlitValveClose(_paramTarget, _robot))
                {
                    return Result.DONE;
                }
            }

            Notify("Start");
            return Result.RUN;
        }

        public override Result Monitor()
        {
            try
            {
                //开门之前需要对两边腔体进行气压调节,降Lift
                if (_paramIsOpen)
                {
                    if (_paramTarget == ModuleName.LoadLock)
                    {
                        CheckPressureConditionATM((int)RoutineStep.CheckPressureCondition, _paramTarget);//先判断Load腔是否在大气，因为EFEM始终在大气

                        if (!_bothPressureIsATM)
                        {
                            ExecuteRoutine((int)RoutineStep.ServoRoutine, _llServoToRoutine);
                            ExecuteRoutine((int)RoutineStep.LiftRoutine, _llLiftRoutine);
                        }
                    }
                    else if (_paramTarget == ModuleName.UnLoad)
                    {
                        CheckPressureConditionATM((int)RoutineStep.CheckPressureCondition, _paramTarget);//先判断UnLoad腔是否在大气

                        if (!_bothPressureIsATM)
                        {
                            ExecuteRoutine((int)RoutineStep.ServoRoutine, _unLoadServoToRoutine);
                            ExecuteRoutine((int)RoutineStep.LiftRoutine, _unLoadLiftRoutine);
                        }
                    }

                    CheckPressureCondition((int)RoutineStep.CheckPressureCondition2,_paramTarget);
                }

                SetSlitValve((int)RoutineStep.SetSlitValve, _paramTarget, _robot, _paramIsOpen, _motionTimeout);

                TimeDelay((int)RoutineStep.TimeDlay1, 1);

            }
            catch (RoutineBreakException)
            {
                return Result.RUN;
            }
            catch (RoutineFaildException)
            {
                return Result.FAIL;
            }

            Notify("Finished");
            return Result.DONE;

        }

        public void CheckPressureConditionATM(int id, ModuleName target)
        {
            Tuple<bool, Result> ret = Execute(id, () =>
            {
                Notify($"check pressure condition to open {target} slit valve");
                double targetPressure = 0.0;
                if (target == ModuleName.LoadLock)
                {
                    targetPressure = _loadLock.ChamberPressure;
                }
                else
                {
                    targetPressure = _unLoad.ChamberPressure;
                }

                _bothPressureIsATM = Math.Abs(EfemDevice.AtmPressureBase - targetPressure) <= _maxPressureDiffOpenSlitValve;

                return true;
            });

            if (ret.Item1)
            {
                if (ret.Item2 == Result.FAIL)
                {
                    throw (new RoutineFaildException());
                }
                else
                    throw (new RoutineBreakException());
            }
        }


        public void CheckPressureCondition(int id, ModuleName target)
        {
            Tuple<bool, Result> ret = Execute(id, () =>
            {
                Notify($"check {target} pressure is atm");
                double targetPressure = 0.0;
                if (target == ModuleName.LoadLock)
                {
                    targetPressure = _loadLock.ChamberPressure;
                }
                else
                {
                    targetPressure = _unLoad.ChamberPressure;
                }

                if (Math.Abs(EfemDevice.AtmPressureBase - targetPressure) > _maxPressureDiffOpenSlitValve)
                {
                    Stop($"pressure difference {Math.Abs(EfemDevice.AtmPressureBase - targetPressure):F3}  between Efem and {target} exceed tolerance {_maxPressureDiffOpenSlitValve}");
                    return false;
                }

                return true;
            });

            if (ret.Item1)
            {
                if (ret.Item2 == Result.FAIL)
                {
                    throw (new RoutineFaildException());
                }
                else
                    throw (new RoutineBreakException());
            }
        }


        public void SetSlitValve(int id, ModuleName module,ModuleName _robort, bool isOpen, int timeout)
        {
            Tuple<bool, Result> ret = ExecuteAndWait(id, () =>
            {
                Notify($"set slit valve {module} " + (isOpen ? "Open" : "Close"));

                if (!EfemDevice.SetSlitValve(module, _robort, isOpen, out string reason))
                {
                    Stop(reason);
                    return false;
                }

                return true;
            }, () =>
            {
                return isOpen ? EfemDevice.CheckSlitValveOpen(module, _robort) : EfemDevice.CheckSlitValveClose(module, _robort);

            }, timeout * 1000);

            if (ret.Item1)
            {
                if (ret.Item2 == Result.FAIL)
                {
                    throw (new RoutineFaildException());
                }
                else if (ret.Item2 == Result.TIMEOUT) //timeout
                {
                    Stop($"can not complete in {timeout} seconds");
                    throw (new RoutineFaildException());
                }
                else
                    throw (new RoutineBreakException());
            }
        }

    }

}
