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

public static class MenuHelper
{
	private static Canvas canvas;
	private static Stack<MenuSection> sections;

	private static List<Func<bool>> backButtonHandlers = new List<Func<bool>> ();
	
	private static GameObject dialogPrefab;

	public static bool IsMenuDisplayed {
		get {
			return MenuHelper.sections.Count != 0;
		}
	}

	public static bool CanGoBack {
		get {
			return MenuHelper.sections.Count > 1;
		}
	}

	public static MenuSection CurrentSection {
		get {
			if (MenuHelper.IsMenuDisplayed == true) {
				return MenuHelper.sections.Peek ();
			} else {
				return null;
			}
		}
	}

	public static event EventHandler<SectionChangedEventArgs> SectionChanged;

	private static void OnSectionChanged (MenuSection from, MenuSection to, bool isForward)
	{
		var handler = SectionChanged;
		if (handler != null) {
			handler (null, new SectionChangedEventArgs () {
				from = from,
				to = to,
				isForward = isForward
			});
		}
	}

	public static event EventHandler MenuClosed;

	private static void OnMenuClosed ()
	{
		
		var handler = MenuClosed;
		if (handler != null) {
			handler (null, EventArgs.Empty);
		}
	}


	public static void Initialize (Canvas canvas, MenuSection initialSection, GameObject dialogprefab)
	{
		MenuHelper.sections = new Stack<MenuSection> ();
		MenuHelper.canvas = canvas;
		MenuHelper.dialogPrefab = dialogprefab;

		foreach (var go in GameObject.FindGameObjectsWithTag("MenuScreen")) {
			go.GetComponent<MenuSection> ().Initialize ();
			var tr = (RectTransform)go.transform;
			tr.anchoredPosition = new Vector2 (0f, 0f);
			tr.gameObject.SetActive (false);
		}

		if (initialSection != null) {
			MenuHelper.NavigateTo (initialSection);
		}
	}

	/// <summary>
	/// Shows a dialog box with a single option.
	/// </summary>
	/// <returns>The DialogController instance for the newly created dialog</returns>
	/// <param name="title">Title.</param>
	/// <param name="message">Message.</param>
	/// <param name="buttonText">Button text.</param>
	/// <param name="action">Action.</param>
	/// <param name="buttonSound">Button click sound.</param>
	public static DialogController ShowDialog (string title, string message, string buttonText, Action action = null, AudioClip buttonSound = null)
	{
		return MenuHelper.ShowDialog (title, message, null, buttonText, (i) => {
			if (action != null) {
				action ();
			}
		});
	}

	/// <summary>
	/// Shows a dialog box with two options.
	/// </summary>
	/// <returns>The DialogController instance for the newly created dialog</returns>
	/// <param name="title">Title.</param>
	/// <param name="message">Message.</param>
	/// <param name="button1Text">Button 1 text.</param>
	/// <param name="button2Text">Button 2 text.</param>
	/// <param name="action">Action.</param>
	/// <param name="button1Sound">Button 1 sound.</param>
	/// <param name="button2Sound">Button 2 sound.</param>
	public static DialogController ShowDialog (string title, string message, string button1Text, string button2Text, Action<int> action, AudioClip button1Sound = null, AudioClip button2Sound = null)
	{
		Action<int> wrappedAction = delegate(int value) {
			MenuHelper.ReleaseBackButton ();
			action (value);
		};

		var dlg = GameObject.Instantiate (MenuHelper.dialogPrefab);
		dlg.transform.SetParent (MenuHelper.canvas.transform, false);
		
		var dlgController = dlg.GetComponent<DialogController> ();
		dlgController.Initialize (title, message, button1Text, button2Text, wrappedAction, button1Sound, button2Sound);

		MenuHelper.HandleBackButton (delegate() {
			wrappedAction (0);
			GameObject.Destroy (dlg);
			return true;
		});

		return dlgController;
	}

	/// <summary>
	/// Navigates to a menu section instance, which must already be in the menu container.
	/// </summary>
	/// <param name="section">The section.</param>
	public static void NavigateTo (MenuSection section, bool replacePrevious = false)
	{
		MenuSection lastSection = null;
		if (MenuHelper.sections.Count > 0) {
			lastSection = MenuHelper.sections.Peek ();
			MenuHelper.HideSection (lastSection, true);

			if (replacePrevious == true) {
				MenuHelper.sections.Pop ();
			}
		}
		
		MenuHelper.sections.Push (section);
		
		MenuHelper.ShowSection (section, true);

		MenuHelper.OnSectionChanged (lastSection, section, true);
	}

	/// <summary>
	/// Navigates to an existing section determined by its type (subtype of MenuSection).
	/// </summary>
	/// <param name="sectionType">The Type of the section - subtype of MenuSection.</param>
	public static void NavigateTo (Type sectionType, bool replacePrevious = false)
	{
		var section = MenuHelper.canvas.GetComponentInChildren (sectionType, true) as MenuSection;
		if (section == null) {
			throw new ArgumentException ("Could not find a section of type " + sectionType.Name + " in the canvas");
		}

		MenuHelper.NavigateTo (section);
	}

	/// <summary>
	/// Goes back to the previous section, or closes (hides) the menu if such does not exist.
	/// </summary>
	public static void GoBack ()
	{
		if (MenuHelper.sections.Count == 0) {
			return;
		}

		var currentSection = sections.Pop ();
		MenuHelper.HideSection (currentSection, false);

		if (MenuHelper.sections.Count > 0) {
			var lastSection = sections.Peek ();
			MenuHelper.ShowSection (lastSection, false);

			MenuHelper.OnSectionChanged (currentSection, lastSection, false);
		} else if (MenuHelper.sections.Count == 0) {
			MenuHelper.OnSectionChanged (currentSection, null, false);

			MenuHelper.OnMenuClosed ();
		}
	}

	/// <summary>
	/// Method for internal usage. Used to send signal from the MenuInitializer to the MenuHelper about back press.
	/// </summary>
	public static void OnBackPressed ()
	{
		bool isHandled = false;
		for (int i = backButtonHandlers.Count - 1; i >= 0; i--) {
			isHandled = backButtonHandlers [i] ();
			if (isHandled == true) {
				break;
			}
		}
	
		if (isHandled == false && sections.Count > 0) {
			var currentSection = sections.Peek ();
			currentSection.OnBackPressed ();
		}
	}

	public static void HandleBackButton (Func<bool> action)
	{
		backButtonHandlers.Add (action);
	}

	public static void ReleaseBackButton ()
	{
		backButtonHandlers.RemoveAt (backButtonHandlers.Count - 1);
	}

	private static void ShowSection (MenuSection section, bool isForward)
	{
		section.gameObject.SetActive (true);
		
		section.isActive = true;
		
		section.OnNavigatedTo (isForward);
	}

	private static void HideSection (MenuSection section, bool isForward)
	{
		section.gameObject.SetActive (false);
		
		section.isActive = false;
		
		section.OnNavigatedFrom (isForward);
	}
}

public class SectionChangedEventArgs : EventArgs
{
	public MenuSection from;
	public MenuSection to;
	public bool isForward;
}