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

namespace Venus_RT.Modules
{
    class TMCycle : ModuleRoutineBase, IRoutine
    {
        enum TMCycleStep
        {
            Start,
            ReturnBack,
            Cycling,
            End,
        }

        private bool IsModuleAvailable(ModuleName module) => dictSchedulers.Keys.Contains(module) && dictSchedulers[module].IsAvailable;

        List<ModuleName> tmCycleRoutine = new List<ModuleName>() { ModuleName.LLA, ModuleName.PMA, ModuleName.PMB, ModuleName.LLB };
        int cycleCount = 1000;
        ModuleName _sourceModule = ModuleName.LLA;
        ModuleName _destinationModule = ModuleName.LLB;
        int _sourceSlotNumber = 4;
        int _destinationSlotNumber = 4;
        SchedulerTMRobot _TMRobot = (SchedulerTMRobot)Singleton<TransferModule>.Instance.GetScheduler(ModuleName.TMRobot);
        private readonly int INVALID_SLOT = -1;

        Dictionary<ModuleName, SchedulerModule> dictSchedulers = new Dictionary<ModuleName, SchedulerModule>();

        Queue<MoveItem> _ReturnWafers = new Queue<MoveItem>();
        Queue<MoveItem> _runningItems = new Queue<MoveItem>();
        Queue<MoveItem> _CycleWafers = new Queue<MoveItem>();

        public int? CycleIndex;
        public TMCycle() : base(ModuleName.System)
        {
            Name = "TM Cycle";

            void _initMoudle(ModuleName name, SchedulerModule sche)
            {
                if (ModuleHelper.IsInstalled(name))
                {
                    dictSchedulers[name] = sche;
                }
            }

            _initMoudle(ModuleName.LLA, new SchedulerLoadLock(ModuleName.LLA));
            _initMoudle(ModuleName.LLB, new SchedulerLoadLock(ModuleName.LLB));
            _initMoudle(ModuleName.PMA, new SchedulerPM(ModuleName.PMA));
            _initMoudle(ModuleName.PMB, new SchedulerPM(ModuleName.PMB));
            _initMoudle(ModuleName.PMC, new SchedulerPM(ModuleName.PMC));
            _initMoudle(ModuleName.PMD, new SchedulerPM(ModuleName.PMD));
        }

        public RState Start(params object[] objs)
        {
            CycleIndex = 0;
            if (objs.Length == 2)
            {
                var modules = ((string[])objs[0]).ToList();
                if (modules.Count >= 2)
                    tmCycleRoutine.Clear();

                foreach(var mod in modules)
                {
                    try
                    {
                        ModuleName module = ModuleHelper.Converter(mod);
                        tmCycleRoutine.Add(module);
                    }
                    catch
                    {
                        LOG.Write(eEvent.ERR_ROUTER, "TMCycle", $"Invalid module string: {mod}");
                        return RState.Failed;
                    }
                }
                cycleCount = (int)objs[1];
            }

            return Runner.Start(Module, Name);
        }

        public RState Monitor()
        {
            Runner.Run(TMCycleStep.Start,                      NullFun)
                .LoopStart(TMCycleStep.ReturnBack, "Cycle",    cycleCount, StartReturn, ReturnBack)
                .LoopEnd(TMCycleStep.Cycling,      StartCycle, Cycling)
                .End(TMCycleStep.End,              NullFun,    _delay_50ms);

            return Runner.Status;
        }

        private bool StartReturn()
        {
            CycleIndex += 1;
            _destinationModule = tmCycleRoutine.Last();
            _destinationSlotNumber = SC.GetValue<int>($"{_destinationModule}.SlotNumber");
            _sourceModule = tmCycleRoutine.First();
            _sourceSlotNumber = SC.GetValue<int>($"{_sourceModule}.SlotNumber");
            _ReturnWafers.Clear();
            for(int i = 0; i < _destinationSlotNumber; i++)
            {
                if(WaferManager.Instance.CheckHasWafer(_destinationModule, i))
                {
                    _ReturnWafers.Enqueue(new MoveItem(_destinationModule, i, _sourceModule, INVALID_SLOT, Hand.None));
                }
            }

            return true;
        }


        List<int> GetReadyInSlot(ModuleName module, int slotCount)
        {
            List<int> slots = new List<int>();
            for (int i = 0; i < slotCount; i++)
            {
                if (WaferManager.Instance.CheckNoWafer(module, i))
                    slots.Add(i);

                if (slots.Count >= 2)
                    return slots;
            }

            return slots;
        }

        List<int> GetReadyOutSlot(ModuleName module, int slotCount)
        {
            List<int> slots = new List<int>();
            for (int i = 0; i < slotCount; i++)
            {
                if (WaferManager.Instance.CheckHasWafer(module, i))
                    slots.Add(i);

                if (slots.Count >= 2)
                    return slots;
            }

            return slots;
        }

        List<ModuleName> GetReadyOutPMs()
        {
            List<ModuleName> outpm = new List<ModuleName>();
            foreach(var module in tmCycleRoutine)
            {
                if(ModuleHelper.IsPm(module))
                {
                    if(IsModuleAvailable(module) && WaferManager.Instance.CheckHasWafer(module, 0))
                    {
                        outpm.Add(module);
                        if (outpm.Count >= 2)
                            break;
                    }
                }
            }

            return outpm;
        }

        List<ModuleName> GetReadyInPMs()
        {
            List<ModuleName> inpm = new List<ModuleName>();
            foreach (var module in tmCycleRoutine)
            {
                if (ModuleHelper.IsPm(module))
                {
                    if (IsModuleAvailable(module) && WaferManager.Instance.CheckNoWafer(module, 0))
                    {
                        inpm.Add(module);
                        if (inpm.Count >= 3)
                            break;
                    }
                }
            }

            return inpm;
        }

        private bool ReturnBack()
        {
            var t1 = IsModuleAvailable(_sourceModule);
            var t2 = IsModuleAvailable(_destinationModule);
            var t3 = _TMRobot.IsAvailable;
            if (IsModuleAvailable(_sourceModule) && IsModuleAvailable(_destinationModule) && _TMRobot.IsAvailable)
            {

                var InSlots = GetReadyInSlot(_sourceModule, _sourceSlotNumber);
                var OutSlots = GetReadyOutSlot(_destinationModule, _destinationSlotNumber);
                if (InSlots.Count == 0 || OutSlots.Count == 0)
                    return true;

                Queue<MoveItem> items = new Queue<MoveItem>();
                for(int i = 0; i < Math.Min(InSlots.Count, OutSlots.Count); i++)
                {
                    items.Enqueue(new MoveItem(_destinationModule, OutSlots[i], _sourceModule, InSlots[i], (Hand)i));
                }

                if(items.Count > 0)
                {
                    _TMRobot.PostMoveItems(items.ToArray());
                }
            }

            return false;
        }

        private bool ModuleHasWafer(ModuleName mod, int nSlots)
        {
            for(int i= 0; i< nSlots; i++)
            {
                if (WaferManager.Instance.CheckHasWafer(mod, i))
                    return true;
            }

            return false;
        }

        private bool PMsHasWafers()
        {
            foreach (var module in tmCycleRoutine)
            {
                if (ModuleHelper.IsPm(module) && ModuleHelper.IsInstalled(module))
                {
                    if (WaferManager.Instance.CheckHasWafer(module, 0))
                        return true;

                }
            }

            return false;

        }

        private bool IsPMsAvailabe()
        {
            foreach (var module in tmCycleRoutine)
            {
                if (ModuleHelper.IsPm(module)&&ModuleHelper.IsInstalled(module))
                {
                    if (!IsModuleAvailable(module))
                        return false;

                }
            }

            return true;
        }

        private bool Cycling()
        {
            if(IsPMsAvailabe() && IsModuleAvailable(_destinationModule) && IsModuleAvailable(_sourceModule) && _TMRobot.IsAvailable)
            {
                if (!PMsHasWafers() && _TMRobot.IsAvailable && _CycleWafers.Count == 0)
                    return true;

                if(PMsHasWafers())
                {
                    var pmSlots = GetReadyOutPMs();
                    var inSlots = GetReadyInSlot(_destinationModule, _destinationSlotNumber);
                    for(int i = 0; i < Math.Min(pmSlots.Count, inSlots.Count); i++)
                    {
                        _runningItems.Enqueue(new MoveItem(pmSlots[i], 0, _destinationModule, inSlots[i], Hand.Both));
                    }
                }
                else
                {
                    var InPMs = GetReadyInPMs();
                    if(_CycleWafers.Count > 0 && InPMs.Count >= 1)
                    {
                        var item = _CycleWafers.Dequeue();
                        _runningItems.Enqueue(new MoveItem(item.SourceModule, item.SourceSlot, InPMs[0], 0, Hand.Both));
                    }

                    if(_CycleWafers.Count > 0 && InPMs.Count >= 2)
                    {
                        var item = _CycleWafers.Dequeue();
                        _runningItems.Enqueue(new MoveItem(item.SourceModule, item.SourceSlot, InPMs[1], 0, Hand.Both));
                    }

                }

                if (_runningItems.Count > 0)
                {
                    if (_TMRobot.PostMoveItems(_runningItems.ToArray()))
                        _runningItems.Clear();
                }
            }

            return false;
        }

        private bool StartCycle()
        {
            _CycleWafers.Clear();
            for(int i = 0; i< _sourceSlotNumber; i++)
            {
                if (WaferManager.Instance.CheckHasWafer(_sourceModule, i))
                    _CycleWafers.Enqueue(new MoveItem(_sourceModule, i, _destinationModule, INVALID_SLOT, Hand.None));
            }

            return true;
        }

        public void Abort()
        {
            CycleIndex = null;
            _TMRobot._entityTaskToken = (int)FSM_MSG.NONE;
        }
    }
}