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

public class GameManager : MonoBehaviour {

    public static GameManager instance = null;

	[SerializeField] AudioClip songMain;
	[SerializeField] AudioClip sndMenuSelect;
	[SerializeField] AudioClip sndDeath;
	[SerializeField] AudioClip sndVictory;
	[SerializeField] AudioClip[] sndPunches;
	[SerializeField] AudioClip[] sndCoinCollect;
	[SerializeField] AudioClip[] sndEnemyCoinCollect;
	[SerializeField] AudioClip[] sndEnemyDeath;
	[SerializeField] Transform coin;
	[SerializeField] List<GameObject> enemyTypes = new List<GameObject>();
	[SerializeField] AnimationCurve fadeCurve;

    private AudioSource audioSource;
	private List<GameObject> enemies = new List<GameObject>();
	private List<GameObject> characters = new List<GameObject>();
	private List<GameObject> coins = new List<GameObject>();
    private GameObject player;
	private int killCount = 0;
	private int spawnCount = 1;
	private int wave = 0;
	private float spawnTimer = 12.5f;
	private float minTimer = 7.5f;
	private bool won = false;

	private GameObject winScreen;
	private GameObject loseScreen;

	public Vector2 screenMin;
	public Vector2 screenMax;

    void Awake() {
        if (instance == null) {
            instance = this;
        } else if (instance != this) {
            Destroy(gameObject);
        }

        audioSource = GetComponent<AudioSource>();
		winScreen = GameObject.FindGameObjectWithTag("Win");
		loseScreen = GameObject.FindGameObjectWithTag("Lose");

		Color wintmp = winScreen.GetComponent<SpriteRenderer>().color;
		Color losetmp = loseScreen.GetComponent<SpriteRenderer>().color;
		wintmp.a = 0;
		losetmp.a = 0;
		winScreen.GetComponent<SpriteRenderer>().color = wintmp;
		loseScreen.GetComponent<SpriteRenderer>().color = losetmp;

		SetEnemies();
		player = GameObject.FindGameObjectWithTag("Player");
		SetCharacters();
		SetCoins();

		screenMin = Camera.main.ScreenToWorldPoint(new Vector2(0, 0));
		screenMax = Camera.main.ScreenToWorldPoint(new Vector2(Screen.width, Screen.height));
    }

    void Start() {
        audioSource.Stop();
        audioSource.loop = true;
        audioSource.clip = songMain;
        audioSource.volume = 0.5f;
        audioSource.Play();

		SpawnEnemy();
		StartCoroutine(Wave());
    }

    void Update() {
        bool exit = Input.GetButtonDown("Cancel");
        if (exit) {
			SceneManager.LoadScene("Menu");
		}

		bool restart = Input.GetKeyDown(KeyCode.R);
		if (restart) {
			SceneManager.LoadScene("Game");
		}
    }

	IEnumerator Wave() {
		yield return new WaitForSeconds(spawnTimer);
		if (!player.GetComponent<Character>().isDead) {
			wave += 1;
			spawnTimer -= .5f;
			spawnTimer = Mathf.Max(spawnTimer, minTimer);
			spawnCount = Mathf.Min(Mathf.FloorToInt(wave / 3) + 1, 3);
			for (int x = 0; x < spawnCount; x += 1) {
				SpawnEnemy();
			}
			StartCoroutine(Wave());
		}
	}

	public void PlayMenuSelect() {
        audioSource.PlayOneShot(sndMenuSelect, .5f);
    }

	public void PlayDeath() {
        audioSource.PlayOneShot(sndDeath, Random.Range(0.25f, 0.3f));
    }

	public void PlayPunch() {
        audioSource.PlayOneShot(sndPunches[Random.Range(0, sndPunches.Length)], Random.Range(0.25f, 0.3f));
    }

	public void PlayCoinCollect() {
        audioSource.PlayOneShot(sndCoinCollect[Random.Range(0, sndCoinCollect.Length)], Random.Range(0.25f, 0.3f));
    }

	public void PlayEnemyCoinCollect() {
        audioSource.PlayOneShot(sndEnemyCoinCollect[Random.Range(0, sndEnemyCoinCollect.Length)], Random.Range(0.25f, 0.3f));
    }

	public void PlayEnemyDeath() {
        audioSource.PlayOneShot(sndEnemyDeath[Random.Range(0, sndEnemyDeath.Length)], Random.Range(0.25f, 0.3f));
    }

	public List<GameObject> GetCharacters() {
		return characters;
	}

    public List<GameObject> GetEnemies() {
        return enemies;
    }

    public List<GameObject> GetCoins() {
        return coins;
    }

	public void SetCharacters() {
		List<GameObject> temp = new List<GameObject>();
		temp.AddRange(enemies);
		temp.Add(player);
		characters = temp;
	}

    public void SetEnemies() {
		enemies.Clear();
        GameObject[] _enemies = GameObject.FindGameObjectsWithTag("Enemy");
        foreach (GameObject enemy in _enemies) {
            if (enemy.activeSelf && !enemy.GetComponent<Character>().isDead) {
                enemies.Add(enemy);
            }
        }
    }

    public void SetCoins() {
		coins.Clear();
        GameObject[] _coins = GameObject.FindGameObjectsWithTag("Coin");
        foreach (GameObject _coin in _coins) {
            if (_coin.activeSelf && _coin.GetComponent<Coin>().isActive && !_coin.GetComponent<Coin>().isFlying) {
                coins.Add(_coin);
            }
        }
    }

	public void SpawnEnemy() {
		if (enemies.Count < 8 && !won) {
			GameObject enemyType = enemyTypes[Random.Range(0, enemyTypes.Count)];
			int spawn = Random.Range(0, 4);
			Vector3 screenPosition;
			Vector3 spawnPoint;

			switch (spawn) {
				case 0: {
					screenPosition = Camera.main.ScreenToWorldPoint(new Vector3(Random.Range(0,Screen.width), 0, 0));
					spawnPoint = new Vector3(screenPosition.x, screenPosition.y - (enemyType.GetComponent<SphereCollider>().bounds.size.y / 2) - 1, transform.position.z);
					break;
				}
				case 1: {
					screenPosition = Camera.main.ScreenToWorldPoint(new Vector3(Random.Range(0,Screen.width), Screen.height, 0));
					spawnPoint = new Vector3(screenPosition.x, screenPosition.y + (enemyType.GetComponent<SphereCollider>().bounds.size.y / 2) + 1, transform.position.z);
					break;
				}
				case 2: {
					screenPosition = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Random.Range(0,Screen.height), 0));
					spawnPoint = new Vector3(screenPosition.x + (enemyType.GetComponent<SphereCollider>().bounds.size.x / 2) + 1, screenPosition.y, transform.position.z);
					break;
				}
				case 3:
				default: {
					screenPosition = Camera.main.ScreenToWorldPoint(new Vector3(0, Random.Range(0,Screen.height), 0));
					spawnPoint = new Vector3(screenPosition.x - (enemyType.GetComponent<SphereCollider>().bounds.size.x / 2) - 1, screenPosition.y, transform.position.z);
					break;
				}
			}

			Instantiate(enemyType.transform, spawnPoint, Quaternion.identity);
			SetEnemies();
			SetCharacters();
		}
	}

	public GameObject AddCoin(Vector3 pos) {
		Transform _coin = Instantiate(coin, pos, Quaternion.identity);
		return _coin.gameObject;
	}

	public void RemoveEnemy(GameObject enemy) {
		enemies.Remove(enemy);
	}

    public GameObject GetPlayer() {
        return player;
    }

	public void AddKill(int num) {
		killCount += num;
	}

    public void Lose() {
		StartCoroutine(LoseScreen());
    }

    public void Win() {
		won = true;
		audioSource.PlayOneShot(sndVictory, .5f);
		// kill enemies
		for (int i = 0; i < enemies.Count; i += 1) {
			enemies[i].GetComponent<Enemy>().KillMe();
		}
		StartCoroutine(WinScreen());
    }

	IEnumerator LoseScreen() {
		float startTime = Time.time;
		float timeDelta = 0;
		float fadeTimer = 2f;

		while (timeDelta != 1) {
			timeDelta = Time.time - startTime;
			if (timeDelta < fadeTimer) {
				timeDelta = timeDelta / fadeTimer;
			} else {
				timeDelta = 1;
			}

			Color color = loseScreen.GetComponent<SpriteRenderer>().color;
			color.a = (fadeCurve.Evaluate(timeDelta) * 10);
			loseScreen.GetComponent<SpriteRenderer>().color = color;

			yield return new WaitForEndOfFrame();
		}
	}

	IEnumerator WinScreen() {
		float startTime = Time.time;
		float timeDelta = 0;
		float fadeTimer = 2f;

		while (timeDelta != 1) {
			timeDelta = Time.time - startTime;
			if (timeDelta < fadeTimer) {
				timeDelta = timeDelta / fadeTimer;
			} else {
				timeDelta = 1;
			}

			Color color = winScreen.GetComponent<SpriteRenderer>().color;
			color.a = (fadeCurve.Evaluate(timeDelta) * 10);
			winScreen.GetComponent<SpriteRenderer>().color = color;

			yield return new WaitForEndOfFrame();
		}
	}
}
