using NUnit.Framework;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

enum GameStatus
{
    waiting_on_first_card,
    waiting_on_second_card,
    match_found,
    no_match_found,
    game_complete
}

public class Game : MonoBehaviour
{

    [SerializeField] int rows;
    [SerializeField] int columns;

    [SerializeField] float totalPairs;

    [SerializeField] string frontsidesFolder;
    [SerializeField] string backsidesFolder;

    [SerializeField] Sprite[] frontSides;
    [SerializeField] Sprite[] backSides;

    [SerializeField] Sprite selectedBackSprite;

    [SerializeField] List<Sprite> selectedFrontSprites;

    [SerializeField] GameObject cardPrefab;
    [SerializeField] Stack<GameObject> stackOfCards;
    [SerializeField] GameObject[,] placedCards;

    [SerializeField] Transform fieldAnchor;
    [SerializeField] float offsetX;
    [SerializeField] float offsetY;

    GameStatus status;

    [SerializeField] GameObject[] selectedCards;

    private float timeoutTimer;
    [SerializeField] private float timeoutTarget;

    private byte lives = 10;
    private byte cardsLeft;

    [SerializeField] TextMeshProUGUI lifeText;

    [SerializeField] TextMeshProUGUI resultText;
    [SerializeField] GameObject resultPanel;

    [SerializeField] AudioSource laughSFX;
    [SerializeField] AudioSource cheerSFX;

    public bool AllowedToSelectCard(Card card)
    {
        if (selectedCards[0] == null)
        {
            return true;
        }

        if (selectedCards[1] == null)
        {
            if (selectedCards[0] != card.gameObject)
            {
                return true;
            }
        }

        return false;
    }


    void Start()
    {
        laughSFX = GameObject.Find("Laugh").GetComponent<AudioSource>();
        cheerSFX = GameObject.Find("Cheer").GetComponent<AudioSource>();
        MakeCards();
        DistributeCards();
        selectedCards = new GameObject[2];
        status = GameStatus.waiting_on_first_card;

        cardsLeft = (byte)(rows * columns);
        lifeText.text = "Levens: " + lives;
    }

    void Update()
    {
        if (status == GameStatus.match_found || status == GameStatus.no_match_found)
        {
            RotateBackOrRemovePair();
        }
    }

    private void MakeCards()
    {
        CalculateAmountOfPairs();
        LoadSprites();
        selectBackSprite();
        SelectFrontSprites();
        ConstructCards();
    }

    private void DistributeCards()
    {
        placedCards = new GameObject[columns, rows];
        ShuffleCards();
        PlaceCardsOnField();
    }

    private void CalculateAmountOfPairs()
    {
        if (rows * columns % 2 == 0) // Even aantal
        {
            totalPairs = rows * columns / 2;
        }
        else 
        {
            Debug.LogError("Oneven aantal kaarten!");
        }
    }

    private void LoadSprites()
    {
        frontSides = Resources.LoadAll<Sprite>(frontsidesFolder);
        backSides = Resources.LoadAll<Sprite>(backsidesFolder);
    }

    private void selectBackSprite()
    {
        if (backSides.Length > 0)
        {
            int rnd = Random.Range(0, backSides.Length);

            selectedBackSprite = backSides[rnd];
        }
        else
        {
            Debug.LogError("Er zijn geen beschikbare backsprites");
        }
    }

    private void SelectFrontSprites() 
    {
        if (frontSides.Length < totalPairs) {
            Debug.LogError("Er zijn te wenig plaatjes voor " + totalPairs + " paren!");
        }

        selectedFrontSprites = new List<Sprite>();

        while (selectedFrontSprites.Count < totalPairs) // zolang kleiner dan totaal aantal paren
        {
            int rnd = Random.Range(0, frontSides.Length); // pak een random kaart

            if (selectedFrontSprites.Contains(frontSides[rnd]) == false) // check of het niet al in gebruik is
            {
                selectedFrontSprites.Add(frontSides[rnd]); // voeg sprite toe
            }
        }
    }

    private void ConstructCards()
    {
        // Maak een stapel aan
        stackOfCards = new Stack<GameObject>();

        // maak een parent aan
        GameObject parent = new GameObject();
        parent.name = "Cards";

        // maak elke kaart aan
        foreach (Sprite sprite in selectedFrontSprites) 
        {
            for (int i =0; i < 2; i++) // 2x per kaart
            {
                GameObject go = Instantiate(cardPrefab); // maak kaart
                Card cardScript = go.GetComponent<Card>(); // krijg script

                cardScript.SetBack(selectedBackSprite);
                cardScript.SetFront(sprite);

                go.name = sprite.name;
                go.transform.parent = parent.transform;

                stackOfCards.Push(go); // zet in stack
            }
        }
    }

    private void ShuffleCards()
    {
        while (stackOfCards.Count > 0)
        {
            int randX = Random.Range(0, columns);
            int randY = Random.Range(0, rows);

            if (placedCards[randX, randY] == null)
            {
                Debug.Log("Kaart" + stackOfCards.Peek().name + " is geplaats op x: " + randX + " y: " + randY);

                placedCards[randX, randY] = stackOfCards.Peek();

                stackOfCards.Pop();
            }
        }
    }

    private void PlaceCardsOnField()
    {
        for (int y = 0; y < rows; y++) // voor alles op y as
        {
            for (int x = 0; x < columns; x++) // voor alles in x as
            {
                // maak var aan voor kaart
                GameObject Card = placedCards[x, y];

                // krijg script
                Card cardScript = Card.GetComponent<Card>();

                // krijg grootte
                Vector2 cardSize = cardScript.GetBackSize();

                // bereken juiste positie
                float xpos = fieldAnchor.transform.position.x + (x * (cardSize.x + offsetX + cardSize.x / 2));
                float ypos = fieldAnchor.transform.position.y + (y * (cardSize.y - offsetY + cardSize.y / 2));

                Debug.Log(Card.transform.lossyScale.x);

                // zet het op de juiste positie
                placedCards[x,y].transform.position = new Vector3 (xpos, ypos, 0);


                
            }
        }
    }

    public void SelectCard(GameObject card)
    {
        if (status == GameStatus.waiting_on_first_card)
        {
            selectedCards[0] = card;
            status = GameStatus.waiting_on_second_card;
        }
        else if (status == GameStatus.waiting_on_second_card)
        {
            selectedCards[1] = card;
            CheckForMatchingPair();
        }
    }

    private void CheckForMatchingPair()
    {
        timeoutTimer = 0f;

        if (selectedCards[0].name == selectedCards[1].name)
        {
            status = GameStatus.match_found;
        }
        else
        {
            status = GameStatus.no_match_found;
        }
    }

    private void RotateBackOrRemovePair()
    {
        timeoutTimer += Time.deltaTime;

        if (timeoutTimer >= timeoutTarget)
        {
            if (status == GameStatus.match_found)
            {
                selectedCards[0].SetActive(false);
                selectedCards[1].SetActive(false);
                cardsLeft -= 2;
                cheerSFX.pitch = Random.Range(1.5f, 2f);
                cheerSFX.Play();
            }
            else
            {
                selectedCards[0].GetComponent<Card>().TurnToBack();
                selectedCards[1].GetComponent<Card>().TurnToBack();
                lives -= 1;
                lifeText.text = "Levens: " + lives;
                laughSFX.pitch = Random.Range(0.5f, 2f);
                laughSFX.Play();
            }

            selectedCards[0] = null;
            selectedCards[1] = null;

            if (lives == 0 || cardsLeft <= 0)
            {
                resultPanel.SetActive(true);
                if (lives == 0)
                {
                    resultText.text = "GAME OVER"; // Doe alleen kaarten weg als er er geen zijn
                    GameObject cardsParent = GameObject.Find("Cards");
                    cardsParent.SetActive(false);
                }
                else
                {
                    resultText.text = "WINNAAR!";
                }

                status = GameStatus.game_complete;

            }
            else
            {
                status = GameStatus.waiting_on_first_card;
            }
        }
    }
}
