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

public class GameManager : MonoBehaviour {
	float EXTRA_TIME = 0;

#region SINGLETON
	public static GameManager Instance;
#endregion

	public GameUI ui;

	public ScreenManager screenManager;

	public GameStage gameStage;

	int _leftStartingBlock = 16;
	int _rightStartingBlock = 23;

	[SerializeField]PlayerControls leftPlayerControls;
	[SerializeField]PlayerControls rightPlayerControls;

	[SerializeField]Player[] playerSpawns;

	[SerializeField]ChangeSpeed _removalSpeed;

	[SerializeField]GameObject blueExplosion;
	[SerializeField]GameObject redExplosion;

	public Material[] materialColors;

	Difficulty _difficulty;

	

#region REMOVE_BLOCK
	float SLOW_REMOVAL_TIME = 10;
	float MEDIUM_REMOVAL_TIME = 5;
	float FRANTIC_REMOVAL_TIME = 5;

	float _resetRemovalTime = 5;
	float _timeToRemoval;
#endregion

#region WAVES
	[SerializeField]EnemyWaves[] easyWaves;
	[SerializeField]EnemyWaves[] mediumWaves;
	[SerializeField]EnemyWaves[] hardWaves;

	[SerializeField]EnemyWaves[] playerWave;

	EnemyWaves[] _selectedWaveSet;

	int _waveIndex = 0;
#endregion

	bool _gameActive = false;
	bool _removeActive = false;

	int _removeIndex = 0;

	List<Player> _leftPlayers;
	List<Player> _rightPlayers;

	bool _vsHuman = false;

	public bool gameActive
	{
		get {return this._gameActive;}
	}

	// Use this for initialization
	void Awake () {
		if (GameManager.Instance != null)
		{
			Destroy(this.gameObject);
			return;
		}

		GameManager.Instance = this;
        Screen.SetResolution(950, 540, false);
	}
	
	void OnEnable()
	{
		GameManager.Instance = this;	
	}

	void Start()
	{
		this.ui.gameObject.SetActive(false);
	}
	
	// Update is called once per frame
	void Update () {
		if (this._gameActive)
		{

			if (this._removeActive)
			{
				this._timeToRemoval -= Time.deltaTime;
				if (this._timeToRemoval < 0)
				{
				//Gotta give warning firsts.
					this._timeToRemoval += this._resetRemovalTime;
					OnRemoveTileTime();
				}

				this.ui.UpdateTime(this._timeToRemoval);
			}
			
		}
	}

	#region CONTROLS
	public void SetPlayerKeyboard(PlayerControls control)
	{
		this.leftPlayerControls = control;
	}
	#endregion

	#region INIT
	public void SetDifficulty(Difficulty newDifficulty)
	{
		this._difficulty = newDifficulty;
		this._vsHuman = (newDifficulty == Difficulty.VsHuman);
	}
	#endregion

	public void SpawnStage()
	{
		this._waveIndex = 0;
		Shot[] shots = GetComponentsInChildren<Shot>();
		for (int i = shots.Length -1 ; i >=0; i--)
		{
			Destroy(shots[i].gameObject);
		}

		this._removeIndex = 0;

		this.gameStage.ResetTiles();
		this.ui.ResetProfiles();
		this._leftPlayers = new List<Player>();

		AddLeftPlayer();
		AddRightPlayer();


		switch (this._removalSpeed)
		{
			case ChangeSpeed.Slow:
				this._resetRemovalTime = SLOW_REMOVAL_TIME;
				break;
				
			case ChangeSpeed.Medium:
				this._resetRemovalTime = MEDIUM_REMOVAL_TIME;
				break;
				
			case ChangeSpeed.Frantic:
				this._resetRemovalTime = FRANTIC_REMOVAL_TIME;
				break;
				
		}

		this.ui.gameObject.SetActive(true);

		OnStartWave();
	}

	public void AddLeftPlayer()
	{
		GameObject leftPlayer =	SpawnPlayer(playerSpawns[0], this._leftStartingBlock, GameColor.Blue);
		leftPlayer.name = "Left";
		leftPlayer.transform.localEulerAngles = new Vector3(0, 90, 0);
		leftPlayer.AddComponent<PlayerHumanControls>();
		
		Player humanPlayer = leftPlayer.GetComponent<Player>();

		humanPlayer.SetControls(this.leftPlayerControls);	
		humanPlayer.SetProfile(this.ui.CreateProfile());

		this._leftPlayers.Add(humanPlayer);
	}

	public void AddRightPlayer()
	{		
		this._rightPlayers = new List<Player>();

		switch (this._difficulty)
		{
			case Difficulty.Easy:
				this._selectedWaveSet = this.easyWaves;
				this._removalSpeed = ChangeSpeed.Slow;
				SpawnNewWave();
				break;
			case Difficulty.Medium:
				this._selectedWaveSet = this.mediumWaves;
				this._removalSpeed = ChangeSpeed.Medium;
				SpawnNewWave();
				break;

			case Difficulty.Hard:
				this._selectedWaveSet = this.hardWaves;
				this._removalSpeed = ChangeSpeed.Frantic;
				SpawnNewWave();
			break;

			case Difficulty.VsHuman:
				GameObject rightPlayer = SpawnPlayer(playerSpawns[0], this._rightStartingBlock, GameColor.Red);
				rightPlayer.GetComponent<Player>().SetProfile(this.ui.CreateProfile());
				rightPlayer.transform.localEulerAngles = new Vector3(0, -90, 0);
				rightPlayer.AddComponent<PlayerHumanControls>();
				rightPlayer.GetComponent<Player>().SetControls(this.rightPlayerControls);


				this._rightPlayers.Add(rightPlayer.GetComponent<Player>());
				this._selectedWaveSet = this.playerWave;
				break;
		}	
	
		
		
	
	}

	public GameObject SpawnPlayer(Player playerType, int startingIndex, GameColor startUpColor)
	{
		Player newPlayer = Instantiate<Player>(playerType, this.gameStage.transform);
		newPlayer.SetLocation(startingIndex);

		newPlayer.transform.localPosition = this.gameStage.GetIndex(startingIndex).transform.localPosition + Vector3.up;

		newPlayer.GetComponent<Player>().SetColor(startUpColor);

		return newPlayer.gameObject;
	}

	public bool OnPlayerMove(Player player, Direction direction)
	{
		bool result = false;

		GameTile destinationTile = this.gameStage.GetIndex(player.tileIndex + (int)direction);
		GameTile sourceTile = this.gameStage.GetIndex(player.tileIndex);
		
		//Check destiationTile if occupied		
		Player[] players = gameStage.GetComponentsInChildren<Player>();
		foreach (Player checkPlayer in players)
		{
			if(checkPlayer.tileIndex == player.tileIndex + (int)direction)
			{
				return false;
			}
		}

		if (destinationTile != null && destinationTile.gameColor == player.color)
		{
			int distance = Mathf.CeilToInt(Vector2.Distance(new Vector2(destinationTile.row, destinationTile.col), new Vector2(sourceTile.row, sourceTile.col)));
			if (distance > 1)
			{
				result = false;
			}
			else
			{
				result = true;
			}
		}	


		if (result)
		{
			//Player movement
			player.transform.localPosition = destinationTile.transform.localPosition + Vector3.up;
			player.SetLocation(player.tileIndex + (int)direction);
		}


		return result;
	}

	public void OnRemoveTileTime()
	{
		int[] leftRemove = new int[]{0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 21, 22, 23, 24, 29,30, 31, 32};
		int[] rightRemove = new int[]{7, 6, 5, 4, 15, 14, 13, 12, 23, 22, 21, 20, 25, 26, 27, 28, 33, 34, 35, 36};

		gameStage.RemoveTile(leftRemove[this._removeIndex]);
		gameStage.RemoveTile(rightRemove[this._removeIndex]);

		this._removeIndex++;

		if (this._removeIndex >= leftRemove.Length || this._removeIndex >= rightRemove.Length) this._removeIndex--;


	}

	public void OnEnemyDeath(Player player)
	{
		GameObject explosion = Instantiate((player.color == GameColor.Blue) ? blueExplosion : redExplosion);
		explosion.transform.position = player.transform.position + (Vector3.up * 5);

		if (this._rightPlayers.IndexOf(player) != -1) this._rightPlayers.RemoveAt(this._rightPlayers.IndexOf(player));

		if (this._rightPlayers.Count < 1)
		{
			this._timeToRemoval += EXTRA_TIME;

			OnEndWave();
		}

		//Spawn check how many enemies are active and spawn next wave
	}

	void SpawnNewWave()
	{
		this._removeIndex--;

		if (this._removeIndex < 0) this._removeIndex = 0;

		this.ui.ClearRightProfiles();
		EnemyWaves currentWave = this._selectedWaveSet[this._waveIndex];
		for(int i = 0; i < currentWave.enemy.Count; i++)
		{			
			Enemy enemy = currentWave.enemy[i];

			GameTile startingTile = this.gameStage.GetIndex(enemy.startingCol, enemy.startingRow);

			//Check if tile is valid first
			if (startingTile == null || startingTile.gameColor != GameColor.Red)
			{
				continue;
			}


			Player player = Instantiate(enemy.enemy, this.gameStage.transform);
			player.transform.localEulerAngles = new Vector3(0, -90, 0);
			player.SetLocation(enemy.startingCol, enemy.startingRow);
			player.transform.localPosition = this.gameStage.GetIndex(player.tileIndex).transform.localPosition + Vector3.up;
			player.SetColor(GameColor.Red);
			player.GetComponent<Player>().SetProfile(this.ui.CreateProfile());
				
			switch (enemy.ai)
			{
				case AiType.AiBasic:
					player.gameObject.AddComponent<PlayerComputerEasyControls>();					
					break;
				case AiType.Lizer:
					player.gameObject.AddComponent<PlayerComputerLizerControls>();
					break;

				case AiType.MrTroy:
					player.gameObject.AddComponent<PlayerComputerMrTroyControls>();
					break;
			}

			this._rightPlayers.Add(player);
		}
	}

	void OnStartWave()
	{
		this._gameActive = true;
		this._removeActive = true;
		this._timeToRemoval = 0;//this._resetRemovalTime;
		Player[] players = gameStage.GetComponentsInChildren<Player>();
		foreach (Player player in players)
		{
			player.SendMessage("StartControls");
		}

	}

	void OnEndWave()
	{
		gameStage.StopChangingTiles();
		//Check next wave
		this._waveIndex ++;

		if (!this._gameActive) return;
		if (this._waveIndex >= this._selectedWaveSet.Length)
		{
			OnWinComplete();
			//Nothing left player win animation
		}
		else
		{
			SpawnNewWave();
		}

	}

	void Cleanup()
	{
		Shot[] shots = GetComponentsInChildren<Shot>();
		foreach (Shot shot in shots)
		{
			shot.enabled = false;
		}

		gameStage.StopChangingTiles();

	}

	void OnWinComplete()
	{
		this.ui.ClearTime();
		this._removeActive = false;
		Cleanup();

		ScreenManager.Instance.Show("WinScreen");
		foreach(Player human in this._leftPlayers)
		{
			human.GetComponent<PlayerHumanControls>().enabled = false;
		}

		foreach(Player human in this._rightPlayers)
		{
			human.GetComponent<PlayerHumanControls>().enabled = false;			
		}

		Invoke("ReturnToTitle", 5f);
		
	}

	public void OnPlayerDeath(Player player)
	{
		GameObject explosion = Instantiate((player.color == GameColor.Blue) ? blueExplosion : redExplosion);
		explosion.transform.position = player.transform.position + (Vector3.up * 5);
			
		this._removeActive = false;
		//Get all enemies and tell them to knock it off!

		//Show Title screen

		this._gameActive = false;
		foreach(Player enemy in this._rightPlayers)
		{
			enemy.SendMessage("StopMoving");
		}

		Cleanup();
		OnLoseComplete();
	}

	void OnLoseComplete()
	{
		ScreenManager.Instance.Show("LoseScreen");
		Invoke("ReturnToTitle", 5f);
	}

	void ReturnToTitle()
	{
		for(int i = this._leftPlayers.Count - 1; i >= 0; i--)
		{
			Destroy(this._leftPlayers[i].gameObject);
		}

		for (int i = this._rightPlayers.Count -1; i >= 0; i--)
		{
			Destroy(this._rightPlayers[i].gameObject);
		}
		this._leftPlayers = new List<Player>();
		this._rightPlayers = new List<Player>();

		this.ui.ResetProfiles();

		ScreenManager.Instance.Show("TitlePanel");
	}

	public void CheckTile(GameTile removedTile)
	{
		int removedIndex = this.gameStage.indexOf(removedTile);
		Player[] players = gameStage.GetComponentsInChildren<Player>();
		foreach (Player player in players)
		{
			if(player.tileIndex == removedIndex)
			{
				player.Damage(100);
			}
		}
	}

	void OnApplicationQuit () {
 	}

	void OnDestroy()
	{
		if (GameManager.Instance == this)
		{
			GameManager.Instance = null;
		}	
	}
}
