using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Game : MonoBehaviour 
{
    [System.Serializable]
    public class Row
    {
        public GameObject[] blockObjects;
        public GameObject fallingObject;
        public GameObject fadeBlock;
        public Color[] blockColours;

        [HideInInspector]
        public float fallingBlockTime = 0;

        [HideInInspector]
        public bool isFallingBlock = false;

        [HideInInspector]
        public int currentLength = 1;
    };

    [System.Serializable]
    public class Player
    {
        public GameObject playerObject;
        public ParticleSystem explodeObject;
        public int currentRow;
    };

    public Color[] colours;

    public ScreenShake screenShake;

    public AudioSource[] audioSources;

    public float fallTime;
    public float nextFallTime;

    public TextMesh scoreText;

    public Player player;
    public Row[] rows;

    private int currentScore = 0;

    private Color fallColour;
    private bool isGameOver = false;

	void Start () 
    {
        for (int i = 0; i < rows.Length; i++)
        {
            Row row = rows[i];
            row.blockColours = new Color[row.blockObjects.Length];

            Color newCol = colours[Random.Range(0, colours.Length)];
            SetColour(row.blockObjects[0], newCol);
            row.blockColours[0] = newCol;
        }

        player.currentRow = Random.Range(0, rows.Length);
        SetPlayerPosition(player.currentRow, 0);

        StartCoroutine(StartFallDelayed());
	}

    void SetPlayerPosition(int rowID, int rowHeight)
    {
        player.playerObject.transform.position = new Vector3(-8 + rowID, -4 + rowHeight, -0.4f);
    }

    void SetColour(GameObject blockObject, Color newCol)
    {
        MeshRenderer firstBlockRenderer = blockObject.GetComponentInChildren<MeshRenderer>();
        firstBlockRenderer.material.SetColor("_EmissionColor", newCol);
        firstBlockRenderer.material.SetColor("_Color", newCol);
    }

    int GetRandomRow()
    {
        int playerLength = rows[player.currentRow].currentLength;

        bool isLeftBigger = (player.currentRow > 0) && (rows[player.currentRow-1].currentLength - playerLength) >= 2;
        bool isRightBigger = (player.currentRow < rows.Length - 1) && (rows[player.currentRow + 1].currentLength - playerLength) >= 2;

        //Higher chance of getting current row if player is stuck
        if ((isLeftBigger || isRightBigger) && Random.Range(0, 100) < 75)
        {
            return player.currentRow;
        }

        //Higher chance of being closer to player than further away
        if (Random.Range(0, 100) > 65)
        {
            int randRow = 0;

            do
            {
                randRow = Random.Range(Mathf.Clamp(player.currentRow - 4, 0, rows.Length), Mathf.Clamp(player.currentRow + 4, 0, rows.Length));
            }
            while (rows[randRow].currentLength >= rows[randRow].blockObjects.Length);

            return randRow;
        }
        else
        {
            int randRow = 0;

            do
            {
                randRow = Random.Range(0, rows.Length);
            }
            while (rows[randRow].currentLength >= rows[randRow].blockObjects.Length);

            return randRow;
        }
    }

    IEnumerator StartFallDelayed()
    {
        int nextRow = GetRandomRow();
        rows[nextRow].fadeBlock.gameObject.SetActive(true);

        fallColour = colours[Random.Range(0, colours.Length)];
        SetColour(rows[nextRow].fallingObject, fallColour);
        SetColour(rows[nextRow].fadeBlock, new Color(fallColour.r, fallColour.g, fallColour.b, 0.4f));

        yield return new WaitForSeconds(nextFallTime);
        StartFall(nextRow); //Start next random one
    }

    IEnumerator EndAnimation()
    {
        for (int j = 0; j < rows.Length; j++)
        {
            rows[j].fadeBlock.SetActive(false);
            rows[j].fallingObject.SetActive(false);

            for (int i = 15; i > 0; i--)
            {
                if (i < rows[j].blockObjects.Length)
                {
                    rows[j].blockObjects[i].SetActive(false);
                    yield return new WaitForSeconds(0.01f);
                }
            }

            audioSources[3].Play();
        }

        yield return new WaitForSeconds(0.2f);

        SceneManager.LoadScene("Menu");
    }

    void IncreaseRow(int rowID)
    {
        Row row = rows[rowID];
        row.currentLength++;

        if (player.currentRow == rowID)
        {
            SetPlayerPosition(player.currentRow, rows[player.currentRow].currentLength - 1);
        }

        SetColour(row.blockObjects[0], fallColour);

        for (int i = 0; i < row.currentLength; i++)
        {
            row.blockObjects[i].SetActive(true);
        }

        for (int i = row.currentLength-1; i >= 1; i--)
        {
            row.blockColours[i] = row.blockColours[i - 1];
            SetColour(row.blockObjects[i], row.blockColours[i]);
            row.blockObjects[i].SetActive(true);
        }

        row.blockColours[0] = fallColour;
    }

    void DecreaseRow(int rowID)
    {
        Row row = rows[rowID];
        row.currentLength--;

        if (player.currentRow == rowID)
        {
            SetPlayerPosition(player.currentRow, rows[player.currentRow].currentLength - 1);
        }

        row.blockObjects[row.currentLength].SetActive(false);

        for (int i = row.currentLength - 1; i >= 1; i--)
        {
            row.blockColours[i] = row.blockColours[i - 1];
            SetColour(row.blockObjects[i], row.blockColours[i]);
            row.blockObjects[i].SetActive(true);
        }
    }

    void StartFall(int rowID)
    {
        Row row = rows[rowID];

        row.fallingBlockTime = 0;
        row.isFallingBlock = true;
        row.fallingObject.transform.localPosition = new Vector3(0, Mathf.Lerp(-4, -1, row.fallingBlockTime), 0);
        row.fallingObject.gameObject.SetActive(true);
    }

    void Update()
    {
        if (!isGameOver)
        {
            currentScore += 1;
            scoreText.text = currentScore.ToString("N0");

            if (rows[player.currentRow].currentLength >= rows[player.currentRow].blockObjects.Length)
            {
                audioSources[1].Play();

                player.explodeObject.transform.position = player.playerObject.transform.position - new Vector3(0, 1, 0);
                player.explodeObject.Play();

                player.playerObject.SetActive(false);

                StopAllCoroutines();
                StartCoroutine(EndAnimation());

                if (currentScore > PlayerPrefs.GetInt("ScoreBest"))
                {
                    PlayerPrefs.SetInt("ScoreBest", currentScore);
                    PlayerPrefs.Save();
                }

                isGameOver = true;
                return;
            }

            if (Input.GetKeyDown(KeyCode.A))
            {
                if (player.currentRow > 0 && (rows[player.currentRow - 1].currentLength - rows[player.currentRow].currentLength) <= 1)
                {
                    audioSources[0].pitch = Random.Range(0.9f, 1.1f);
                    audioSources[0].Play();

                    screenShake.ShakeScreenRandom(0.2f);

                    player.currentRow--;
                    SetPlayerPosition(player.currentRow, rows[player.currentRow].currentLength - 1);
                }
            }

            if (Input.GetKeyDown(KeyCode.D))
            {
                if (player.currentRow < rows.Length - 1 && (rows[player.currentRow + 1].currentLength - rows[player.currentRow].currentLength) <= 1)
                {
                    audioSources[0].pitch = Random.Range(0.9f, 1.1f);
                    audioSources[0].Play();

                    screenShake.ShakeScreenRandom(0.2f);

                    player.currentRow++;
                    SetPlayerPosition(player.currentRow, rows[player.currentRow].currentLength - 1);
                }
            }

            for (int i = 0; i < rows.Length; i++)
            {
                Row row = rows[i];

                if (row.isFallingBlock)
                {
                    if (row.fallingBlockTime < fallTime)
                    {
                        row.fallingBlockTime += Time.deltaTime;
                        row.fallingObject.transform.localPosition = new Vector3(0, Mathf.Lerp(-4, -1, row.fallingBlockTime / fallTime), 0);
                    }
                    else
                    {
                        if (row.currentLength < row.blockObjects.Length)
                        {
                            audioSources[2].pitch = Random.Range(0.9f, 1.1f);
                            audioSources[2].Play();

                            screenShake.ShakeScreenRandom(0.1f);
                            IncreaseRow(i);
                        }

                        row.isFallingBlock = false;
                        row.fallingObject.gameObject.SetActive(false);
                        row.fadeBlock.gameObject.SetActive(false);

                        StartCoroutine(StartFallDelayed());
                    }
                }
            }
        }
    }
}