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

[Serializable]
public struct WaveDepths
{
	public int FirstDepth;
	public int LastDepth;
	public Transform WavePrefab;
}

public class GameController : MonoBehaviour {
	public List<WaveDepths> Waves;
	public List<Transform> WallPrefabs;
	public List<Transform> BackgroundPrefabs;
	public List<Transform> DeepBackgroundPrefabs;
	public int DifficultyLevel;
	public Transform PlayerDamagePrefab;
	public AudioClip PlayerDamageClip;

	public float Volume=1.0f;
	public AudioSource DamageAudio { get; set; }
	public AudioSource RewardAudio { get; set; }
	public AudioSource ShootingAudio { get; set; }

	public Texture FullHeartTexture;
	public Texture EmptyHeartTexture;

	public Texture MenuTitleTexture;
	public Texture LevelTexture;
	public Texture SummaryTexture;
	public Texture TutorialTexture;

	public Font GUIFont;
	public int GUIFontSize;
	private bool guiInitialized=false;

	private float tenStartTime=0;
	private float prevTenTime=0;

	public LevelData CurrentLevel { get; set; }

	public Transform Player;
	private PlayerStatus pstat;

	private enum GameState
	{
		MenuTitleScreen,
		LevelScreen,
		Running,
		SummaryScreen,
		TutorialScreen
	}
	private GameState state;
	private float lastSwitchTime;

	public System.Random rand;
	private LevelGenerator levelGenerator;
	private LevelInstantiator levelInstantiator;

	private int killProgress=0;

	public void ReportKill()
	{
		killProgress++;

		if(killProgress>((float)DifficultyLevel/2.5f))  // higher levels take more kills to climb
		{
			int old=DifficultyLevel;
			DifficultyLevel = Mathf.Min (DifficultyLevel+1, 10);
			killProgress = 0;
			UpdatePlayerSpeedForLevel();
		
			if(DifficultyLevel == 10 && old < 10)
				EnterTen();
		}
	}

	public void ReportPlayerHit()
	{
		DifficultyLevel = Mathf.Max (DifficultyLevel-1, 0);
		killProgress = 0;
		UpdatePlayerSpeedForLevel();

		if(DifficultyLevel == 9)
			ExitTen ();

	}

	private void UpdatePlayerSpeedForLevel()
	{
		PlayerMovementController pmove = Player.GetComponent<PlayerMovementController>();

		float portion = (float)DifficultyLevel/(float)10f;
		float factor = Mathf.Pow(portion, 1.3f);

		pmove.BaseForwardSpeed = 2 + 7f*factor;
	}

	private void EnterTen()
	{
		tenStartTime = Time.fixedTime;
	}

	private void ExitTen()
	{
		prevTenTime = Time.fixedTime - tenStartTime;
	}

	void Start () 
	{
		rand = new System.Random();

		GameObject pgo = GameObject.Find ("Player");
		Player = pgo.transform;
		pstat = pgo.GetComponent<PlayerStatus>();


		DamageAudio   = gameObject.AddComponent<AudioSource>();
		RewardAudio   = gameObject.AddComponent<AudioSource>();
		ShootingAudio = gameObject.AddComponent<AudioSource>();

		DifficultyLevel=0;
		CurrentLevel = new LevelData(0);
		levelGenerator = new LevelGenerator(this);
		levelInstantiator = new LevelInstantiator(this);
		iTween.CameraFadeAdd();
		EnterState (GameState.MenuTitleScreen);
	}
	
	void Update () 
	{
		switch(state)
		{
		case GameState.MenuTitleScreen:
			if(Input.GetKeyDown(KeyCode.T))
			{
				EnterState(GameState.TutorialScreen);
			}
			else if(true)//Input.GetMouseButtonDown (0))
			{
				CurrentLevel = levelGenerator.GenerateLevel(1);
				EnterState(GameState.LevelScreen);
			}
			break;

		case GameState.LevelScreen:
			if(true)//Time.time - lastSwitchTime > 1 && (Input.anyKey || Input.GetMouseButton (0)))
			{
				levelInstantiator.InstantiateLevel(CurrentLevel);
				EnterState(GameState.Running);
			}
			break;

		case GameState.Running:
						
			if(CurrentLevel.Over)
			{
				foreach(Transform t in CurrentLevel.Transforms)
				{
						if(t != null)
					GameObject.Destroy (t.gameObject);
				}
				EnterState (GameState.SummaryScreen);
			}
			break;

		case GameState.SummaryScreen:
			if(Time.time - lastSwitchTime > 1 && (Input.anyKey || Input.GetMouseButton (0)))
			{
				if(CurrentLevel.Victory)
				{
					CurrentLevel = levelGenerator.GenerateLevel(CurrentLevel.Level + 1);
					EnterState(GameState.LevelScreen);
				}
				else
					EnterState(GameState.MenuTitleScreen);
			}
			break;

		case GameState.TutorialScreen:
			if(Input.GetMouseButtonDown(0))
			{
				EnterState(GameState.MenuTitleScreen);
			}
			break;

		}
	}

	void OnGUI()
	{
		if(!guiInitialized)
		{
			GUI.skin.label.font = GUIFont;
			GUI.skin.label.fontSize = GUIFontSize;
			GUI.skin.label.alignment = TextAnchor.UpperCenter;
			guiInitialized=true;
		}
				
		switch(state)
		{
		case GameState.MenuTitleScreen:
			//GUI.DrawTexture(new Rect(5,5, 600,600), MenuTitleTexture); //MenuTitleTexture.width, MenuTitleTexture.width),  MenuTitleTexture);
			break;
		case GameState.LevelScreen:
			//GUI.DrawTexture(new Rect(5,5, 600,600),  LevelTexture);
			//GUI.TextArea(new Rect(20,20,80,80), "" + CurrentLevel.Level);
			break;
		case GameState.Running:
			// ADD HUD CODE HERE
			//ShowHeartHUD();
			ShowTenTime();
			break;
		case GameState.SummaryScreen:
			GUI.DrawTexture(new Rect(5,5, 600,600),  SummaryTexture);
			break;
		case GameState.TutorialScreen:
			GUI.DrawTexture(new Rect(5,5, 600,600),  TutorialTexture);
			break;
			
		}
	}

	private void ShowTenTime()
	{
		if(DifficultyLevel != 10 && prevTenTime == 0)
			return;

		string msg = (DifficultyLevel==10) ? Mathf.FloorToInt(Time.fixedTime - tenStartTime)+"s" : Mathf.FloorToInt(prevTenTime)+"s";
		GUI.Label(new Rect(0.92f*Screen.width, 0.05f*Screen.height, 0.08f*Screen.width, 0.1f*Screen.height), msg); 
	}

	public void GUIShowScreenCentered(float y, string msg, GUIStyle style = null)
	{
		if(style==null)
			GUI.Label(new Rect(0, y*Screen.height, Screen.width, (1-y)*Screen.height), msg); 
		else
			GUI.Label(new Rect(0, y*Screen.height, Screen.width, (1-y)*Screen.height), msg, style); 
	}

	public void GUIShowOffset(float x, float y, string msg, GUIStyle style = null)
	{
		int chars = msg.IndexOf("\n");
		if(chars == -1)
			chars = msg.Length;

		float wid = (float)chars * 0.026f;
		float size = Mathf.Min (Screen.width, Screen.height);

		// screen is from mid - size/2 to mid + size/2
		float sx = x*size + (Screen.width/2f - (size/2f));
		float sy = y*size + (Screen.height/2f - (size/2f));

		//Debug.Log ("Offset label at " + sx + ", " + sy + " wid " + wid);
		if(style == null)
			GUI.Label(new Rect(sx, sy, wid*size, Screen.height-sy), msg); 
		else
			GUI.Label(new Rect(sx, sy, wid*size, Screen.height-sy), msg, style); 
	}

	private void ShowHeartHUD()
	{
		float heartSize = 32f;
		for(int i = 0; i<pstat.MaxHealth; i++)
		{
			Texture tex = (pstat.Health>i) ? FullHeartTexture : EmptyHeartTexture;
			Rect rect = new Rect(1 + i*heartSize*1.2f, 3, heartSize, heartSize);
			GUI.DrawTexture(rect, tex);
			
			//GUI.DrawTexture(new Rect(7*Screen.width/8 + i*heartSize, 7*Screen.width/8, heartSize, heartSize), tex);
			//GUI.DrawTextureWithTexCoords(new Rect(7*Screen.width/8+heartHeight/10 + iconInset, nextBoxYOff+nextBoxHgt/10 + iconInset, mag*nextIconSpace, mag*nextIconSpace ), ntex, new Rect(0,0, 12f/(float)ntex.width,1));
		}
	}
	
	private void EnterState(GameState newState)
	{
		lastSwitchTime = Time.time;
		GameState oldState = state;
		state = newState;

		Debug.Log ("GameController Entering State " + state + " from " + oldState);

		switch(state)
		{
		case GameState.MenuTitleScreen:
			Player.gameObject.SetActive(false);
			break;
		case GameState.LevelScreen:
			Player.gameObject.SetActive(false);
			break;
		case GameState.Running:
			Player.gameObject.SetActive(true);
			Player.position = CurrentLevel.StartPosition;
			break;
		case GameState.SummaryScreen:
			Player.gameObject.SetActive(false);
			break;
		case GameState.TutorialScreen:
			Player.gameObject.SetActive(false);
			break;
			
		}

	}

	public void Shuffle<T>(IList<T> list)
	{
		for(int i=0; i < list.Count; i++)
			Swap(list, i, rand.Next(i, list.Count));
	}
	
	public void Swap<T>(IList<T> list, int i, int j)
	{
		var temp = list[i];
		list[i] = list[j];
		list[j] = temp;
	}

	public T DrawFromAtRandom<T>(IList<T> list)
	{
		if(list == null || list.Count == 0)
			return default(T);

		int idx = rand.Next(0, list.Count);
		return list[idx];
	}

	public T RemoveFromAtRandom<T>(IList<T> list)
	{
		T item = DrawFromAtRandom(list);
		//if(item != default(T))
			list.Remove(item);
		return item;

	}
}
