using System;
using Assets.Source.Misc;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Assets.Source.Patterns
{
    public class Singleton<T> : MonoBehaviour, IReleaseTracker where T : MonoBehaviour
    {
        protected static T _instance;

        protected static object _lock = new object();

        public static T Instance
        {
            get
            {
                if (!Application.isPlaying)
                {
                    if (_instance == null)
                        _instance = _instance = FindObjectOfType<T>();
                    return _instance;
                }

                //if (applicationIsQuitting)
                //{
                //    //Debug.LogWarning("[Singleton] Instance " + typeof(T) +
                //    //                 " already destroyed on application quit." +
                //    //                 "Won't create again - returning null.");
                //    return null;
                //}

                lock (_lock)
                {
                    if (_instance == null)
                    {
                        _instance = (T)FindObjectOfType(typeof(T));

                        //if (_instance == null)
                        //{
                        //    GameObject singleton = new GameObject();
                        //    _instance = singleton.AddComponent<T>();
                        //    singleton.name = "(singleton) " + typeof(T);

                        //    DontDestroyOnLoad(singleton);
                        //}
                    }

                    return _instance;
                }
            }
        }

        public static bool HasInstance => _instance != null;

        protected static bool applicationIsQuitting = false;
        public static bool AwakeDone { get; private set; }
        public static event Action OnAwakeDone;
        public event Action OnRelease;

        /// <summary>
        /// When unity quits, it destroys objects in a random order.
        /// In principle, a Singleton is only destroyed when application quits.
        /// If any script calls Instance after it have been destroyed, 
        ///   it will create a buggy ghost object that will stay on the Editor scene
        ///   even after stopping playing the Application. Really bad!
        /// So, this was made to be sure we're not creating that buggy ghost object.
        /// </summary>
        protected virtual void OnDestroy()
        {
            applicationIsQuitting = true;
            OnRelease?.Invoke();
        }

        protected virtual void OnEnable()
        {
            SceneManager.sceneLoaded += HandleLoadedLevel;
        }

        protected virtual void OnDisable()
        {
            SceneManager.sceneLoaded -= HandleLoadedLevel;
        }

        protected virtual void HandleLoadedLevel(Scene scene, LoadSceneMode mode)
        {
            FindInstance();
        }

        protected virtual void Awake()
        {
            Init();
            _instance = this as T;
            AwakeDone = true;
            OnAwakeDone?.Invoke();
            OnAwakeDone = null;
        }

        public static void FindInstance()
        {
            _instance = FindObjectOfType<T>();
        }

        protected virtual void Init() { }

        public static void CallbackWhenAwaken(Action callback)
        {
            if (HasInstance)
                callback?.Invoke();
            else
                OnAwakeDone += callback;
        }
    }
}