/****************************************************************************
* Copyright 2019 Nreal Techonology Limited. All rights reserved.
*                                                                                                                                                          
* This file is part of NRSDK.                                                                                                          
*                                                                                                                                                           
* https://www.nreal.ai/        
* 
*****************************************************************************/

using UnityEngine;

namespace NRKernal
{
    /// <summary> A single ton. </summary>
    /// <typeparam name="T"> Generic type parameter.</typeparam>
    public class SingleTon<T> where T : new()
    {
        private static T instane = default(T);
        public static void CreateInstance()
        {
            if(instane == null)
            {
                instane = new T();
            }
        }

        /// <summary> Gets the instance. </summary>
        /// <value> The instance. </value>
        public static T Instance
        {
            get
            {
                if(instane == null)
                {
                    CreateInstance();
                }
                return instane;
            }
        }
    }

    /// <summary> A singleton behaviour. </summary>
    /// <typeparam name="T"> Generic type parameter.</typeparam>
    public class SingletonBehaviour<T> : MonoBehaviour where T : SingletonBehaviour<T>
    {
        /// <summary> The instance. </summary>
        private static T instance;

        /// <summary>
        /// Returns the Singleton instance of the classes type. If no instance is found, then we search
        /// for an instance in the scene. If more than one instance is found, we throw an error and no
        /// instance is returned. </summary>
        /// <value> The instance. </value>
        protected static T Instance
        {
            get
            {
                if (instance == null && searchForInstance)
                {
                    searchForInstance = false;
                    T[] objects = FindObjectsOfType<T>();
                    if (objects.Length == 1)
                    {
                        instance = objects[0];
                    }
                    else if (objects.Length > 1)
                    {
                        Debug.LogErrorFormat("Expected exactly 1 {0} but found {1}.", typeof(T).ToString(), objects.Length);
                    }
                }
                return instance;
            }
        }

        /// <summary> True to search for instance. </summary>
        private static bool searchForInstance = true;

        /// <summary> Assert is initialized. </summary>
        public static void AssertIsInitialized()
        {
            Debug.Assert(IsInitialized, string.Format("The {0} singleton has not been initialized.", typeof(T).Name));
        }

        /// <summary> Returns whether the instance has been initialized or not. </summary>
        /// <value> True if this object is initialized, false if not. </value>
        public static bool IsInitialized
        {
            get
            {
                return instance != null;
            }
        }

        /// <summary> True if is dirty, false if not. </summary>
        protected bool isDirty = false;

        /// <summary>
        /// Base Awake method that sets the Singleton's unique instance. Called by Unity when
        /// initializing a MonoBehaviour. Scripts that extend Singleton should be sure to call
        /// base.Awake() to ensure the static Instance reference is properly created. </summary>
        protected virtual void Awake()
        {
            if (IsInitialized && instance != this)
            {
                isDirty = true;
                DestroyImmediate(gameObject);
                NRDebugger.Info("Trying to instantiate a second instance of singleton class {0}. Additional Instance was destroyed", GetType().Name);
            }
            else if (!IsInitialized)
            {
                instance = (T)this;
                DontDestroyOnLoad(gameObject);
            }
        }

        /// <summary>
        /// Base OnDestroy method that destroys the Singleton's unique instance. Called by Unity when
        /// destroying a MonoBehaviour. Scripts that extend Singleton should be sure to call
        /// base.OnDestroy() to ensure the underlying static Instance reference is properly cleaned up. </summary>
        protected virtual void OnDestroy()
        {
            if (instance == this)
            {
                instance = null;
                searchForInstance = true;
            }
        }
    }
}