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

// Transition manager

namespace com.deeperbeige.lib
{
	public class Transition : MonoBehaviour
	{
		// Events fired by this class
		public  const  string EVENT_START       = "transition_start";
		public  const  string EVENT_PRE_CHANGE  = "transition_pre_scene_change";
		public  const  string EVENT_POST_CHANGE = "transition_post_scene_change";
		public  const  string EVENT_COMPLETE    = "transition_complete";

	    // Singleton
	    public  static Transition inst { get; private set; }
	    
		// Behaviour modifiers
		public  static bool   autoForgetEvents = true;               // Should this class fire an Events.ForgetAll() before changing scenes
		public  static string animationHide    = "TransitionHide";   // The name of the hide animation
		public  static string animationReveal  = "TransitionReveal"; // The name of the reveal animation
		
		// Global state (across scenes)
		private static bool   transitioning    = false;        // Is the system currently running a transition
		private static bool   hiding           = false;        // Is the transition in the hide phase
		private static bool   revealing        = false;        // Is the transition in the reveal phase
		private static string headingTo        = string.Empty; // What scene the transition is heading to
		
		// History. This is maintained automatically while transitioning around the app.
		// This is public so that you can fiddle and adjust it in special cases. Take care!
		public static List<string> history     = new List<string>();
		
		// Transition to a new scene
		public static void To(string sceneID) { To(sceneID, true); }
		public static void To(string sceneID, bool saveToHistory) { To(sceneID, saveToHistory, false); }
		public static void To(string sceneID, bool saveToHistory, bool instant)
		{
			// Don't stop once transitioning (helps with repeated button presses)
			if (transitioning && ! instant) return;
			
			// Fallback in case the system breaks for some reason
			if (! inst || instant)
			{
				if (! inst) DevPanel.Log("Transition.To(" + sceneID + "): Misconfigured. Loading scene directly.");
				Events.Send(Transition.EVENT_PRE_CHANGE);
				if (autoForgetEvents) Events.ForgetAll();
				Application.LoadLevel(sceneID);
				return;
			}

			headingTo     = sceneID;
			transitioning = true;
			hiding        = true;
			revealing     = false;
			
			if (saveToHistory) history.Add(Application.loadedLevelName);

			inst.animation.Play(Transition.animationHide);
			Events.Send(Transition.EVENT_START);
		}
		
		// Transition back through history
		// If there's no more history to go back to, goes to defaultSceneID instead.
		// You should always supply a default, so that scenes still work when developing them in the editor.
		// Good for the Android hardware-back button
		public static void GoBack(string defaultSceneID) { GoBack(defaultSceneID, 1); }
		public static void GoBack(string defaultSceneID, int steps)
		{
			if (history == null) return;
			if (history.Count == 0)
			{
				To(defaultSceneID, false);
				return;
			}
			
			steps = Mathf.Clamp(steps, 1, history.Count);
			
			string prevSceneID = history[history.Count - steps];
			history.RemoveRange(history.Count - steps, steps);
			To(prevSceneID, false);
		}
		
		private void Awake()
		{
			Transition.inst = this;
			revealing       = true;
			transitioning   = true;
			animation.Play(Transition.animationReveal);
			Events.Send(Transition.EVENT_POST_CHANGE);
		}
		
		private void OnDestroy()
		{
			inst = null;
		}
		
		// Wait for something to happen
		private void Update()
		{
			if (! transitioning) return;
			
			if (revealing && ! animation.IsPlaying(Transition.animationReveal))
			{
				// Transition reveal complete
				headingTo     = "";
				transitioning = false;
				hiding        = false;
				revealing     = false;

				Events.Send(Transition.EVENT_COMPLETE);
			}
			
			if (hiding && ! animation.IsPlaying(Transition.animationHide))
			{
				// Transition hide complete
				hiding    = false;
				revealing = true;
				Events.Send(Transition.EVENT_PRE_CHANGE);
				if (autoForgetEvents) Events.ForgetAll();
				Application.LoadLevel(headingTo);
			}
		}
	}
}
