using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(AudioSource))]
public class GameManager : MonoBehaviour
{
    public enum GameState
    {
        Begin,
        Running,
        NextStep,
        Step,
        End,
    };

    private static GameManager s_instance = null;

    [Header("Game Play")]
    [SerializeField]
    private float m_scrollingSpeed = 1f;

    [Header("Tank Settings")]
    [SerializeField]
    private List<Spawner> m_spawners = new List<Spawner>();
    [SerializeField]
    private SpawningRules[] m_rules;

    private SpawningRules m_currentSpawningRules;
    private int m_currentSpawningRule = 0;

    [Header("LD Settings")]
    [SerializeField]
    private GameObject m_spawnPoint;
    [SerializeField]
    private List<LevelDesign> m_ldRules;

    private LevelDesign m_currentLD;

    private int m_currentDifficultyLevel;
    private float m_cooldownBeforeStepScreen;
    private float m_cooldownBeforeNextRule;

    [Header("Player")]
    [SerializeField]
    private PlayerController m_player;
    [SerializeField]
    private GameObject m_startingPos;

    [Header("Sounds")]
    [SerializeField]
    private float m_fadeInSpeed;
    [SerializeField]
    private AudioSource m_ambianceEmitter;
    [SerializeField]
    private float m_ambianceFadeIn;
    [SerializeField]
    private AudioSource m_musicEmitter;
    [SerializeField]
    private AudioSource m_nextLevel;

    [Header("UI")]
    [SerializeField]
    private UnityEngine.UI.Image m_introImage;
    [SerializeField]
    private UnityEngine.UI.Text m_stepText;

    private bool m_firstBegin = true;

    private static GameState m_state = GameState.Begin;
    public static GameState State
    {
        get { return m_state; }
        set
        {
            m_state = value;

            if (m_state == GameState.Begin)
            {
                Score = 0;
                s_instance.m_currentDifficultyLevel = 0;
            }
            else if (m_state == GameState.Running)
            {
                ScrollingSpeedBase = s_instance.m_scrollingSpeed;
                ScrollingSpeed = ScrollingSpeedBase;

                s_instance.m_currentSpawningRules = s_instance.m_rules[Mathf.Min(s_instance.m_rules.Length - 1, s_instance.m_currentDifficultyLevel)];
                s_instance.m_currentSpawningRules.StartRule();

                s_instance.m_currentLD = s_instance.m_ldRules[Mathf.Min(s_instance.m_ldRules.Count - 1, s_instance.m_currentDifficultyLevel)];
                s_instance.m_currentLD.StartRule();

                Player.enabled = true;
                s_instance.m_firstBegin = false;
            }
            else if (m_state == GameState.NextStep)
            {
                if (Score != 0)
                {
                    s_instance.m_cooldownBeforeStepScreen = 5f;
                    s_instance.m_currentDifficultyLevel++;
                }
            }
            else if (m_state == GameState.Step)
            {
                s_instance.m_nextLevel.Play();

                // display step message.
                s_instance.m_stepText.text = "Level " + (s_instance.m_currentDifficultyLevel + 1);
                Color color = s_instance.m_stepText.color;
                color.a = 0f;
                s_instance.m_stepText.color = color;

                s_instance.m_cooldownBeforeNextRule = 5f;
            }
            else if (m_state == GameState.End)
            {
                ExecuteEvents.Execute<IEventGameOver>(null, (_x, _y) => { _x.OnGameOver(); });
            }
        }
    }

    public static float ScrollingSpeed
    {
        get;

        set;
    }

    public static float ScrollingSpeedBase { get; private set; }

    public static Spawner GetSpawner()
    {
        return s_instance.m_spawners[Random.Range(0, s_instance.m_spawners.Count)];
    }


    public static PlayerController Player
    {
        get
        {
            return s_instance.m_player;
        }
    }

    public static GameObject GetSpawnPoint()
    {
        return s_instance.m_spawnPoint;
    }

    public static int Score { get; set; }

    #region Unity Callback
    // Use this for initialization
    protected void Start()
    {
        s_instance = this;
        State = GameState.Begin;
    }

    // Update is called once per frame
    protected void Update()
    {
        switch (m_state)
        {
            case GameState.Begin:
                {
                    m_ambianceFadeIn += m_fadeInSpeed * Time.deltaTime;
                    if (m_firstBegin)
                    {
                        m_ambianceEmitter.volume = m_ambianceFadeIn;
                    }

                    if (m_musicEmitter.isPlaying)
                    {
                        m_musicEmitter.volume = (m_ambianceFadeIn / 0.5f) - 1f;

                        if (m_firstBegin)
                        {
                            Color color = m_introImage.color;
                            color.a = 2f - (m_ambianceFadeIn / 0.5f);
                            m_introImage.color = color;
                        }
                    }
                    else if (m_ambianceFadeIn > 0.5f)
                    {
                        m_musicEmitter.volume = 0f;
                        m_musicEmitter.Play();
                    }

                    if (m_ambianceFadeIn > 1f)
                    {
                        State = GameState.NextStep;
                    }
                }
                break;
            case GameState.Running:
                {
                    if (m_currentSpawningRules.Update())
                    {
                        //m_currentSpawningRules = s_instance.m_rules[Random.Range(0, s_instance.m_rules.Length)];
                        m_currentSpawningRules.StartRule();
                    }

                    if (m_currentLD.Update())
                    {
                        State = GameState.NextStep;
                    }
                }
                break;
            case GameState.NextStep:
                {
                    m_cooldownBeforeStepScreen -= Time.deltaTime;
                    if (m_cooldownBeforeStepScreen <= 0f)
                    {
                        State = GameState.Step;
                    }
                }
                break;
            case GameState.Step:
                {
                    m_cooldownBeforeNextRule -= Time.deltaTime;
                    if (m_cooldownBeforeNextRule <= 0f)
                    {
                        State = GameState.Running;
                    }

                    // fade in.
                    Color color = m_stepText.color;
                    if (m_cooldownBeforeNextRule >= 4.8f)
                    {
                        color.a = (5f - m_cooldownBeforeNextRule)/0.2f;
                    }
                    // fade out.
                    else if (m_cooldownBeforeNextRule <= 0.2f)
                    {
                        color.a = m_cooldownBeforeNextRule/0.2f;
                    }
                    m_stepText.color = color;
                }
                break;
            case GameState.End:
                {
                    ScrollingSpeed -= Time.deltaTime;
                    if (ScrollingSpeed < 0f)
                    {
                        ScrollingSpeed = 0f;
                    }

                    if (m_musicEmitter.isPlaying)
                    {
                        float fadeout = ScrollingSpeed / ScrollingSpeedBase;
                        m_musicEmitter.volume = fadeout;
                        if (m_musicEmitter.volume <= 0f)
                        {
                            m_musicEmitter.Stop();
                        }
                    }
                    else if (Input.GetButtonDown("Submit"))
                    {
                        m_player.transform.position = m_startingPos.transform.position;
                        State = GameState.Begin;
                        m_ambianceFadeIn = 0f;
                        Player.ResetAnim();
                    }
                }
                break;
        }
    }
    #endregion
}
