using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using System.Collections;

public class LevelEnder : MonoBehaviour
{
    public WorldFiller worldFiller;

    public BallSpawner ballSpawner;

    public MultiplierSpawner multiplierSpawner;

    public SharkSpawner sharkSpawner;

    public CakeSpawner cakeSpawner;

    public MusicManager musicManager;

    public Player player;

    public SpriteRenderer levelEndTally;

    public SpriteRenderer levelEndTallyBackground;

    public Animator levelEndTallyAnimator;

    public AudioClip victory;

    public AudioClip showBonus;

    public AudioClip countBonus;

    public AudioClip multiplierBonusSound;

    public AudioClip multiplierPenaltySound;

    public Text timeBonus;

    public Text overachieverBonus;

    public Text overachieverPercent;

    public Text isolationBonus;

    public Text isolationCount;

    public Text multiplierBonus;

    public Text totalBonus;

    public SpriteRenderer overarchieverPercentLabel;

    public SpriteRenderer isolationCountSingleLabel;

    public SpriteRenderer isolationCountPluralLabel;

    private AudioSource audioSource;

    private bool levelEnded = false;

    private bool tallyShown = false;

    private bool addScoreToPlayer = false;

    private int scoreTotal = 0;

    private float addScoreTimer = 0.0f;

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        audioSource = GetComponent<AudioSource>();
    }

    // Update is called once per frame
    void Update()
    {
        // Check for victory condition.
        if (
            player && 
            player.percentCovered >= 80 && 
            !levelEnded
        )
        {
            musicManager.PauseMusic();
            audioSource.PlayOneShot(victory);
            levelEnded = true;
            StartCoroutine(EndLevelAfterDelay());
        }

        if (
            levelEnded && 
            (
                Mouse.current.leftButton.wasPressedThisFrame ||
                Keyboard.current.anyKey.wasPressedThisFrame
            )
        )
        {
            addScoreToPlayer = true;
        }

        if (addScoreToPlayer && tallyShown)
        {
            addScoreTimer += Time.deltaTime;
            if (addScoreTimer >= 0.02f && scoreTotal > 0)
            {
                addScoreTimer = 0.0f;
                addScoreToPlayer = AddScore();

                if (!addScoreToPlayer)
                {
                    tallyShown = false;
                    StartCoroutine(ProceedToNextLevel());
                }
            }
        }
    }

    public void EndLevel()
    {
        tallyShown = false;

        // Stop all spawners.
        ballSpawner.StopSpawning();
        multiplierSpawner.StopSpawning();
        sharkSpawner.StopSpawning();
        cakeSpawner.StopSpawning();

        // Show the level end tally.
        musicManager.PlayVictoryMusic();
        levelEndTally.enabled = true;
        levelEndTallyBackground.enabled = true;
        levelEndTallyAnimator.ResetTrigger("replay");
        levelEndTallyAnimator.SetTrigger("replay");

        // Destroy all objects other than the player.
        ClearBoard();
    }

    private void ClearBoard()
    {
        // Clear the game board by destroying all objects except the player.
        var yummies = GameObject.FindGameObjectsWithTag("Yummy");
        var multipliers = GameObject.FindGameObjectsWithTag("Multiplier");
        var shark = GameObject.FindGameObjectWithTag("Shark");

        foreach (var yummy in yummies)
        {
            Destroy(yummy);
        }

        foreach (var multiplier in multipliers)
        {
            Destroy(multiplier);
        }

        if (shark != null)
        {
            Destroy(shark);
        }

        worldFiller.ClearSpawnedWalls();
    }

    private bool AddScore()
    {
        audioSource.PlayOneShot(countBonus);

        int scoreToAdd = scoreTotal > 100 ? 100 : scoreTotal;
        scoreTotal -= 100;
        scoreTotal = Mathf.Max(scoreTotal, 0);

        player.score += scoreToAdd;
        ShowBonus(totalBonus, scoreTotal, null, null, null, showBonus);

        if (scoreTotal <= 0)
        {
            audioSource.PlayOneShot(showBonus);
            return false;
        }

        return true;
    }

    private void ShowBonus(Text bonusText, int bonusValue, int? bonusCount, Text countLabel, SpriteRenderer bonusLabel, AudioClip bonusSound)
    {
        bonusText.text = $"{bonusValue}";

        if (bonusCount.HasValue)
        {
            countLabel.text = $"{bonusCount.Value}";
        }

        if (bonusLabel)
        {
            bonusLabel.enabled = true;
        }

        audioSource.PlayOneShot(bonusSound);
    }

    private IEnumerator EndLevelAfterDelay()
    {
        yield return new WaitForSeconds(victory.length);
        EndLevel();
        
        // Wait a moment before showing the bonus tally.
        yield return new WaitForSeconds(1.5f);
        scoreTotal = player.bonus;
        ShowBonus(timeBonus, player.bonus, null, null, null, showBonus);
        ShowBonus(totalBonus, scoreTotal, null, null, null, showBonus);
        
        // Calculate and show the overachiever bonus.
        yield return new WaitForSeconds(0.75f);
        int overachieverBonusCount = 20 - (100 - player.percentCovered);
        int overachieverBonusValue = 100 * overachieverBonusCount;
        scoreTotal += overachieverBonusValue;

        ShowBonus(
            overachieverBonus, 
            overachieverBonusValue, 
            overachieverBonusCount, 
            overachieverPercent, 
            overarchieverPercentLabel, 
            showBonus
        );
        ShowBonus(totalBonus, scoreTotal, null, null, null, showBonus);

        // Calculate and show the isolation bonus.
        yield return new WaitForSeconds(0.75f);
        int isolationBonusCount = 0;
        int isolationBonusValue = 100 * isolationBonusCount;
        scoreTotal += isolationBonusValue;

        ShowBonus(
            isolationBonus, 
            isolationBonusValue, 
            isolationBonusCount, 
            isolationCount, 
            isolationBonusCount == 1 ? isolationCountSingleLabel : isolationCountPluralLabel, 
            showBonus
        );
        ShowBonus(totalBonus, scoreTotal, null, null, null, showBonus);

        // Calculate and show the multiplier bonus if applicable.
        if (player.multiplier != 1.0f)
        {
            yield return new WaitForSeconds(0.75f);
            int multiplierValue = player.multiplier == 0.5f ? 1 : (int)(player.multiplier);
            scoreTotal = (int)(scoreTotal * player.multiplier);

            ShowBonus(
                multiplierBonus, 
                multiplierValue, 
                null, 
                null, 
                null, 
                multiplierValue == 1 ? multiplierPenaltySound : multiplierBonusSound
            );
            ShowBonus(totalBonus, scoreTotal, null, null, null, showBonus);
        }

        tallyShown = true;

        yield return new WaitForSeconds(7.3f - 3.0f - (player.multiplier != 1.0f ? 0.75f : 0.0f));
        addScoreToPlayer = true;
    }

    private IEnumerator ProceedToNextLevel()
    {
        musicManager.PauseMusic();

        yield return new WaitForSeconds(1.0f);
        
        player.OnLevelEnd();

        // Reenable spawners.
        multiplierSpawner.StartSpawning();
        sharkSpawner.StartSpawning();
        cakeSpawner.StartSpawning();
        ballSpawner.StartSpawning();

        // Reset state.
        levelEnded = false;
        tallyShown = false;
        addScoreToPlayer = false;
        scoreTotal = 0;
        addScoreTimer = 0.0f;

        // Clear text labels.
        timeBonus.text = "";
        totalBonus.text = "";
        isolationBonus.text = "";
        overachieverBonus.text = "";
        isolationCount.text = "";
        overachieverPercent.text = "";
        multiplierBonus.text = "";
        overarchieverPercentLabel.enabled = false;
        isolationCountSingleLabel.enabled = false;
        isolationCountPluralLabel.enabled = false;

        // Hide score tally.
        levelEndTally.enabled = false;
        levelEndTallyBackground.enabled = false;

        // TODO: Reset balls.

        // Restart music.
        musicManager.ResumeMusic();
    }
}
