﻿using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
using System.Collections.Generic;

public abstract class InputElement {
	public abstract string DisplayName();
	public abstract float Value();
	public abstract string ToSaveString();
	
	public static InputElement FromSaveString(string saveString) {
		if (saveString.StartsWith("Key:")) return InputElementKey.FromSaveString(saveString);
		if (saveString.StartsWith("Axis:")) return InputElementAxis.FromSaveString(saveString);
		return null;
	}
	
	public static InputElement ScanForAnyInput() {
		InputElement result = InputElementKey.ScanForAnyKey();
		if (result == null) result = InputElementAxis.ScanForAnyAxis();
		return result;
	}
}

public class InputElementKey : InputElement {
	public KeyCode keyCode;
	
	public InputElementKey(KeyCode code) {
		this.keyCode = code;
	}
	
	public override string DisplayName() {
		return keyCode.ToString();
	}
	
	public override float Value() {
		return Input.GetKey(keyCode) ? 1 : 0;
	}
	
	public override string ToSaveString() {
		return "Key:" + keyCode;
	}
	
	public static InputElementKey FromSaveString(string saveString) {
		string keyName = saveString.Substring(4);
		KeyCode code = (KeyCode)System.Enum.Parse(typeof(KeyCode), keyName);
		return new InputElementKey(code);
	}
	
	public static InputElementKey ScanForAnyKey() {
		foreach (KeyCode code in System.Enum.GetValues(typeof(KeyCode))) {
			if (Input.GetKey(code)) return new InputElementKey(code);
		}
		return null;
	}
}

public class InputElementAxis : InputElement {
	string axis;
	float sign;
	
	public InputElementAxis(string axis, float sign) {
		this.axis = axis;
		this.sign = sign;
	}
	
	public override string DisplayName() {
		return axis + (sign > 0 ? " > 0" : " < 0");
	}
	
	public override float Value() {
		float val = Input.GetAxisRaw(axis);
		return Mathf.Clamp01(val * sign);
	}
	
	public override string ToSaveString() {
		return "Axis:" + axis + ":" + sign;
	}	
	
	public static InputElementAxis FromSaveString(string saveString) {
		string[] parts = saveString.Split(new char[]{':'});
		float sign;
		float.TryParse(parts[2], out sign);
		return new InputElementAxis(parts[1], sign);
	}
	
	public static InputElementAxis ScanForAnyAxis() {
		for (int joynum=1; joynum<=2; joynum++) {
			for (int axisnum=1; axisnum<=4; axisnum++) {
				string axis = string.Format("Joy{0}Axis{1}", joynum, axisnum);
				float val = Input.GetAxisRaw(axis);
				if (Mathf.Abs(val) > 0.25f) {
					Debug.Log(axis + " value: " + val);
					return new InputElementAxis(axis, Mathf.Sign(val));
				}
			}
		}
		return null;
	}
}

public class InputConfigDemo : MonoBehaviour {
	#region Public Properties
	
	public Graphic promptBox;
	public Text promptText;
	public GameObject bulletPrototype;
	public float moveSpeed = 4;
	
	#endregion
	//--------------------------------------------------------------------------------
	#region Private Properties
	
	InputElement leftInput;
	InputElement rightInput;
	InputElement fireInput;
	
	bool waitingForNeutral;		// waiting for there to be NO inputs
	bool waitingForInput;		// waiting for an input to appear
	float nextFireTime;			// when we can fire again
	
	#endregion
	//--------------------------------------------------------------------------------
	#region MonoBehaviour Events
	void Start() {
		promptBox.gameObject.SetActive(false);
		LoadFromPrefs();
	}
	
	void Update() {
		if (waitingForNeutral) {
			// Just do nothing until we get NO inputs.
			InputElement elem = InputElement.ScanForAnyInput();
			if (elem != null) {
				Debug.Log("Saw: " + elem.ToSaveString());
				return;
			}
			
			// Then, wait for the user to give us a new input.
			waitingForNeutral = false;
			waitingForInput = true;
			Debug.Log("Waiting for input");
		}
		
		if (waitingForInput) {
			// We're prompting for an input... check to see if we got it.
			InputElement elem = InputElement.ScanForAnyInput();
			if (elem != null) {
				// We got one!
				Debug.Log("Saw input: " + elem.DisplayName());
				
				switch (promptText.text) {
				case "Left":	leftInput = elem; break;
				case "Right":	rightInput = elem; break;
				case "Fire":	fireInput = elem; break;
				}
				promptBox.gameObject.SetActive(false);
				waitingForInput = false;
				SaveToPrefs();
			}
			return;
		}
		
		// Play the game!
		if (leftInput != null) {
			transform.position += -transform.right * leftInput.Value() * moveSpeed * Time.deltaTime;
		}
		if (rightInput != null) {
			transform.position += transform.right * rightInput.Value() * moveSpeed * Time.deltaTime;
		}
		if (fireInput != null && fireInput.Value() > 0 && Time.time > nextFireTime	) {
			GameObject noob = Instantiate(bulletPrototype) as GameObject;
			noob.transform.position = transform.position;
			noob.SetActive(true);
			nextFireTime = Time.time + 0.2f;
		}
	}

	#endregion
	//--------------------------------------------------------------------------------
	#region Public Methods
	
	public void PromptForInput(string whichInput) {
		promptBox.gameObject.SetActive(true);
		promptText.text = whichInput;
		waitingForNeutral = true;
		Debug.Log("Waiting for neutral");
	}
	
	#endregion
	//--------------------------------------------------------------------------------
	#region Private Methods
	
	void SaveToPrefs() {
		if (leftInput != null) PlayerPrefs.SetString("leftInput", leftInput.ToSaveString());
		if (rightInput != null) PlayerPrefs.SetString("rightInput", rightInput.ToSaveString());
		if (fireInput != null) PlayerPrefs.SetString("fireInput", fireInput.ToSaveString());		
	}
	
	void LoadFromPrefs() {
		leftInput = InputElement.FromSaveString(PlayerPrefs.GetString("leftInput", "Key:LeftArrow"));
		rightInput = InputElement.FromSaveString(PlayerPrefs.GetString("rightInput", "Key:RightArrow"));
		fireInput = InputElement.FromSaveString(PlayerPrefs.GetString("fireInput", "Key:LeftShift"));		
	}
	
	#endregion
}
