﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Amanotes.ContentReader;
using MoreMountains.Tools;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class GameController : Singleton<GameController>
{

    [HideInInspector] public AudioSource mainAudio;
    [SerializeField] private string mp3FileName;
    [SerializeField] private string binFileName;
    [SerializeField] private Object tilePrefab;
    [HideInInspector] public List<TileController> listTilesActive = new List<TileController>();

    private List<NoteData> notesData;
    private List<TileController> listTiles = new List<TileController>();
    private Coroutine audioSpeedRate;
    private int loopCount = 0;
    private int score;
    private int curNoteId;
    private float gameAudioDuration;
    private Vector3 lastTilePos;
    [Header("Result Screen")]
    [SerializeField] private GameObject resultScreen;
    [SerializeField] private Text textScoreResult;
    [Header("Gameplay Screen")]
    [SerializeField] private GameObject gameplayScreen;
    [SerializeField] private Text textScore;
    [SerializeField] private Image gameBackground;
    [HideInInspector] public GameStage gameStage;

    public void Awake()
    {
        ContentNoteGenerator.InitKey("U2FsdGVkX1/VnIMRYhPOzn55L1jkUVbI+bzCFYsPypefgl8pxW5uPY15GSzBNsma");
        mainAudio = GetComponent<AudioSource>();

        mainAudio.clip = GetAudioClip(mp3FileName);
        notesData = GetNotesData(binFileName);

        ResetGameData();
    }

    private AudioClip GetAudioClip(string fileName)
    {
        string contentPath = Utils.GetStreamingAssetPathForWWW(fileName);
        WWW reader = new WWW(contentPath);
        while (!reader.isDone) { }
        return reader.GetAudioClip();
    }

    private List<NoteData> GetNotesData(string fileName)
    {
        string contentPath = Utils.GetStreamingAssetPathForWWW(fileName);
        WWW reader = new WWW(contentPath);
        while (!reader.isDone) { }
        List<NoteData> notesData = new List<NoteData>();
        NoteData noteBase = new NoteData();
        noteBase.timeAppear = 0;
        noteBase.nodeID = 0;
        notesData.Add(noteBase);
        NoteGeneration.GetNotes(reader.bytes, NoteTab.Easy,
            (res, bpm) =>
            {
                for (int i = 0; i < res.Count; i++)
                {
                    if (res[i].timeAppear - notesData[notesData.Count - 1].timeAppear > 0.01 && res[i].timeAppear > 0.2f)
                    {
                        res[i].nodeID = notesData.Count;
                        notesData.Add(res[i]);

                    }
                }

                Debug.Log("Total notes: " + notesData.Count);
            },
            (err) =>
            {
                Debug.Log(err);
            });
        return notesData;
    }

    private void ResetGameData()
    {

        loopCount = 0;
        score = 0;
        PlayerController.Instance.ResetPlayerData();
        listTilesActive = new List<TileController>();
        gameStage = GameStage.Ready;
        for (int i = 0; i < listTiles.Count; i++)
        {
            listTiles[i].gameObject.SetActive(false);
        }
        curNoteId = 0;
        lastTilePos = Vector3.zero;

        for (int i = 0; i < 5; i++)
        {
            listTilesActive.Add(SpawnTile(notesData[curNoteId], false));
            curNoteId++;
        }
        gameAudioDuration = mainAudio.clip.length;
        mainAudio.time = 0;
    }

    private void Update()
    {

        if (gameStage == GameStage.Ready)
        {
            if (Input.GetMouseButtonDown(0))
            {

                mainAudio.Play();
                gameStage = GameStage.Start;
                PlayerController.Instance.OnGround();

            }

        }
        else
        {
            textScore.text = score.ToString();
        }


        if (curNoteId >= notesData.Count)
        {

            curNoteId = 0;
            loopCount++;
        }
        if (listTilesActive.Count < 2)
        {
            listTilesActive.Add(SpawnTile(notesData[curNoteId], true));
            curNoteId++;
        }
        else if (mainAudio.time + 2f >= gameAudioDuration && mainAudio.time + 2f - gameAudioDuration > notesData[curNoteId].timeAppear)
        {

            listTilesActive.Add(SpawnTile(notesData[curNoteId], true));
            curNoteId++;

        }
        else if (mainAudio.time + 2f >= notesData[curNoteId].timeAppear && GameController.Instance.notesData[curNoteId].timeAppear >= mainAudio.time - 2f)
        {
            listTilesActive.Add(SpawnTile(notesData[curNoteId], true));
            curNoteId++;
        }

    }


    public void PlayerGetScore(int score)
    {
        this.score += score;
    }

    private TileController SpawnTile(NoteData note, bool doActionFade)
    {
        float posX = 0;
        float posZ = (note.timeAppear + loopCount * gameAudioDuration) * GameConst.TILE_DISTANCE;
        if (note.timeAppear > 0 || note.nodeID > 0)
        {
            posX = Random.Range(-GameConst.MAX_SPAWN_TILE_WIDTH_SPACE, GameConst.MAX_SPAWN_TILE_WIDTH_SPACE);
            float deltaPosX = (posZ - lastTilePos.z) / GameConst.TILE_DISTANCE * GameConst.MAX_SPAWN_TILE_WIDTH_SPACE;
            if (posX > lastTilePos.x + deltaPosX) posX = lastTilePos.x + deltaPosX;
            if (posX < lastTilePos.x - deltaPosX) posX = lastTilePos.x - deltaPosX;
        }
        Vector3 position = new Vector3(posX, 0, posZ);
        TileController temp = GetTile();
        temp.gameObject.SetActive(true);
        temp.noteData = note;
        temp.spawnPosition = position;
        temp.transform.position = position;
        float fadeTime = 1f;
        if (doActionFade)
        {
            temp.transform.position += new Vector3(0, 0, 100);
            temp.DoFadeIn(fadeTime);
        }
        lastTilePos = position;
        return temp;
    }


    private TileController GetTile()
    {
        for (int i = 0; i < listTiles.Count; i++)
        {
            if (!listTiles[i].gameObject.activeSelf)
            {
                return listTiles[i];
            }
        }
        return InitTile();
    }

    private TileController InitTile()
    {
        TileController temp = (Instantiate(tilePrefab, transform) as GameObject).GetComponent<TileController>();
        listTiles.Add(temp);
        return temp;

    }

    IEnumerator ShowResultScreen()
    {
        yield return new WaitForSeconds(0.3f);
        textScoreResult.text = score.ToString();
        resultScreen.gameObject.SetActive(true);

    }

    public void PlayerDead()
    {
        mainAudio.Pause();
        gameStage = GameStage.Lose;
        StartCoroutine(ShowResultScreen());
    }

    public void PlayerWin()
    {
        mainAudio.Pause();
        gameStage = GameStage.Win;
        StartCoroutine(ShowResultScreen());
    }


    public void OnClickButtonReplay()
    {
        resultScreen.gameObject.SetActive(false);
        ResetGameData();
    }

}

public enum GameStage
{
    Ready, Start, Lose, Win
}


[System.Serializable]
public class Theme
{
    public List<Sprite> backgrounds;
    public Object tile;


}
