using UnityEngine;

public class TestBehaviour : MonoBehaviour
{
    private void OnDestroy()
    {
        Debug.Log("OnDestroy() called in other");
    }
}


//////////////////////


using UnityEngine;

// TODO: Refactor
[DisallowMultipleComponent]
public abstract class MonoBehaviourSingletonBase<SingletonType> : MonoBehaviour
    where SingletonType : MonoBehaviourSingletonBase<SingletonType>
{
    private static SingletonType _instance;

    public static SingletonType Instance
    {
        get
        {
            if (_instance == null)
                FindOrTryCreateSingleton();

            return _instance;
        }
    }

    public static bool IsAnyInstanceLiving => (_instance != null) || FindFirstObjectByType<SingletonType>(findObjectsInactive: FindObjectsInactive.Include);

	public virtual string GameObjectName => typeof(SingletonType).Name;


	// Initialize
	protected virtual void Awake()
    {
        if ((_instance != null) && (_instance != this))
        {
            Debug.LogFormat("Scene.name: {0}, Scene.path: {1}, Scene.handle: {2}, Scene.IsValid(): {3}, Scene.isLoaded: {4}, _instance != null: {5}",
                _instance.gameObject.scene.name,
                _instance.gameObject.scene.path,
                _instance.gameObject.scene.handle,
                _instance.gameObject.scene.IsValid(),
                _instance.gameObject.scene.isLoaded,
                _instance != null);

            DestroyImmediate(this.gameObject);
            return;
        }

        _instance = (this as SingletonType);
    }

    protected static void TryCreateSingleton()
    {
        if (GameControllerPersistentSingleton.IsQuitting || SceneControllerPersistentSingleton.IsActiveSceneChanging)
            throw new System.Exception("Cant create Singleton. You are probably trying to instantiate in OnDestroy() or OnDisable()");

		_instance = new GameObject(typeof(SingletonType).Name, typeof(SingletonType)).GetComponent<SingletonType>();
		_instance.name = _instance.GameObjectName;

#if UNITY_EDITOR
		if (!Application.isPlaying)
			_instance.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
#endif
	}

    protected static void FindOrTryCreateSingleton()
    {
        // Try to find
        if (_instance == null)
            _instance = FindFirstObjectByType<SingletonType>(findObjectsInactive: FindObjectsInactive.Include);

        // If still cant find, try to create
        if (_instance == null)
            TryCreateSingleton();
    }

	protected static void DestroyAllInstances()
    {
        foreach (var iteratedInstance in FindObjectsByType<SingletonType>(FindObjectsInactive.Include, sortMode: FindObjectsSortMode.None))
            DestroyImmediate(iteratedInstance.gameObject);
    }
}


//////////////////////

using System;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

public sealed class GameControllerPersistentSingleton : MonoBehaviourSingletonBase<GameControllerPersistentSingleton>
{
	[field: NonSerialized]
	public static bool IsQuitting { get; private set; }

	public static bool IsPaused => (Time.timeScale == 0);


	// Initialize
	[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
	private static void OnBeforeSplashScreen()
	{
		SceneManager.activeSceneChanged += OnActiveSceneChanged;
		SceneManager.sceneLoaded += OnSceneLoaded;
		SceneManager.sceneUnloaded += OnSceneUnloaded;
	}


	// Update
	private static void OnActiveSceneChanged(Scene lastScene, Scene loadedScene)
	{
		Debug.Log("OnActiveSceneChanged called in GameControllerPersistentSingleton");
        
        // RuntimeInitializeOnLoadMethod wont respect execution order and TryCreateSingleton accesses the SceneControllerPersistentSingleton
		SceneControllerPersistentSingleton.OnGameControllerActiveSceneChanged(lastScene, loadedScene);

		if (!IsAnyInstanceLiving)
			TryCreateSingleton();
	}
    

    // Dispose
    private void OnApplicationQuit()
	{
		IsQuitting = true;
	}
}


//////////////////////


using UnityEngine;
using UnityEngine.SceneManagement;

// TODO: Refactor
[DefaultExecutionOrder(-1)]
public sealed class SceneControllerPersistentSingleton : MonoBehaviourSingletonBase<SceneControllerPersistentSingleton>
{
	public static bool IsActiveSceneChanging { get; private set; }


	// Update
	public void RestartScene()
	{
		SceneManager.LoadScene(SceneManager.GetActiveScene().name);
	}

	public static void OnGameControllerActiveSceneChanged(Scene lastScene, Scene loadedScene)
	{
		Debug.Log("OnActiveSceneChanged() called in SceneControllerPersistentSingleton");
		IsActiveSceneChanging = false;

		if (!IsAnyInstanceLiving)
			TryCreateSingleton();
	}


	// Dispose
    /*private void OnDisable()
	{
		Debug.Log("OnDestroy() called in SceneControllerPersistentSingleton");
		Debug.Log("IsActiveSceneChanging is true");
		IsActiveSceneChanging = true;
	}*/

	private void OnDestroy()
	{
		Debug.Log("OnDestroy() called in SceneControllerPersistentSingleton");
		Debug.Log("IsActiveSceneChanging is true");
		IsActiveSceneChanging = true;
	}
}


//////////////////////