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

public enum GameState {
    Playing,
    AllApplesCollected,
    PlayerCrashed,
    TimedOut
}

public class GameStateManager : MonoBehaviour {

    #region Unity Singleton Pattern
    private static GameStateManager instance = null;
    public static GameStateManager Instance {
        get { return instance; }
    }

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

        // Der GameStateManager gilt nur pro Szene => löschen!
        //DontDestroyOnLoad(this.gameObject);
    }
    #endregion Unity Singleton Pattern

    public int levelTimeSeconds = 90;
    public float timeTickSeconds = 1;

    public UnityEvent onTimeTick = new UnityEvent();

    public UnityEvent onAppleCollected = new UnityEvent();
    public UnityEvent onGameStateChanged = new UnityEvent();

    private GameState currentGameState = GameState.Playing;
    public GameState CurrentGameState {
        get { return currentGameState; }
    }

    private float lastTickTime = 0;
    private float startTime = 0;

    public float PassedSeconds {
        get {
            return Time.time - startTime;
        }
    }

    public float RemainingSeconds {
        get {
            return levelTimeSeconds - PassedSeconds;
        }
    }

    public string RemainingTimeString {
        get {
            return RemainingSeconds.ToTimeStringMMSS();
        }
    }


    //private List<Apple> allApplesInScene = new List<Apple>();
    //private List<Apple> allApplesInScene = new List<Apple>();
    private Dictionary<AppleCategory, List<Apple>> applesByCategory
        = new Dictionary<AppleCategory, List<Apple>>();

    public int ApplesCount(AppleCategory category) {
        if (!applesByCategory.ContainsKey(category)) {
            return 0;
        }
        return applesByCategory[category].Count;
    }

    public int CollectedApplesCount(AppleCategory category) {
        int result = 0;
        if (applesByCategory.ContainsKey(category)) {
            foreach (Apple apple in applesByCategory[category]) {
                if (!apple.gameObject.activeSelf) {
                    result++;
                }
            }
        }
        return result;
    }

    public void Start() {
        FindApplesInScene();
        StartPlaying();
    }

    private void FindApplesInScene() {
        Apple[] apples = FindObjectsOfType<Apple>();
        foreach (Apple apple in apples) {
            if (!applesByCategory.ContainsKey(apple.category)) {
                applesByCategory[apple.category] = new List<Apple>();
            }
            applesByCategory[apple.category].Add(apple);
        }
    }

    public void Update() {
        if (currentGameState == GameState.Playing) {
            CheckTimeTick();
            CheckForTimeOut();
        }
    }

    private void CheckTimeTick() {
        if (Time.time - lastTickTime > timeTickSeconds) {
            onTimeTick.Invoke();
            lastTickTime = Time.time;
        }
    }

    private void CheckForTimeOut() {
        if (RemainingSeconds < 0) {
            TimedOut();
        }
    }

    public void StartPlaying() {
        startTime = Time.time;
        lastTickTime = 0; // results in immediate tick

        ChangeGameState(GameState.Playing);
    }

    private void TimedOut() {
        ChangeGameState(GameState.TimedOut);
    }

    public void PlayerCrashed() {
        ChangeGameState(GameState.PlayerCrashed);
    }

    public void AppleCollected() {
        onAppleCollected.Invoke();
        CheckApplesCollected();
    }

    private void CheckApplesCollected() {
        int greenApplesCount = ApplesCount(AppleCategory.Green);

        int greenApplesCollectedCount
             = CollectedApplesCount(AppleCategory.Green);

        if (greenApplesCount == greenApplesCollectedCount) {
            ChangeGameState(GameState.AllApplesCollected);
        }
    }

    private void ChangeGameState(GameState newState) {
        currentGameState = newState;
        onGameStateChanged.Invoke();
    }

}
