#include <iostream> 
#include <SFML/Graphics.hpp>
#include <string>
#include <cstdlib>
#include <ctime>
#include <cmath> 
#include <SFML/Audio.hpp>

bool pixelPerfectCollision(
    const sf::Sprite& spriteA, const sf::Image& imageA,
    const sf::Sprite& spriteB, const sf::Image& imageB)
{
    sf::FloatRect boundsA = spriteA.getGlobalBounds();
    sf::FloatRect boundsB = spriteB.getGlobalBounds();

    auto intersectionOpt = boundsA.findIntersection(boundsB);
    if (!intersectionOpt.has_value())
        return false;

    sf::FloatRect overlap = intersectionOpt.value();

    for (float y = overlap.position.y; y < overlap.position.y + overlap.size.y; y += 1.f)
    {
        for (float x = overlap.position.x; x < overlap.position.x + overlap.size.x; x += 1.f)
        {
            unsigned int localXA = static_cast<unsigned int>((x - spriteA.getPosition().x) / spriteA.getScale().x);
            unsigned int localYA = static_cast<unsigned int>((y - spriteA.getPosition().y) / spriteA.getScale().y);

            unsigned int localXB = static_cast<unsigned int>((x - spriteB.getPosition().x) / spriteB.getScale().x);
            unsigned int localYB = static_cast<unsigned int>((y - spriteB.getPosition().y) / spriteB.getScale().y);

            if (localXA >= imageA.getSize().x || localYA >= imageA.getSize().y) continue;
            if (localXB >= imageB.getSize().x || localYB >= imageB.getSize().y) continue;

            sf::Color colorA = imageA.getPixel({localXA, localYA});
            sf::Color colorB = imageB.getPixel({localXB, localYB});

            if (colorA.a > 0 && colorB.a > 0)
                return true;
        }
    }
    return false;
}

float currentSpeed = 0;

int main() {
    std::cout << "PROGRAMMA AVVIATO!" << std::endl;

    sf::RenderWindow window(
        sf::VideoMode({800, 600}),//risoluzione gioco
        "LOVEBALL"
    );
    //MUSIC
    sf::Music menuMusic;
    sf::Music gameMusic;

    if (!menuMusic.openFromFile("menu_music.ogg"))
    {
        std::cerr << "ERRORE: impossibile caricare la musica del menu!" << std::endl;
    }

    menuMusic.setVolume(40.f);
    menuMusic.setLooping(true);

    if (!gameMusic.openFromFile("game_music.ogg"))
    {
        std::cerr << "ERRORE: impossibile caricare la musica del gioco!" << std::endl;
    }

    gameMusic.setVolume(50.f);
    gameMusic.setLooping(true);

    //TEXTURE DEL PLAYER
    //Creiamo un'immagine molto piccola: 32 pixel in largezza e 8 pixel in altezza;
    //Inizialmente tutti i pixel sono trasparenti.
    sf::Image playerImage({80, 8}, sf::Color::Transparent);

    //Colori
    //Creazione colori della barra
    sf::Color magenta(140, 20, 80);
    

    //Corpo nero della barra
    //Vogliamo calcolare la parte centrale della barra.
    //x indica la posizione orizzontale, y la posizione verticale.
    for (unsigned int x = 2; x < 46; x++)
    {
        for (unsigned int y = 1; y < 7; y++) 
        {   
            playerImage.setPixel({x, y}, sf::Color::Black);
            //Coloriamo il singolo pixel che si trova nella posizione {x, y}.
            //Il colore utilizzazto è nero.
        }
    }

    //Blocchi magenta

    //Blocco superiore
    for (unsigned int x = 1; x < 79; x++)
    {   
        playerImage.setPixel({x, 0}, magenta);
    }
    //Blocco inferiore
    for (unsigned int x = 1; x < 79; x++)
    {
        playerImage.setPixel({x, 7}, magenta);
    }
    //Bordo sinistro
    for (unsigned int y = 1; y < 7; y++)
    {
        playerImage.setPixel({1, y}, magenta);
    }
    //Bordo destro
    for (unsigned int y = 1; y < 7; y++)
    {
        playerImage.setPixel({78, y}, magenta);
    }

    //CUORI  <3

    //PRIMO CUORE <3
    //Parte superiore
    playerImage.setPixel({7, 2}, magenta);
    playerImage.setPixel({8, 2}, magenta);
    //Abbiamo colorato i pixel di magenta sia in verticale y che in orizzontale x.
    //Abbiamo lasciato il numero 9 per creare lo spazio tra le due gobbe del cuore.
    playerImage.setPixel({10, 2}, magenta);
    playerImage.setPixel({11, 2}, magenta);
    //Abbiamo colorato i pixel di magenta sia in verticale y che in orizzontale x.

    //Parte centrale
    for (unsigned int x = 6;  x <= 12; x++)
    {
        playerImage.setPixel({x, 3}, magenta);
    }
    //Parte inferiore
    for (unsigned int x = 7; x <= 11; x++)
    {
        playerImage.setPixel({x, 4}, magenta);
    }
    //Punta
    for (unsigned int x = 8; x <= 10; x++)
    {
        playerImage.setPixel({x, 5}, magenta);
    }
    //Invece di scrivere pixel per pixel, utilizziamo un FOR per ripetere la stessa operazione per diversi pixel.

    //SECONDO CUORE <3
    //Parte superiore
    playerImage.setPixel({35, 2}, magenta);
    playerImage.setPixel({36, 2}, magenta);

    playerImage.setPixel({38, 2}, magenta);
    playerImage.setPixel({39, 2}, magenta);

    //Parte centrale
    for (unsigned int x = 34; x <= 40; x++)
    {
        playerImage.setPixel({x, 3}, magenta);
    }
    //Parte inferiore
    for (unsigned int x = 35; x <= 39; x++)
    {
        playerImage.setPixel({x, 4}, magenta);
    }
    //Punta
    for (unsigned int x = 36; x <= 38; x++)
    {
        playerImage.setPixel({x, 5}, magenta);
    }

    //TERZO CUORE <3
    //Parte superiore
    playerImage.setPixel({63, 2}, magenta);
    playerImage.setPixel({64, 2}, magenta);

    playerImage.setPixel({66, 2}, magenta);
    playerImage.setPixel({67, 2}, magenta);

    //Parte centrale
    for (unsigned int x = 62; x <= 68; x++)
    {
        playerImage.setPixel({x, 3}, magenta);
    }

    //Parte inferiore
    for (unsigned int x = 63; x <= 67; x++)
    {
        playerImage.setPixel({x, 4}, magenta);
    }

    //Punta
    for (unsigned int x = 64; x <= 66; x++)
    {
        playerImage.setPixel({x, 5}, magenta);
    }

    //USIAMO IL FOR
    //Perchè dobbiamo fare la stessa operazione su tanti pixel.

    sf::Texture playerTexture(playerImage);
    playerTexture.setSmooth(false);//Impedisce a sfml di sfumare/interpolare i pixel quando ingrandiamo la barra.

    //PLAYER
    sf::Sprite player(playerTexture);

    player.setScale({150.f / 80.f, 20.f / 8.f });

    player.setPosition({325.f, 550.f});

    //PALLINA PIXEL ART
    sf::Image ballImage({16, 16}, sf::Color::Transparent);

    //Forma della pallina
    const char* ballShape[] = //Ricordiamo che W = bianco e B = nero.
    {
        "     WWWWWW     ",
        "   WWBBBBBBWW   ",
        "  WBBBBBBBBBBW  ",
        " WBBBBBBBBBBBBW ",
        "WBBBBBBBBBBBBBBW",
        "WBBBBBBBBBBBBBBW",
        "WBBBBBBBBBBBBBBW",
        "WBBBBBBBBBBBBBBW",
        "WBBBBBBBBBBBBBBW",
        "WBBBBBBBBBBBBBBW",
        "WBBBBBBBBBBBBBBW",
        " WBBBBBBBBBBBBW ",
        "  WBBBBBBBBBBW  ",
        "   WWBBBBBBWW   ",
        "     WWWWWW     ",
        "                "
    };

    //Andiamo a contrallare nelle righe e nelle colonne quale lettera è associata e quindi di quale colore si colorerà.
    for (unsigned int y = 0; y < 16; y++)
    {
        for (unsigned int x = 0; x < 16; x++)
        {
            if (ballShape[y][x] == 'W')
            {
                ballImage.setPixel({x, y}, sf::Color::White);
            }
            else if (ballShape[y][x] == 'B')
            {
                ballImage.setPixel({x, y}, sf::Color::Black);
            }
        }
    }

    //SCRITTA LOVE
    //Ogni lettera è formata da 3 colonne x 5 righe.
    //1 = pixel acceso
    //0 = pixel spento

    const char* font[4][5]= 
    {
        //L
        {
            "100", 
            "100", 
            "100",
            "100",
            "111"
        },

        //O
        {
            "111",
            "101",
            "101",
            "101",
            "111"
        },
        
        //V
        {
            "101",
            "101",
            "101",
            "010",
            "010"
        },

        //E
        {
            "111",
            "100",
            "110",
            "100",
            "111"
        }
    };

    std::string love = "LOVE";//Parola che stiamo creando

    for (int riga = 0; riga < 3; riga++)//creiamo 3 righe
    {
        for (int i = 0; i < 4; i++)
        {
            int lettera = i % 4;//ci permette di ripetere LOVE
            int startX = 1 + i * 4;
            int startY = 2 + riga * 6;
        

            for (int y = 0 ; y < 5; y++)
            {
                for (int x = 0; x < 3; x++)
                {
                    if (font[lettera][y][x] == '1')
                    {
                        int pixelX = startX + x;
                        int pixelY = startY + y;

                        //Controlliamo che il pixel sia ancora dentro la pallina.
                        if (pixelX >= 0 && pixelX < 16 &&
                            pixelY >= 0 && pixelY < 16)
                            {
                                //Disegniamo la lettere esclusivamente sui pixel neri
                                if (ballShape[pixelY][pixelX] == 'B')
                                {
                                    ballImage.setPixel(
                                        {
                                            (unsigned int)pixelX,
                                            (unsigned int)pixelY
                                        },
                                    sf::Color::White
                                    );
                                };
                            }
                    }
                }
            }
        }
    }

    //Texture della pallina
    sf::Texture ballTexture(ballImage);
    ballTexture.setSmooth(false);

    //Sprite della pallina
    sf::Sprite ball(ballTexture);

    ball.setScale({2.f, 2.f});
    ball.setPosition({384.f, 50.f});

    //RETTANGOLO VERDE
    //Blocco 24 x 12 pixel
    sf::Image obstacleImage({24, 12}, sf::Color::Transparent);

    //Colori del rettangolo
    sf::Color obstacleGreenLight(120, 220, 140);
    sf::Color obstacleGreenDark(20, 120, 50);

    for (unsigned int y = 0; y < 12; y++)
    {
        for (unsigned int x = 0; x < 24; x++)
        {
            bool isEdge = (x == 0 || x == 23 || y == 0 || y == 11);
            obstacleImage.setPixel({x, y}, isEdge ? obstacleGreenLight : obstacleGreenDark);
        }
    }

    //Texture del rettangolo
    sf::Texture obstacleTexture(obstacleImage);
    obstacleTexture.setSmooth(false);

    //Sprite del rettangolo
    sf::Sprite obstacle(obstacleTexture);

    //Ingrandimento pixel
    obstacle.setScale({3.f, 3.f});

    //Posizione del rettangolo
    obstacle.setPosition({364.f, 110.f});


    //TRIANGOLO GIALLO
    //Blocco 28 x 24 pixel
    sf::Image obstacleTriangleImage({28, 24}, sf::Color::Transparent);

    //Colori del triangolo
    sf::Color obstacleYellowLight(240, 210, 60);
    sf::Color obstacleYellowDark(180, 140, 20);

    for (unsigned int y = 0; y < 24; y++)
    {
        unsigned int halfWidth = (y * 14) / 24;
        unsigned int left = 14 - halfWidth;
        unsigned int right = 14 + halfWidth;

        for (unsigned int x =left; x <= right && x < 28; x++)
            {
                bool isEdge = (x == left || x == right || y == 23);
                obstacleTriangleImage.setPixel({x, y}, 
                    isEdge ? obstacleYellowLight : obstacleYellowDark);
            }
    }

    //Texture del triangolo
    sf::Texture obstacleTriangleTexture(obstacleTriangleImage);
    obstacleTriangleTexture.setSmooth(false);

    //Sprite del triangolo
    sf::Sprite obstacleTriangle(obstacleTriangleTexture);

    //Ingrandimento pixel
    obstacleTriangle.setScale({3.f, 3.f});

    //Posizione del triangolo
    obstacleTriangle.setPosition({130.f, 400.f});

    //ROMBO CELESTE
    //Blocco 26 x 26 pixel
    sf::Image obstacleDiamondImage({26, 26}, sf::Color::Transparent);

    //Colori rombo
    sf::Color obstacleCyanLight(140, 210, 240);   // bordo
    sf::Color obstacleCyanDark(50, 130, 180); 

    int centerX = 13, centerY = 13, radius = 12;

    for (int y = 0; y < 26; y++)
    {
        for (int x = 0; x < 26; x++)
        {
            int distance = std::abs(x - centerX) + std::abs(y - centerY);

            if (distance <= radius)
            {
                bool isEdge = (distance >= radius - 1);
                obstacleDiamondImage.setPixel({(unsigned int)x, (unsigned int)y}, isEdge ? obstacleCyanLight : obstacleCyanDark);
            }
        }
    }

    //Texture del rombo
    sf::Texture obstacleDiamondTexture(obstacleDiamondImage);
    obstacleDiamondTexture.setSmooth(false);

    //Sprite del rombo
    sf::Sprite obstacleDiamond(obstacleDiamondTexture);

    //Ingrandimento pixel
    obstacleDiamond.setScale({3.f, 3.f});

    //Posizione del rombo
    obstacleDiamond.setPosition({570.f, 400.f});

    //CUORE GRANDE ROSA
    //Blocco 40 x 36 pixel
    sf::Image obstacleHeartImage({40, 36}, sf::Color::Transparent);

    //Colori del cuore
    sf::Color obstacleHeartLight(225, 120, 170);
    sf::Color obstacleHeartDark(180, 20, 90);
    for (unsigned int py = 0; py <36; py++)
    {
        for (unsigned int px = 0; px < 40; px++)
        {
            float x = (px - 20.f) / 14.f;//centriamo orizzontalmente
            float y = (18.f - py) / 14.f;//invertiamo Y (nell'immagine cresce verso il basso)

            float value = std::pow(x * x + y * y - 1.f, 3.f) 
                - x * x * std::pow(y, 3.f);

            if (value <= 0.f)
            {
                //Bordo = leggermente fuori dalla curva "ristretta" del 6%
                float xo = x * 1.06f, yo = y * 1.06f;
                float valueOutline = std::pow(xo * xo + yo * yo - 1.f, 3.f)
                    - xo * xo * std::pow(yo, 3.f);

                obstacleHeartImage.setPixel({px, py}, 
                    valueOutline > 0.f ? obstacleHeartLight : obstacleHeartDark);
            }
        }
    }

    //Texture del cuore 
    sf::Texture obstacleHeartTexture(obstacleHeartImage);
    obstacleHeartTexture.setSmooth(false);

    //Sprite del cuore
    sf::Sprite obstacleHeart(obstacleHeartTexture);

    //Ingrandiamo i pixel
    obstacleHeart.setScale({3.f, 3.f});

    //Posizione del cuore
    obstacleHeart.setPosition({340.f, 250.f});

    //FIORE MINACCIOSO
    //Immagine pixel art del fiore
    sf::Image flowerImage({24, 24}, sf::Color::Transparent);

    //Colori del fiore
    sf::Color flowerDark(80, 10, 60);
    sf::Color flowerLight(220, 40, 130);
    sf::Color flowerCenter(225, 180, 40);

    //Forma del Fiore
    const char* flowerShape[] =
    {
        "       LLLLLL       ",
        "     LLLLLLLLLL     ",
        "    LLLLLLLLLLLL    ",
        "   LLLLLLLLLLLLLL   ",
        "   LLLLLLLLLLLLLL   ",
        "    LLLLLLLLLLLL    ",
        "      LLLLLLLL      ",
        "        LLLL        ",

        "   LLLL      LLLL   ",
        "  LLLLLL    LLLLLL  ",
        " LLLLLLLLLLLLLLLLLL ",
        " LLLLLLLLLLLLLLLLLL ",
        " LLLLLLLCCCCLLLLLLLL ",
        " LLLLLLCCCCCCLLLLLL ",
        " LLLLLLCCCCCCLLLLLL ",
        " LLLLLLLCCCCLLLLLLLL ",

        " LLLLLLLLLLLLLLLLLL ",
        " LLLLLLLLLLLLLLLLLL ",
        "  LLLLLLLLLLLLLLLL  ",
        "   LLLLLLLLLLLLLL   ",
        "    LLLLLLLLLLLL    ",
        "      LLLLLLLL      ",
        "        LLLL        ",
        "        LLLL        ",

        // resto trasparente
    };

    //DISEGNO DEL FIORE
    for (unsigned int y = 0; y < 24; y++)
    {
        for (unsigned int x = 0; x < 24; x++)
        {
            if (flowerShape[y][x] == 'L')
            {
                flowerImage.setPixel(
                    {x, y},
                    flowerDark
                );
            }

            else if (flowerShape[y][x] == 'C')
            {
                flowerImage.setPixel(
                    {x,y},
                    flowerCenter
                );
            }
        }
    }

    //Texture del fiore
    sf::Texture flowerTexture(flowerImage);
    flowerTexture.setSmooth(false);

    //Sprite del fiore
    sf::Sprite obstacleFlower(flowerTexture);

    //Il centro del fiore saràil punto di origine
    obstacleFlower.setOrigin({12.f, 12.f});

    //Dimensione ENORME
    const float flowerBigScale = 40.f;

    //Il fiore parte invisibile 
    obstacleFlower.setScale({0.f, 0.f});

    //Posizione del fiore
    obstacleFlower.setPosition({400.f, 300.f});

    //GESTIONE APPARIZIONE FIORE
    //Timer che conta i secondi tra un apparizione e un altra
    float flowerTimer = 0.f;

    //Il fiore inizialmente non è visibile
    float flowerVisibleTime = 0.f;

    // Il fiore inizialmente NON è visibile
    bool flowerVisible = false;

    //Compare ogni 15 secondi
    const float flowerInterval = 15.f;

    //Rimane ENORME per 3 secondi
    const float flowerDuration = 3.f;;



   
    //Velocità iniziale della pallina
    float ballSpeedX = 120.f;
    float ballSpeedY = 120.f;

    const float speedIncreaseFactor = 1.05f;//+5% di velocità ad ogni collisione con un ostacolo
    const float maxBallSpeed = 500.f;//Tetto massimo, per non renderla ingiocabile
    
    sf::Clock clock;

    float playerSpeed = 300.f;

    //VELOCITA' OSTACOLI
    float obstacleSpeed = 80.f;
    float triangleSpeed = 100.f;
    float diamondSpeed = 90.f;
    float heartSpeed = 70.f;

    //Timer della parita
    sf::Clock gameClock;

    //Inizializzazione della casualità
    std::srand(static_cast<unsigned int>(std::time(nullptr)));

    //DIREZIONE CASUALE DEGLI OSTACOLI
    if (std::rand() % 2 == 0)
        obstacleSpeed = -obstacleSpeed;

    if (std::rand() % 2 == 0)
        triangleSpeed = -triangleSpeed;
    
    if (std::rand() % 2 == 0)
        diamondSpeed = -diamondSpeed;

    if (std::rand() % 2 == 0)
        heartSpeed = -heartSpeed;

    //Scelta casuale se partire da destra o da sinistra
    if (std::rand() % 2 == 0)
    {
        ballSpeedX = 120.f; 
    }
    else 
    {
        ballSpeedX = -120.f;
    }
    ballSpeedY = 120.f;//La pallina va sempre verso il basso
    
    sf::Font gameFont;
    if (!gameFont.openFromFile("pixel_font.ttf"))
    {
        std::cout << "ERRORE: Font non trovato!" << std::endl;
        return 1;
    }

    //STATO DEL GIOCO
    enum class Scene {Menu, Instruction, Playing};
    Scene currentScene = Scene::Menu;
    menuMusic.play();

    int menuSelection = 0;
    // 0 = Gioca
    // 1 = Come giocare
    // 2 = Esci
    bool musicEnabled = true;

    int score = 0;
    bool wasCollidingWithObstacle = false;
    bool wasCollidingWithTriangle = false;
    bool wasCollidingWithDiamond = false;
    bool wasCollidingWithHeart = false;

    bool scorePopupActive = false;
    float scorePopupTimer = 0.f;
    sf::Vector2f scorePopupPosition;

    float scorePopupScale = 0.f;
    const float scorePopupDuration = 0.45f;
    const float scorePopupStartScale = 2.5f;
    const float scorePopupEndScale = 1.f;

    //Testi del menu principale
    sf::Text menuTitleText(gameFont);
    menuTitleText.setString("LOVEBALL");
    menuTitleText.setCharacterSize(70);
    menuTitleText.setFillColor(sf::Color::White);
    menuTitleText.setPosition({230.f, 130.f});

    sf::Text menuPlayText(gameFont);
    menuPlayText.setString("PLAY");
    menuPlayText.setCharacterSize(30);
    menuPlayText.setPosition({350.f, 320.f});

    sf::Text menuInstructionsText(gameFont);
    menuInstructionsText.setString("HOW TO PLAY");
    menuInstructionsText.setCharacterSize(30);
    menuInstructionsText.setPosition({290.f, 380.f});

    sf::Text menuVolumeText(gameFont);
    menuVolumeText.setString("VOLUME: ON");
    menuVolumeText.setCharacterSize(30);
    menuVolumeText.setOrigin(menuVolumeText.getLocalBounds().getCenter());
    menuVolumeText.setPosition({400.f, 440.f});

    sf::Text menuExitText(gameFont);
    menuExitText.setString("EXIT THE GAME");
    menuExitText.setCharacterSize(30);
    menuExitText.setOrigin(menuExitText.getLocalBounds().getCenter());
    menuExitText.setPosition({400.f, 500.f});

    sf::Text menuHintText(gameFont);
    menuHintText.setString("Use UP/DOWN to select, ENTER to confirm");
    menuHintText.setCharacterSize(16);
    menuHintText.setFillColor(sf::Color(150, 150, 150));
    menuHintText.setPosition({150.f, 550.f});

    //TESTI DELLA SCHERMATA ISTRUZIONI
    sf::Text instructionsTitleText(gameFont);
    instructionsTitleText.setString("INSTRUCTIONS");
    instructionsTitleText.setCharacterSize(40);
    instructionsTitleText.setFillColor(sf::Color::White);
    instructionsTitleText.setPosition({230.f, 60.f});

    sf::Text instructionsBodyText(gameFont);
    instructionsBodyText.setString(
        "Use the LEFT ARROW and RIGHT ARROW\n"
        "to move the bar.\n\n"
        "Bounce the ball, don't drop it.\n"
        "Hit obstacles to increase\n"
        "its speed.\n\n"
        "Watch out for the flower: when it appears,\n"
        "the ball becomes invisible!\n\n"
        "Survive 60 seconds to win.\n\n"
        "Press ESC to return to the menu"
    );
    instructionsBodyText.setCharacterSize(22);
    instructionsBodyText.setFillColor(sf::Color::White);
    instructionsBodyText.setPosition({100.f, 150.f});

    sf::Text timerText(gameFont);

    sf::Text scoreText(gameFont);
    scoreText.setCharacterSize(30);
    scoreText.setString("SCORE: 0");
    scoreText.setOrigin(scoreText.getLocalBounds().getCenter());
    scoreText.setPosition({400.f, 30.f});

    sf::Text scorePopupText(gameFont);
    scorePopupText.setString("+100");
    scorePopupText.setCharacterSize(32);
    scorePopupText.setOrigin(scorePopupText.getLocalBounds().getCenter());
    scorePopupText.setFillColor(sf::Color::White);

    timerText.setCharacterSize(24);
    timerText.setFillColor(sf::Color::White);
    timerText.setPosition({700.f, 15.f});

    
    sf::Text gameOverText(gameFont);

    gameOverText.setString("GAME OVER");
    gameOverText.setCharacterSize(60);
    gameOverText.setFillColor(sf::Color::White);
    gameOverText.setPosition({230.f, 250.f});

    sf::Text gameWonText(gameFont);

    gameWonText.setString("YOU WIN!");
    gameWonText.setCharacterSize(60);
    gameWonText.setFillColor(sf::Color::White);
    gameWonText.setPosition({250.f, 250.f});
    
    sf::Text endScoreText(gameFont);
    endScoreText.setString("SCORE: ");
    endScoreText.setCharacterSize(60);
    endScoreText.setFillColor(sf::Color::White);
    endScoreText.setPosition({250.f, 400.f});


    //Sottotitolo con istruzioni, più piccolo
    sf::Text restartText(gameFont);
    restartText.setString("Press SPACE to play again");
    restartText.setCharacterSize(20);
    restartText.setFillColor(sf::Color::White);
    restartText.setPosition({260.f, 330.f});
    //Vite del player
    int lives = 3;

    //Controllo se la partita è terminata
    bool gameOver = false;
    bool gameWon = false;

    //Cuore pixel art
    sf::Image heartImage({16, 16}, sf::Color::Transparent);

    const char* heartShape[] =
    {
        "                ",
        "  WW    WW      ",
        " WWWW  WWWW     ",
        "WWWWWWWWWWWW     ",
        "WWWWWWWWWWWW     ",
        " WWWWWWWWWW      ",
        "  WWWWWWWW       ",
        "   WWWWWW        ",
        "    WWWW         ",
        "     WW          ",
        "                ",
        "                ",
        "                ",
        "                ",
        "                ",
        "                "
    };

    //Disegno cuore, pixel per pixel
    for (unsigned int y = 0; y < 16; y++)
    {
        for (unsigned int x = 0; x < 16; x++)
        {
            if (heartShape[y][x] == 'W')
            {
                heartImage.setPixel({x, y}, magenta);
            }
        }
    }
    //Texture del cuore
    sf::Texture heartTexture(heartImage);
    heartTexture.setSmooth(false);

    //Cuore scuro per le vite perse
    sf::Image darkHeartImage({16, 16}, sf::Color::Transparent);
    for (unsigned int y =0; y < 16; y++)
    {
        for (unsigned int x = 0; x <16; x++)
        {
            if (heartShape[y][x] == 'W')
            {
                darkHeartImage.setPixel({x, y}, sf::Color(60, 60, 60));
            }
        }
    }

    sf::Texture darkHeartTexture(darkHeartImage);
    darkHeartTexture.setSmooth(false);

    sf::Sprite heart1(heartTexture);
    sf::Sprite heart2(heartTexture);
    sf::Sprite heart3(heartTexture);

    heart1.setScale({2.f, 2.f});
    heart2.setScale({2.f, 2.f});
    heart3.setScale({2.f, 2.f});

    heart1.setPosition({20.f, 15.f});
    heart2.setPosition({55.f, 15.f});
    heart3.setPosition({90.f, 15.f});

    while (window.isOpen())//creato la finestra di gioco, messo in modalità loop dove leggo input, aggiorno il gioco , disegno il gioco e ripeto.

    {  
        while (const std::optional event = window.pollEvent())
        {
            //Chiudi la finestra
            if (event->is<sf::Event::Closed>())
            {
                window.close();
            }

            //Controllo se il tasto è stato premuto
            if (const auto* key = event->getIf<sf::Event::KeyPressed>())
            {
                //MENU PRINCIPALE
                if (currentScene == Scene::Menu)
                {
                    //Giù
                    if (key->code == sf::Keyboard::Key::Down)
                    {
                        menuSelection++;

                        //Se superiamo l'ultima voce, torniamo alla prima
                        if (menuSelection > 3)
                        {
                            menuSelection = 0;
                        }
                    }

                    //Sù
                    if (key->code == sf::Keyboard::Key::Up)
                    {
                        menuSelection--;

                        // Se andiamo sotto la prima voce, passiamo all'ultima
                        if (menuSelection < 0)
                        {
                            menuSelection = 3;
                        }
                    }

                    //Invio
                    if (key->code == sf::Keyboard::Key::Enter)
                    {
                        //GIOCA
                        if (menuSelection == 0)
                        {
                            //Ferma la musica del menu
                            menuMusic.stop();

                            //Avvia la musica della partita
                            gameMusic.play();

                            currentScene = Scene::Playing;
                            gameClock.restart();
                        }

                        //COME GIOCARE 
                        else if (menuSelection == 1)
                        {
                            currentScene = Scene::Instruction;
                        }

                        else if (menuSelection == 2)
                        {
                            // Attiva/disattiva il volume
                            if (musicEnabled)
                            {
                                //Disattiva la musica
                                musicEnabled = false;
                                menuVolumeText.setString("VOLUME: OFF");

                                menuMusic.setVolume(0.f);
                                gameMusic.setVolume(0.f);
                            }
                            else
                            {
                                //Riattiva la musica
                                musicEnabled = true;
                                menuVolumeText.setString("VOLUME: ON");

                                menuMusic.setVolume(40.f);
                                gameMusic.setVolume(50.f);
                            }
                        }

                        //ESCI DAL GIOCO
                        else if (menuSelection == 3)
                        {
                            window.close();
                        }
                    }
                }   

                //SCHERMATA ISTRUZIONI
                else if (currentScene == Scene::Instruction)
                {
                    //ESC = torna al menu
                    if (key->code == sf::Keyboard::Key::Escape)
                    {
                         // Ferma la musica della partita
                        gameMusic.stop();

                        // Torna alla musica del menu
                        menuMusic.play();

                        currentScene = Scene::Menu;
                    }
                }

                //DURANTE LA PARTITA
                else if (currentScene == Scene::Playing)
                {
                    // ESC = torna al menu principale
                    if (key->code == sf::Keyboard::Key::Escape)
                    {
                        currentScene = Scene::Menu;
                    }
                }
            }
        }

        float deltaTime = clock.restart().asSeconds();

        // ANIMAZIONE POP DEL +100
        if (scorePopupActive)
        {
            scorePopupTimer += deltaTime;

            float progress = scorePopupTimer / scorePopupDuration;

            if (progress >= 1.f)
            {
                scorePopupActive = false;
            }
            else
            {
                //Il +100 parte grande e torna rapidamente alla dimensione normale
                scorePopupScale =
                    scorePopupStartScale +
                    (scorePopupEndScale - scorePopupStartScale) * progress;

                scorePopupText.setScale({
                    scorePopupScale,
                    scorePopupScale
                });

                //Il +100 sale leggermente verso l'alto
                scorePopupText.setPosition({
                    scorePopupPosition.x,
                    scorePopupPosition.y - progress * 40.f
                });
            }
        }

        //Riavvio con barra spaziatrice
        if ((gameOver || gameWon) && sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Space))
        {
            //Reset vite
            lives = 3;
            score = 0;
            heart1.setTexture(heartTexture);
            heart2.setTexture(heartTexture);
            heart3.setTexture(heartTexture);

            //Reset stato partita
            gameOver = false;
            gameWon = false;

            //Reset posizione player e pallina
            player.setPosition({325.f, 550.f});
            ball.setPosition({384.f, 50.f});

            //Reset velocità pallina (direzione casuale come all'inizio)
            if (std::rand() % 2 == 0)
            {
                ballSpeedX = 120.f; 
            }
            else 
            {
                ballSpeedX = -120.f;
                ballSpeedY = 120.f;
            }

            //Reset ostacoli nelle posizioni iniiali 
            obstacle.setPosition({364.f, 110.f});
            obstacleTriangle.setPosition({130.f, 400.f});
            obstacleDiamond.setPosition({570.f, 400.f});
            obstacleHeart.setPosition({340.f, 250.f});


            //Reset velocità ostacoli con direzione casuale
            obstacleSpeed = 80.f;
            triangleSpeed = 100.f;
            diamondSpeed = 90.f;
            heartSpeed = 70.f;

            if (std::rand() % 2 == 0) obstacleSpeed = -obstacleSpeed;
            if (std::rand() % 2 == 0) triangleSpeed = -triangleSpeed;
            if (std::rand() % 2 == 0) diamondSpeed = -diamondSpeed;
            if (std::rand() % 2 == 0) heartSpeed = -heartSpeed;

            //Reset fiore
            flowerTimer = 0.f;
            flowerVisibleTime = 0.f;
            flowerVisible = false;
            obstacleFlower.setScale({0.f, 0.f});

            //Reset timer di gioco
            gameClock.restart();
        }
        
        if (currentScene == Scene::Playing && !gameOver && !gameWon)
        {   
            //FIORE MINACCIOSO
            flowerTimer += deltaTime;

            //Il fiore non è visibile
            if (!flowerVisible)
            {
                //Dopo 15 secondi, appare il fiore
                if (flowerTimer >= flowerInterval)
                {
                    //Azzera il timer
                    flowerTimer = 0.f;

                    //IL fiore diventa visibile
                    flowerVisible = true;

                    //Azzera il timer della sua permanenza
                    flowerVisibleTime = 0.f;

                    //APPARE ENORME
                    obstacleFlower.setScale({
                        flowerBigScale,
                        flowerBigScale
                    });
                }
            }

            //Il fiore è visibile
            if (flowerVisible)
            {
                //Contiamo quanto tempo è rimasto sullo schermo
                flowerVisibleTime += deltaTime;

                //Dopo 3 secondi
                if (flowerVisibleTime >= flowerDuration)
                {
                    //Il fiore scompare 
                    flowerVisible = false; 

                    //SCOMPARE DI BOTTO
                    obstacleFlower.setScale({0.f, 0.f});
                }
            }
            
        
            obstacle.move({obstacleSpeed * deltaTime, 0.f});
            obstacleTriangle.move({triangleSpeed * deltaTime, 0.f});
            obstacleDiamond.move({diamondSpeed * deltaTime, 0.f});
            obstacleHeart.move({heartSpeed * deltaTime, 0.f});

            // RETTANGOLO
            //Questi if servono per far tornare indietro dai bordi gli ostacoli, così che non possano uscire al di fuori di X (orizzontale). 
            //La Y (verticale) non è toccata.
            if (obstacle.getPosition().x <= 0.f)
            {
                obstacle.setPosition({0.f, obstacle.getPosition().y});
                obstacleSpeed = std::abs(obstacleSpeed);
            }
            else if (obstacle.getPosition().x + obstacle.getGlobalBounds().size.x >= 800.f)
            {
                obstacle.setPosition({
                    800.f - obstacle.getGlobalBounds().size.x,
                    obstacle.getPosition().y
                });

                obstacleSpeed = -std::abs(obstacleSpeed);
            }

            //TRIANGOLO
            if (obstacleTriangle.getPosition().x <= 0.f)
            {
                obstacleTriangle.setPosition({0.f, obstacleTriangle.getPosition().y});
                triangleSpeed = std::abs(triangleSpeed);
            }
            else if (obstacleTriangle.getPosition().x + obstacleTriangle.getGlobalBounds().size.x >= 800.f)
            {
                obstacleTriangle.setPosition({
                    800.f - obstacleTriangle.getGlobalBounds().size.x,
                    obstacleTriangle.getPosition().y
                });

                triangleSpeed = -std::abs(triangleSpeed);
            }
            
            //ROMBO
            if (obstacleDiamond.getPosition().x <= 0.f)
            {
                obstacleDiamond.setPosition({0.f, obstacleDiamond.getPosition().y});
                diamondSpeed = std::abs(diamondSpeed);
            }
            else if (obstacleDiamond.getPosition().x + obstacleDiamond.getGlobalBounds().size.x >= 800.f)
            {
                obstacleDiamond.setPosition({
                    800.f - obstacleDiamond.getGlobalBounds().size.x,
                    obstacleDiamond.getPosition().y
                });

                diamondSpeed = -std::abs(diamondSpeed);
            }

            // CUORE
            if (obstacleHeart.getPosition().x <= 0.f)
            {
                obstacleHeart.setPosition({0.f, obstacleHeart.getPosition().y});
                heartSpeed = std::abs(heartSpeed);
            }
            else if (obstacleHeart.getPosition().x + obstacleHeart.getGlobalBounds().size.x >= 800.f)
            {
                obstacleHeart.setPosition({
                    800.f - obstacleHeart.getGlobalBounds().size.x,
                    obstacleHeart.getPosition().y
                });

                heartSpeed = -std::abs(heartSpeed);
            }
            
            sf::Vector2f oldBallPosition = ball.getPosition();

            //Movimento della pallina
            //Detto al programma, sposta la pallina di una quantità determinata dalla sua velocità e del tempo trascorso.
            ball.move({
                ballSpeedX * deltaTime,//controlla il movimento destra/sinistra
                ballSpeedY * deltaTime//controlla il movimento su/giù
            });

            //Muri dello schermo
            //Larghezza e altezza della finestra
            float windowWidth = 800.f;
            float windowHeight = 600.f;

            //Pallina caduta sotto lo schermo
            if (ball.getPosition().y > windowHeight)
            {
                lives--;

                //Rimettere pallina in alto
                ball.setPosition({384.f, 50.f});

                //Riportare la barra al suo posto dopo aver perso una vita.
                player.setPosition({325.f, 550.f});

                //Ripristino velocità
                ballSpeedY = 120.f;

                if (lives == 2)
                {
                    heart3.setTexture(darkHeartTexture);
                }
                else if (lives == 1)
                {
                    heart2.setTexture(darkHeartTexture);
                }
                else if (lives == 0)
                {
                    heart1.setTexture(darkHeartTexture);
                }
                //GAME OVER
                if (lives <= 0)
                {
                    gameOver = true;
                }
            }

            //Dimensione della pallina
            float ballWidth = ball.getGlobalBounds().size.x;
            float ballHeight = ball.getGlobalBounds().size.y;

            //Muro sinistro
            //Se la pallina arriva a x = 0, facciamo cambiare direzione alla velocità orizzontale.
            if (ball.getPosition().x <= 0)
            {
                ball.setPosition({
                    0.f,
                    ball.getPosition().y
                });
                
                ballSpeedX = -ballSpeedX;

                currentSpeed = std::sqrt(ballSpeedX * ballSpeedX + ballSpeedY * ballSpeedY);
                if (currentSpeed < maxBallSpeed)
                {
                    ballSpeedX *= speedIncreaseFactor;
                    ballSpeedY *= speedIncreaseFactor;
                }

            }

            //Muro destro
            //Se la pallina arriva al bordo destro, torna indietro.
            if (ball.getPosition().x + ballWidth >= windowWidth)
            {
                ball.setPosition({
                    windowWidth - ballWidth,
                    ball.getPosition().y
                });

                ballSpeedX = -ballSpeedX;
                
                currentSpeed = std::sqrt(ballSpeedX * ballSpeedX + ballSpeedY * ballSpeedY);
                if (currentSpeed < maxBallSpeed)
                {
                    ballSpeedX *= speedIncreaseFactor;
                    ballSpeedY *= speedIncreaseFactor;
                }
            }

            //Muro superiore
            //Se la pallina arriva in alto, rimbalza verso il basso.
            if (ball.getPosition().y <= 0)
            {
                ball.setPosition({
                    ball.getPosition().x,
                    0.f
                });

                ballSpeedY = -ballSpeedY;
                
                currentSpeed = std::sqrt(ballSpeedX * ballSpeedX + ballSpeedY * ballSpeedY);
                if (currentSpeed < maxBallSpeed)
                {
                    ballSpeedX *= speedIncreaseFactor;
                    ballSpeedY *= speedIncreaseFactor;
                }
            }

            //Collisione pallina barra
            if (ball.getGlobalBounds().findIntersection(player.getGlobalBounds()))
            {   
                ballSpeedY = -std::abs(ballSpeedY);//rimbalza sempre verso l'alto

                ball.setPosition({
                    ball.getPosition().x,
                    player.getPosition().y - ball.getGlobalBounds().size.y
                });

            //Calcolo di dove la pallina ha colpito la barra, rispetto al centro
            float playerCenterX = player.getPosition().x + 
                player.getGlobalBounds().size.x / 2.f;
            float ballCenterX = ball.getPosition().x +
                ball.getGlobalBounds().size.x / 2.f;
            
            //HitOffset va da -1 (bordo sinistro) a +1 (bordo destro), 0 = centro
            float hitOffset = (ballCenterX - playerCenterX) /
                (player.getGlobalBounds().size.x / 2.f);
            
            //Applicazione offset alla velocità orizzontale 
            float speedMagnitude = std::sqrt(ballSpeedX * ballSpeedX + ballSpeedY * ballSpeedY);
            ballSpeedX = hitOffset * speedMagnitude * 0.8f;//0.8 = quanto è aggressivo l'angolo massimo
            ballSpeedY = -std::sqrt(std::max(0.f, speedMagnitude * speedMagnitude
                - ballSpeedX * ballSpeedX));
            }

            //COLLISIONE CON RETTANGOLO
            if (ball.getGlobalBounds().findIntersection(obstacle.getGlobalBounds()))
            {   
                if (!wasCollidingWithObstacle)
                {
                    score += 100;
                    wasCollidingWithObstacle = true;

                    //ATTIVA IL POP DEL +100
                    scorePopupActive = true;
                    scorePopupTimer = 0.f;
                    scorePopupScale = scorePopupStartScale;

                    //Il popup nasce vicino alla pallina
                    scorePopupPosition = ball.getPosition();

                    scorePopupText.setScale({
                        scorePopupScale,
                        scorePopupScale
                    });

                    scorePopupText.setPosition(scorePopupPosition);
                }                

                float obstacleLeft   = obstacle.getPosition().x;
                float obstacleRight  = obstacleLeft + obstacle.getGlobalBounds().size.x;
                float obstacleTop    = obstacle.getPosition().y;
                float obstacleBottom = obstacleTop + obstacle.getGlobalBounds().size.y;

                float ballLeft   = ball.getPosition().x;
                float ballTop    = ball.getPosition().y;
                float ballWidth  = ball.getGlobalBounds().size.x;
                float ballHeight = ball.getGlobalBounds().size.y;
                float ballRight  = ballLeft + ballWidth;
                float ballBottom = ballTop + ballHeight;

                //Quanto la pallina si è "infilata" dentro l'ostacolo su ogni lato
                float overlapLeft   = ballRight - obstacleLeft;//Quanto sporge oltre il bordo sinistro dell'ostacolo
                float overlapRight  = obstacleRight - ballLeft;//Quanto sporge oltre il bordo destro
                float overlapTop    = ballBottom - obstacleTop;//Quanto sporge sopra
                float overlapBottom = obstacleBottom - ballTop;//Quanto sporge sotto.

                //Il più piccolo overlap indica da che lato è entrata la pallina
                float minOverlapX = std::min(overlapLeft, overlapRight);
                float minOverlapY = std::min(overlapTop, overlapBottom);

                if (minOverlapX < minOverlapY)
                {
                    //Collisione orizzontale
                    if (overlapLeft < overlapRight)//La via d'uscita più breve è orizzontale -> rimbalzo su x
                    {
                        ball.setPosition({obstacleLeft - ballWidth, ballTop});
                        ballSpeedX = -std::abs(ballSpeedX);
                    }
                    else//La via d'uscita è verticale -> rimbalzo su y
                    {
                        ball.setPosition({obstacleRight, ballTop});
                        ballSpeedX = std::abs(ballSpeedX);
                    }
                }
                else
                {
                    //Collisione verticale
                    if (overlapTop < overlapBottom)
                    {
                        ball.setPosition({ballLeft, obstacleTop - ballHeight});
                        ballSpeedY = -std::abs(ballSpeedY);
                    }
                    else
                    {
                        ball.setPosition({ballLeft, obstacleBottom});
                        ballSpeedY = std::abs(ballSpeedY);
                    }
                }

                currentSpeed = std::sqrt(ballSpeedX * ballSpeedX + ballSpeedY * ballSpeedY);
                if (currentSpeed < maxBallSpeed)
                {
                    ballSpeedX *= speedIncreaseFactor;
                    ballSpeedY *= speedIncreaseFactor;
                }
        
            }
            else
            {
                //La pallina non tocca più il rettangolo
                wasCollidingWithObstacle = false;
            }
    
            //COLLISIONE CON IL TRIANGOLO
            if (pixelPerfectCollision(ball, ballImage, obstacleTriangle, obstacleTriangleImage))
            {   
                if (!wasCollidingWithTriangle)
                {
                    score += 100;
                    wasCollidingWithTriangle = true;

                    //ATTIVA IL POP DEL +100
                    scorePopupActive = true;
                    scorePopupTimer = 0.f;
                    scorePopupScale = scorePopupStartScale;

                    scorePopupPosition = ball.getPosition();

                    scorePopupText.setScale({
                        scorePopupScale,
                        scorePopupScale
                    });

                    scorePopupText.setPosition(scorePopupPosition);
                }

                float obstacleLeft = obstacleTriangle.getPosition().x;
                float obstacleRight = obstacleLeft + obstacleTriangle.getGlobalBounds().size.x;
                float obstacleTop    = obstacleTriangle.getPosition().y;
                float obstacleBottom = obstacleTop + obstacleTriangle.getGlobalBounds().size.y;

                float ballLeft   = ball.getPosition().x;
                float ballTop    = ball.getPosition().y;
                float ballWidth  = ball.getGlobalBounds().size.x;
                float ballHeight = ball.getGlobalBounds().size.y;
                float ballRight  = ballLeft + ballWidth;
                float ballBottom = ballTop + ballHeight;

                float overlapLeft   = ballRight - obstacleLeft;
                float overlapRight  = obstacleRight - ballLeft;
                float overlapTop    = ballBottom - obstacleTop;
                float overlapBottom = obstacleBottom - ballTop;

                float minOverlapX = std::min(overlapLeft, overlapRight);
                float minOverlapY = std::min(overlapTop, overlapBottom);
                
                if (minOverlapX < minOverlapY)
                {
                    if (overlapLeft < overlapRight)
                    {
                        ball.setPosition({obstacleLeft - ballWidth, ballTop});
                        ballSpeedX = -std::abs(ballSpeedX);
                    }
                    else
                    {
                        ball.setPosition({obstacleRight, ballTop});
                        ballSpeedX = std::abs(ballSpeedX);
                    }
                }
                else
                {
                    if (overlapTop < overlapBottom)
                    {
                        ball.setPosition({ballLeft, obstacleTop - ballHeight});
                        ballSpeedY = -std::abs(ballSpeedY);
                    }
                    else
                    {
                        ball.setPosition({ballLeft, obstacleBottom});
                        ballSpeedY = std::abs(ballSpeedY);
                    }
                }
            
                currentSpeed = std::sqrt(ballSpeedX * ballSpeedX + ballSpeedY * ballSpeedY);
                if (currentSpeed < maxBallSpeed)
                {
                    ballSpeedX *= speedIncreaseFactor;
                    ballSpeedY *= speedIncreaseFactor;
                }

            }
            else
            {
                //La pallina non tocca più il Triangolo
                wasCollidingWithTriangle = false;
            }
    

            //COLLISIONE CON IL ROMBO
            if (pixelPerfectCollision(ball, ballImage, obstacleDiamond, obstacleDiamondImage))
            {   
                if (!wasCollidingWithDiamond)
                {
                    score += 100;
                    wasCollidingWithDiamond = true;

                    //ATTIVA IL POP DEL +100
                    scorePopupActive = true;
                    scorePopupTimer = 0.f;
                    scorePopupScale = scorePopupStartScale;

                    scorePopupPosition = ball.getPosition();

                    scorePopupText.setScale({
                        scorePopupScale,
                        scorePopupScale
                    });

                    scorePopupText.setPosition(scorePopupPosition);
                }

                float obstacleLeft   = obstacleDiamond.getPosition().x;
                float obstacleRight  = obstacleLeft + obstacleDiamond.getGlobalBounds().size.x;
                float obstacleTop    = obstacleDiamond.getPosition().y;
                float obstacleBottom = obstacleTop + obstacleDiamond.getGlobalBounds().size.y;

                float ballLeft   = ball.getPosition().x;
                float ballTop    = ball.getPosition().y;
                float ballWidth  = ball.getGlobalBounds().size.x;
                float ballHeight = ball.getGlobalBounds().size.y;
                float ballRight  = ballLeft + ballWidth;
                float ballBottom = ballTop + ballHeight;

                float overlapLeft   = ballRight - obstacleLeft;
                float overlapRight  = obstacleRight - ballLeft;
                float overlapTop    = ballBottom - obstacleTop;
                float overlapBottom = obstacleBottom - ballTop;

                float minOverlapX = std::min(overlapLeft, overlapRight);
                float minOverlapY = std::min(overlapTop, overlapBottom);

                if (minOverlapX < minOverlapY)
                {
                    if (overlapLeft < overlapRight)
                    {
                        ball.setPosition({obstacleLeft - ballWidth, ballTop});
                        ballSpeedX = -std::abs(ballSpeedX);
                    }
                    else
                    {
                        ball.setPosition({obstacleRight, ballTop});
                        ballSpeedX = std::abs(ballSpeedX);
                    }
                }
                else
                {
                    if (overlapTop < overlapBottom)
                    {
                        ball.setPosition({ballLeft, obstacleTop - ballHeight});
                        ballSpeedY = -std::abs(ballSpeedY);
                    }
                    else
                    {
                        ball.setPosition({ballLeft, obstacleBottom});
                        ballSpeedY = std::abs(ballSpeedY);
                    }
                }

                currentSpeed = std::sqrt(ballSpeedX * ballSpeedX + ballSpeedY * ballSpeedY);
                if (currentSpeed < maxBallSpeed)
                {
                    ballSpeedX *= speedIncreaseFactor;
                    ballSpeedY *= speedIncreaseFactor;
                }
                if (currentSpeed < maxBallSpeed)
                {
                    ballSpeedX *= speedIncreaseFactor;
                    ballSpeedY *= speedIncreaseFactor;
                }

            }
            else
            {
                //La pallina non tocca più il rettangolo
                wasCollidingWithDiamond = false;
            }

            //COLLISIONE CON IL CUORE GRANDE
            if (pixelPerfectCollision(ball, ballImage, obstacleHeart, obstacleHeartImage))
            {   
                if (!wasCollidingWithHeart)
                {
                    score += 100;
                    wasCollidingWithHeart = true;

                    //ATTIVA IL POP DEL +100
                    scorePopupActive = true;
                    scorePopupTimer = 0.f;
                    scorePopupScale = scorePopupStartScale;

                    scorePopupPosition = ball.getPosition();

                    scorePopupText.setScale({
                        scorePopupScale,
                        scorePopupScale
                    });

                    scorePopupText.setPosition(scorePopupPosition);
                }

                float obstacleLeft   = obstacleHeart.getPosition().x;
                float obstacleRight  = obstacleLeft + obstacleHeart.getGlobalBounds().size.x;
                float obstacleTop    = obstacleHeart.getPosition().y;
                float obstacleBottom = obstacleTop + obstacleHeart.getGlobalBounds().size.y;

                float ballLeft   = ball.getPosition().x;
                float ballTop    = ball.getPosition().y;
                float ballWidth  = ball.getGlobalBounds().size.x;
                float ballHeight = ball.getGlobalBounds().size.y;
                float ballRight  = ballLeft + ballWidth;
                float ballBottom = ballTop + ballHeight;

                float overlapLeft   = ballRight - obstacleLeft;
                float overlapRight  = obstacleRight - ballLeft;
                float overlapTop    = ballBottom - obstacleTop;
                float overlapBottom = obstacleBottom - ballTop;

                float minOverlapX = std::min(overlapLeft, overlapRight);
                float minOverlapY = std::min(overlapTop, overlapBottom);

                if (minOverlapX < minOverlapY)
                {
                    if (overlapLeft < overlapRight)
                    {
                        ball.setPosition({obstacleLeft - ballWidth, ballTop});
                        ballSpeedX = -std::abs(ballSpeedX);
                    }
                    else
                    {
                        ball.setPosition({obstacleRight, ballTop});
                        ballSpeedX = std::abs(ballSpeedX);
                    }
                }
                else
                {
                    if (overlapTop < overlapBottom)
                    {
                        ball.setPosition({ballLeft, obstacleTop - ballHeight});
                        ballSpeedY = -std::abs(ballSpeedY);
                    }
                    else
                    {
                        ball.setPosition({ballLeft, obstacleBottom});
                        ballSpeedY = std::abs(ballSpeedY);
                    }
                }

                currentSpeed = std::sqrt(ballSpeedX * ballSpeedX + ballSpeedY * ballSpeedY);
                if (currentSpeed < maxBallSpeed)
                {
                    ballSpeedX *= speedIncreaseFactor;
                    ballSpeedY *= speedIncreaseFactor;
                }
                if (currentSpeed < maxBallSpeed)
                {
                    ballSpeedX *= speedIncreaseFactor;
                    ballSpeedY *= speedIncreaseFactor;
                }

            }
            else
            {
                //La pallina non tocca più il rettangolo
                wasCollidingWithHeart = false;
            }
            
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Left))
            {
                if (player.getPosition().x > 0)
                {
                    player.move({-playerSpeed * deltaTime, 0.f});
                }
            }

            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Right))
            {
                if (player.getPosition().x < 650)
                {
                    player.move({playerSpeed * deltaTime, 0.f});
                }
            }
            
            int seconds = static_cast<int>(
                gameClock.getElapsedTime().asSeconds()
            );

            if (seconds >= 60)
            {
                gameWon = true;
            }

            timerText.setString(std::to_string(seconds) + "s.");

            scoreText.setString("PUNTI: " + std::to_string(score));
            scoreText.setOrigin(scoreText.getLocalBounds().getCenter());
            scoreText.setPosition({400.f, 30.f});

        }
        window.clear(sf::Color::Black);

        //MENU PRINCIPALE
        if (currentScene == Scene::Menu)
        {
            //La voce selezionata diventa bianca, l'altra grigia
            if (menuSelection == 0)
            {
                menuPlayText.setFillColor(sf::Color::White);
                menuInstructionsText.setFillColor(sf::Color(100, 100, 100));
                menuVolumeText.setFillColor(sf::Color(100, 100, 100));
                menuExitText.setFillColor(sf::Color(100, 100, 100));
            }
            else if (menuSelection == 1)
            {
                menuPlayText.setFillColor(sf::Color(100, 100, 100));
                menuInstructionsText.setFillColor(sf::Color::White);
                menuVolumeText.setFillColor(sf::Color(100, 100, 100));
                menuExitText.setFillColor(sf::Color(100, 100, 100));
            }
            else if (menuSelection == 2)
            {
                menuPlayText.setFillColor(sf::Color(100, 100, 100));
                menuInstructionsText.setFillColor(sf::Color(100, 100, 100));
                menuVolumeText.setFillColor(sf::Color::White);
                menuExitText.setFillColor(sf::Color(100, 100, 100));
            }
            else
            {
                menuPlayText.setFillColor(sf::Color(100, 100, 100));
                menuInstructionsText.setFillColor(sf::Color(100, 100, 100));
                menuVolumeText.setFillColor(sf::Color(100, 100, 100));
                menuExitText.setFillColor(sf::Color::White);
            }
            //DISEGNO DEL MENU
            window.draw(menuTitleText);
            window.draw(menuPlayText);
            window.draw(menuInstructionsText);
            window.draw(menuVolumeText);
            window.draw(menuExitText);
            window.draw(menuHintText);
            
        }

        //SCERMATA ISTRUZIONI
        else if (currentScene == Scene::Instruction)
        {
            window.draw(instructionsTitleText);
            window.draw(instructionsBodyText);
        }

        //GIOCO
        else if (currentScene == Scene::Playing)
        {
            // GAME OVER
            if (gameOver)
            {
                window.draw(gameOverText);
                window.draw(restartText);
            }

            //VITTORIA
            else if (gameWon)
            {
                window.draw(gameWonText);
                window.draw(restartText);
                endScoreText.setString("SCORE: " + std::to_string(score));
                window.draw(endScoreText);
            }

            //PARTITA IN CORSO
            else
            {
                window.draw(timerText);
                window.draw(scoreText);

                if (scorePopupActive)
                {
                    window.draw(scorePopupText);
                }

                window.draw(heart1);
                window.draw(heart2);
                window.draw(heart3);

                window.draw(obstacle);
                window.draw(obstacleTriangle);
                window.draw(obstacleDiamond);
                window.draw(obstacleHeart);
                window.draw(obstacleFlower);

                window.draw(player);

                //Quando compare il fiore, la pallina diventa invisibile
                if (!flowerVisible)
                {
                    window.draw(ball);
                }
            }
        }
        window.display();
    }
    return 0;
}
           