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

// Klasa zarządzająca stanem gry
public class GameManager : Singleton<GameManager> {

	// Początkowe położenie krasnala
	public GameObject startingPoint;

	// Obiekt liny, na której krasnal jest opuszczany do kopalni
	public Rope rope;

	// Skrypt wygaszający (wywoływany podczas przywracania 
	// ustawień początkowych gry)
	//public Fade fade;

	// Skrypt obsługujący przesuwanie kamery w ślad za krasnalem
	public CameraFollow cameraFollow;

	// 'Bieżący' obiekt krasnala (żywy, w odróżnieniu do tych 
	// wszystkich, które już zginęły)
	Gnome currentGnome;

	// Prefabrykat, którego nowa instancja będzie tworzona 
	// w momencie, gdy gra będzie potrzebować nowego krasnala  
	public GameObject gnomePrefab;

	// Komponent interfejsu użytkownika zawierający przyciski 'Od nowa' i 'Wznów'
	public RectTransform mainMenu;

	// Komponent interfejsu użytkownika zawierający przyciski 
	// 'W górę', 'W dół' oraz 'Menu' 
	public RectTransform gameplayMenu;

	// Komponent interfejsu użytkownika zawierający ekran 'Wygrałeś!' 
	public RectTransform gameOverMenu;

	// Jeśli ta właściwość przyjmie wartość true, należy zignorować
	// wszystkie obrażenia (lecz wciąż pokazywać ich wizualne efekty);
	// Zastosowanie metod 'get' i 'set' oznacza, że właściwość będzie
	// wyświetlana w edytorze Unity na liście metod, na których mogą
	// być wykonywane zdarzenia Unity
	public bool gnomeInvincible { get; set; }

	// Czas jaki należy odczekać po śmierci krasnala, 
	// przed utworzeniem następnego. 
	public float delayAfterDeath = 1.0f;

	// Klip dźwiękowy, który należy odtworzyć po śmierci krasnala 
	public AudioClip gnomeDiedSound;

	// Klip dźwiękowy, który należy odtworzyć, gdy gracz wygrał grę
	public AudioClip gameOverSound;

	void Start() {
		// W momencie rozpoczynania gry, wywołujemy metodę Reset,
		// aby utworzyć nowego krasnala
		Reset ();
	}

	// Przywrócenie początkowego stanu całej gry
	public void Reset() {

		// Wyłączamy menu i włączamy interfejs użytkownika rozgrywki
		if (gameOverMenu)
			gameOverMenu.gameObject.SetActive(false);

		if (mainMenu)
			mainMenu.gameObject.SetActive(false);

		if (gameplayMenu)
			gameplayMenu.gameObject.SetActive(true);

		// Odnajdujemy wszystkie komponenty Resettable i nakazujemy im
		// przywrócić się do stanu początkowego
		var resetObjects = FindObjectsOfType<Resettable>();

		foreach (Resettable r in resetObjects) {
			r.Reset();
		}

		// Tworzymy nowego krasnala
		CreateNewGnome();

		// Uruchamiamy grę
		Time.timeScale = 1.0f;
	}


	void CreateNewGnome() {

		// Usuwamy bieżącego krasnala, jeśli taki jest
		RemoveGnome();

		// Tworzymy nowy obiekt krasnala i ustawiamy go jako 'bieżącego'
		// (zapisując we właściwości currentGnome)
		GameObject newGnome = (GameObject)Instantiate(gnomePrefab, 
			startingPoint.transform.position, 
			Quaternion.identity);                                                     
		currentGnome = newGnome.GetComponent<Gnome>();

		// Wyświetlamy linę
		rope.gameObject.SetActive(true);

		// Łączymy wolny koniec liny z dowolnym komponentem RigidBody
		// wskazanym przez obiekt krasnala (na przykład, z jego stopą)
		rope.connectedObject = currentGnome.ropeBody;

		// Przywracamy domyślną, początkową długość liny
		rope.ResetLength();

		// Nakazujemy komponentowi cameraFollow rozpoczęcie śledzenia
		// obiektu krasnala
		cameraFollow.target = currentGnome.cameraFollowTarget;

	}


	void RemoveGnome() {

		// Nie robimy niczego, jeśli krasnal nie jest widoczny
		if (gnomeInvincible)
			return;

		// Ukrywamy linę
		rope.gameObject.SetActive(false);

		// Przerywamy śledzenie krasnala
		cameraFollow.target = null;

		// Jeśli istnieje bieżący obiekt krasnala, sprawiamy, że 
		// nie będzie on już sterowany przez gracza
		if (currentGnome != null) {

			// Krasnal nie trzyma już skarbu
			currentGnome.holdingTreasure = false;

			// Oznaczamy, że krasnal nie jest już graczem 
			// (dzięki czemu gra nie będzie już zwracać uwagi na
			// mechanizmy wykrywania kolizji)
			currentGnome.gameObject.tag = "Untagged";

			// Znajdujemy wszystkie obiekty ze znacznikiem "Player"
			// i usuwamy z nich ten znacznik
			foreach (Transform child in currentGnome.transform) {
				child.gameObject.tag = "Untagged";
			}

			// Zmieniamy stan gry informując, że nie ma w niej już krasnala
			currentGnome = null;
		}
	}

	// Metoda obsługująca uśmiercenie krasnala
	void KillGnome(Gnome.DamageType damageType) {

		// Jeśli dysponujemy komponentem AudioSource, odtwarzamy 
		// klip oznaczający śmierć krasnala
		var audio = GetComponent<AudioSource>();
		if (audio) {
			audio.PlayOneShot(this.gnomeDiedSound);
		}

		// Wyświetlamy efekty obrażeń
		currentGnome.ShowDamageEffect(damageType);

		// Jeśli krasnal nie jest niezwyciężony, to przywracamy stan 
		// początkowy gry i oznaczamy, że krasnal nie jest już obiektem
		// gracza
		if (gnomeInvincible == false) {

			// Informujemy krasnala o jego śmierci
			currentGnome.DestroyGnome(damageType);

			// Usuwamy krasnala
			RemoveGnome();

			// Przywracamy stan początkowy gry
			StartCoroutine(ResetAfterDelay());

		}
	}

	// Metoda wywoływana w momencie śmierci krasnala
	IEnumerator ResetAfterDelay() {

		// Czekamy przez określony czas (liczbę sekund podaną jako
		// właściwość delayAfterDeath) i wywołujemy metodę Reset
		yield return new WaitForSeconds(delayAfterDeath);
		Reset();
	}


	// Metoda wywoływana kiedy gracz dotknie pułapki
	public void TrapTouched() {
		KillGnome(Gnome.DamageType.Slicing);
	}

	// Metoda wywoływana kiedy gracz dotknie pułapki z ogniem
	public void FireTrapTouched() {
		KillGnome(Gnome.DamageType.Burning);
	}

	// Wywoływana kiedy krasnal zdobędzie skarb.
	public void TreasureCollected() {
		// Informujemy bieżącego krasnala (currentGnome), że powinien
		// mieć skarb
		currentGnome.holdingTreasure = true;
	}


	// Metoda wywoływana kiedy gracz dotrze do wyjścia z poziomu
	public void ExitReached() {
		// Jeśli mamy gracze i jeśli gracz trzyma skarb, to gra jest zakończona!
		if (currentGnome != null && currentGnome.holdingTreasure == true) {

			// Jeśli dysponujemy komponentem AudioSource, odtwarzamy klip
			// oznaczający zakończenie gry
			var audio = GetComponent<AudioSource>();
			if (audio) {
				audio.PlayOneShot(this.gameOverSound);
			}

			// Wstrzymujemy grę
			Time.timeScale = 0.0f;

			// Wyłączamy menu końca gry i wyświetlamy ekran końca gry!
			if (gameOverMenu)
				gameOverMenu.gameObject.SetActive(true);

			if (gameplayMenu)
				gameplayMenu.gameObject.SetActive(false);
		}
	}


	// Metoda wywoływana po dotknięciu przycisku 'Menu' oraz po dotknięciu 
	// przycisku 'Wznów grę' 
	public void SetPaused(bool paused) {

		// Jeśli gra jest wstrzymana zatrzymujemy upływ czasu i wyświetlamy
		// menu (jednocześnie ukrywamy nakładkę gry) 
		if (paused) {
			Time.timeScale = 0.0f;
			mainMenu.gameObject.SetActive(true);
			gameplayMenu.gameObject.SetActive(false);
		} else {
			// Jeśli gra nie jest wstrzymana, wznawiamy upływ czasu i ukrywam
			// menu (jak również włączamy nakładkę gry)
			Time.timeScale = 1.0f;
			mainMenu.gameObject.SetActive(false);
			gameplayMenu.gameObject.SetActive(true);
		}
	}


	// Metoda wywoływana po dotknięciu przycisku 'Nowa gra'
	public void RestartGame() {

		// Natychmiast usuwamy obiekt krasnala (zamiast go uśmiercać)
		Destroy(currentGnome.gameObject);
		currentGnome = null;

		// Teraz przywracamy początkowy stan gry, aby utworzyła nowego krasnala
		Reset();
	}
}
