using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Assets.Source.Core.Managers;
using UnityEngine;
using Object = System.Object;

namespace Assets.Source.Misc
{
    public enum CoroutinePlace
    {
        Undefined,
        Lobby,
        Level
    }

    public static class Run
    {
        private static Dictionary<CoroutinePlace, List<Coroutine>> _coroutineLookup = new Dictionary<CoroutinePlace, List<Coroutine>>();
        private static GameManager GM => GameManager.Instance;

        static Run()
        {
            foreach (var v in Enum.GetValues(typeof(CoroutinePlace)))
                _coroutineLookup.Add((CoroutinePlace)v, new List<Coroutine>());
        }

        public static Coroutine After(float delay, Action action, CoroutinePlace place)
        {
            return Coroutine(DelayRoutine(GM, delay, action), place);
        }

        public static Coroutine NextFixedUpdate(Action action, CoroutinePlace place)
        {
            return Coroutine(FixedUpdateRoutine(GM, action), place);
        }

        public static Coroutine Coroutine(IEnumerator routine, CoroutinePlace place)
        {
            if (GM == null || !GM.isActiveAndEnabled)
                return null;
            var c = GM.StartCoroutine(routine);
            _coroutineLookup[place].Add(c);
            return c;
        }

        public static void StopCoroutine(Coroutine routine)
        {
            if (GM == null || routine == null)
                return;
            GM.StopCoroutine(routine);
            var place = _coroutineLookup.FirstOrDefault(pair => pair.Value.Contains(routine)).Key;
            _coroutineLookup[place].Remove(routine);
        }

        public static void StopAllCoroutinesByPlace(CoroutinePlace place)
        {
            while (_coroutineLookup[place].Count > 0)
            {
                var routine = _coroutineLookup[place].First();
                if (routine != null)
                    GM.StopCoroutine(routine);
                _coroutineLookup[place].RemoveAt(0);
            }
        }

        static IEnumerator FixedUpdateRoutine(Object beh, Action action)
        {
            yield return new WaitForFixedUpdate();
            if (action != null && beh != null)
                action();
        }

        static IEnumerator DelayRoutine(Object beh, float delay, Action action)
        {
            yield return new WaitForSeconds(delay);
            if (action != null && beh != null)
                action();
        }
    }
}
