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

using UnityEngine.SceneManagement;

[RequireComponent(typeof(PlayerManager))]
[RequireComponent(typeof(InventoryManager))]
[RequireComponent(typeof(AudioManager))]

public class Managers : MonoBehaviour {

    // The list of game managers to loop through during startup sequence
    private List<IGameManager> gameManagers;

    // Static game manager properties that other parts of the game code 
    // use to access the state managers available    
    public static PlayerManager Player { get; private set; }

    public static InventoryManager Inventory { get; private set; }

    public static AudioManager Audio { get; private set; }

    public static bool IsGameOver { get; set; }

    [SerializeField]
    private GameObject EnemyPrefab;
    
	void Awake()
    {
        // Initialise the game state manager variables
        Player = GetComponent<PlayerManager>();
        Inventory = GetComponent<InventoryManager>();
        Audio = GetComponent<AudioManager>();

        // Initialise the game managers list
        // and add the managers to it
        gameManagers = new List<IGameManager>();
        gameManagers.Add(Player);
        gameManagers.Add(Inventory);
        gameManagers.Add(Audio);

        // Start the managers using the coroutine.
        StartCoroutine(StartupManagers());

        Messenger.AddListener(GameEvent.GAME_RESTART, OnGameRestart);
        Messenger.AddListener(GameEvent.ENEMY_KILLED, OnEnemyKilled);
    }

    private void OnDestroy()
    {
        Messenger.RemoveListener(GameEvent.GAME_RESTART, OnGameRestart);
        Messenger.RemoveListener(GameEvent.ENEMY_KILLED, OnEnemyKilled);
    }

    private void InitialiseManagers()
    {

    }

    private IEnumerator StartupManagers()
    {
        // Loop through the managers in the start sequence list
        // and call their start up method
        foreach (IGameManager manager in gameManagers)
        {
            manager.StartUp();
        }

        // temporarily yield control over to the rest of the running game
        // return next frame and code will continue executing from next point on return
        yield return null;


        int numModules = gameManagers.Count;
        int numReady = 0;

        // check to see if all managers have started
        while (numReady < numModules)
        {
            int lastReady = numReady;
            numReady = 0;

            foreach (IGameManager manager in gameManagers)
            {
                if (manager.status == ManagerStatus.Started)
                {
                    numReady++;
                }
            }

            if (numReady > lastReady)
                Debug.Log("Progress: " + numReady + "/" + numModules);

            // temporarily yield control over to the rest of the running game 
            // and return next frame to start of while loop
            yield return null;
        }

        // all managers are now ready
        Debug.Log("All managers started up");
    }

    private void Update()
    {
        if (Player.Health <= 0)
        {
            if (!IsGameOver)
            {
                Messenger.Broadcast(GameEvent.GAME_OVER);
                Managers.Audio.PlayBackgroundMusic("intro-synth");
            }
            IsGameOver = true;
        }
    }

    private void OnGameRestart()
    {
        StartCoroutine(Restart());
    }

    private IEnumerator Restart()
    {
        yield return new WaitForSeconds(1f);
        IsGameOver = false;
        StopAllCoroutines();
        // Get the current scene and reload it using its index
        Scene scene = SceneManager.GetActiveScene();
        // Reload the current scene using its build index
        SceneManager.LoadScene(scene.buildIndex);
    }

    private void OnEnemyKilled()
    {
        Player.Score += 1;
        Messenger.Broadcast(GameEvent.ENEMY_COUNT_CHANGED);
        StartCoroutine(RespawnEnemy());
    }

    private IEnumerator RespawnEnemy()
    {
        yield return new WaitForSeconds(5);
        // TO DO: Respawn Enemy at specific position
        GameObject newEnemy = GameObject.Instantiate(EnemyPrefab);
        newEnemy.name = newEnemy.name + "_" + Time.time.ToString();
        Messenger.Broadcast(GameEvent.ENEMY_COUNT_CHANGED);
    }
}
