using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
using System;

/// <summary>
/// Base game state class - main scene control object.
/// There is only one state can be active in single time moment
/// States can be changed by call GameState.Show<CustomState>() or GameState.LoadScene<CustomState>(SceneName)
/// Each state have complex of lifecircle methods - you need override it to implement need behavior
/// </summary>
public class State : MonoBehaviour
{
	const string pathToUIPrefabs = "States/";
	#region Static part
	static Dictionary<System.Type, State> registeredStates = new Dictionary<System.Type, State>();
	static State currentState = null;

	public static State Current { get { return currentState;} }

	public static bool IsCurrentState<T>() where T : State
	{
		if (currentState != null && currentState is T)
		{
			return true;
		}
		return false;
	}
	// TODO Async load level with interface layer Loader
	//	public static void LoadScene<T, LoaderUI>(string sceneName = "")
	//		where T : UILayer
	//		where LoaderUI : UILayer
	//	{
	//		UILayer.ShowModal<LoaderUI> (() =>
	//		{
	//			registeredUI[typeof(LoaderUI)].isNeedDestroyUIBetweenScenes = false;
	//			AsyncLoad<T> (() =>
	//			{
	//				if (loadingDelegate != null)
	//				{
	//					loadingDelegate();
	//				}
	//				registeredUI[typeof(LoaderUI)].isNeedDestroyUIBetweenScenes = true;
	//				Hide<LoaderUI>();
	//			}, sceneName);
	//		});
	//	}

	/// <summary>
	/// Load state from prefab or create state if not exist.
	/// </summary>
	/// <typeparam name="T">The 1st type parameter.</typeparam>
	static T Create<T>() where T : State
	{
		T state = Resources.Load<T>(pathToUIPrefabs + typeof(T).Name);
		if (state != null)
		{
			state = Instantiate<T>(state);
		}
		else
		{
			state = Utils.Create<T>(typeof(T).Name);
		}
		if(!registeredStates.ContainsKey(typeof(T)))
		{
			registeredStates.Add(typeof(T), state);
			state.Init();
		}
		return state;
	}
	// TODO make Show<T> ( params = null) with optional implement params
	// params int[] list // https://msdn.microsoft.com/en-us/library/ms228391(v=vs.90).aspx
	public static T Show<T>() where T : State
	{
		HideCurrentScene();
		currentState = registeredStates.ContainsKey (typeof(T)) ? registeredStates [typeof(T)] : Create<T>();
		currentState.gameObject.SetActive(true);
		currentState.OnStateShow();
		return (T)currentState;
	}
	public static void Show<T, LoaderUI>(System.Action<T> endAction = null)
		where T : State
		where LoaderUI : UILayer
	{
		UILayer.Show<LoaderUI>( () => {
			T state = Show<T>();
			UILayer.Hide<LoaderUI>();
			if (endAction != null)
			{
				endAction(state);
			}
		});
	}

	public static void UpdateCurrent()
	{
		if (currentState != null)
		{
			currentState.UpdateState();
		}
	}

	static void HideCurrentScene()
	{
		if (currentState != null)  // hide old state
		{
			currentState.OnStateHide();
			currentState.gameObject.SetActive(false);
		}
	}

	protected static void Register(State state)
	{
		if (!registeredStates.ContainsKey(state.GetType()))
		{
			registeredStates.Add(state.GetType(), state);
			state.Init();
			if (state.stateByDefault)
			{
				currentState = state;
				currentState.gameObject.SetActive(true);
				currentState.OnStateShow();
			}
			else
			{
				state.gameObject.SetActive(false);
			}
        }
    }
    #endregion

    #region Instance part
    [SerializeField]bool stateByDefault = false; // show this state by default in this scene

	public bool IsShowed
	{
		get { return currentState == this; }
	}

	protected virtual void Awake()
	{
		Register (this);
	}

	void Start()
	{
		Register (this);
	}

	// Lifecircle
	// Init - once on create // can use Start
	// then call GameState.Show<>()
	// oldState StartHiding - use for state out animation, need call onFinishHiding then its done
	// oldState OnHide - use to correct destroying/disabling/stopping state objects
	// newState StartShowing - use for scene start animations

	protected virtual void Init()  // use for initialisation
	{
	}

//	protected virtual void OnShowWithParams(Object parameters)  // use for showing/create and activate  state objects with params
//	{
//	}

	protected virtual void OnStateShow()  // use for showing and activate state objects and interfaces
	{
	}

	protected virtual void OnStateHide()  // use to correct destroying/disabling/stopping state objects
	{
	}

	protected virtual void OnDestroy()
	{
		registeredStates.Remove (this.GetType());
	}

	public virtual void UpdateState()
	{	
	}
	#endregion
}
