using System.Collections.Generic;
using UnityEngine;
using CorgiLabs.Utility;

public class GameManager : MonoBehaviour
{
    public int Level
    {
        get { return m_Level; }
        set
        {
            m_Level = value;

            EventManager.TriggerEvent(EventID.LevelChanged, m_Level);
        }
    }

    public bool CanStartGhostEvent
    {
        get { return m_ActiveSources.Count < m_Settings.GetMaxEvents(Level); }
    }

    public GameSettings Settings
    {
        get { return m_Settings; }
    }

    [SerializeField] private PlayerController[] m_PlayerPrefabs = new PlayerController[2];
    [SerializeField] private Transform[] m_PlayerSpawnPoints = new Transform[2];

    [SerializeField] private GameSettings m_Settings;

    private int m_Level;

    public int FailCount
    {
        get { return m_FailCount; }
        private set
        {
            m_FailCount = value;

            EventManager.TriggerEvent(EventID.FailsChanged, m_FailCount, m_Settings.GetFailThreshold(Level));
        }
    }

    private int m_FailCount = 0;

    private int m_Score = 0;

    private List<GhostEventSource> m_AvailableSources = new List<GhostEventSource>();
    private List<GhostEventSource> m_ActiveSources = new List<GhostEventSource>();

    private PlayerController[] m_Players;

    private void Awake()
    {
        ServiceLocator.Provide(this);

        int count = m_PlayerSpawnPoints.Length;

        m_Players = new PlayerController[count];

        for (int i = 0; i < count; i++)
        {
            m_Players[i] = Instantiate(m_PlayerPrefabs[i], m_PlayerSpawnPoints[i].position, Quaternion.identity);
            m_Players[i].Index = i;
        }

        new GameObject("Input Handler", typeof(InputHandler)).GetComponent<InputHandler>().SetPlayers(m_Players);
    }

    private void OnEnable()
    {
        EventManager.StartListening<GhostEventSource>(EventID.GhostEventStart, OnGhostEventStart);
        EventManager.StartListening<GhostEventSource>(EventID.GhostEventSuccess, OnGhostEventSuccess);
        EventManager.StartListening<GhostEventSource>(EventID.GhostEventFail, OnGhostEventFail);
    }

    private void OnDisable()
    {
        EventManager.StopListening<GhostEventSource>(EventID.GhostEventStart, OnGhostEventStart);
        EventManager.StopListening<GhostEventSource>(EventID.GhostEventSuccess, OnGhostEventSuccess);
        EventManager.StopListening<GhostEventSource>(EventID.GhostEventFail, OnGhostEventFail);
    }

    private void Start()
    {
        Level = 1;
        FailCount = 0;

        Vector2 randomTime = m_Settings.GetRandomEventTimes(Level);

        Invoke("StartRandomEvent", Random.Range(randomTime.x, randomTime.y));

        EventManager.TriggerEvent(EventID.PlayersInstantiated, m_Players);
    }

    private void StartRandomEvent()
    {
        int count = m_AvailableSources.Count;

        if (count > 0 && CanStartGhostEvent)
        {
            GhostEventSource source = m_AvailableSources[Random.Range(0, count - 1)];

            ChangeEventSourceAvailable(source, false);

            source.StartEvent();
        }


        Vector2 randomTime = m_Settings.GetRandomEventTimes(Level);

        Invoke("StartRandomEvent", Random.Range(randomTime.x, randomTime.y));
    }


    private void OnGhostEventStart(GhostEventSource source)
    {
        if (CanStartGhostEvent)
        {
            ChangeEventSourceAvailable(source, false);

            source.StartEvent();
        }
    }

    private void OnGhostEventFail(GhostEventSource source)
    {
        FailCount++;

        if (FailCount >= m_Settings.GetFailThreshold(Level))
        {
            CancelInvoke();
            EventManager.TriggerEvent(EventID.GameOver);
            enabled = false;

            foreach (PlayerController pl in m_Players)
                pl.Sleep();
        }
    }

    private void OnGhostEventSuccess(GhostEventSource source)
    {
        m_Score += source.Points;

        ChangeEventSourceAvailable(source, true);

        if (m_Score >= m_Settings.GetScoreThreshold(Level))
            LevelUp();
    }

    private void LevelUp()
    {
        EventManager.TriggerEvent(EventID.LevelUp);

        m_Score -= m_Settings.GetScoreThreshold(Level);

        Level++;

        FailCount = 0;
    }

    public void ProvideEventSource(GhostEventSource source)
    {
        m_AvailableSources.Add(source);
    }

    public void ChangeEventSourceAvailable(GhostEventSource source, bool available)
    {
        if (available)
        {
            m_AvailableSources.Add(source);
            m_ActiveSources.Remove(source);
        }
        else
        {
            m_AvailableSources.Remove(source);
            m_ActiveSources.Add(source);
        }
    }
}
