#ifndef HANOI_SIM_H
#define HANOI_SIM_H

#include "raylib.h"
#include <stdbool.h>

#define MAX_DISCHI 8

// Struttura interna per gestire lo stato della simulazione
typedef struct {
    int pioli[3][MAX_DISCHI];       // CORRETTO: Matrice per i 3 pioli
    int conteggio[3];               // CORRETTO: Array per il numero di dischi su ciascuno dei 3 pioli
    int totaleDischi;
    bool erroreRegole;
    const char* messaggioErrore;
} HanoiSim;

// Variabile globale statica per mantenere lo stato nascosto all'utente
static HanoiSim sim;

// Inizializza la finestra grafica e i dischi sul primo piolo
void InizializzaSimulazione(int numDischi) {
    if (numDischi > MAX_DISCHI) numDischi = MAX_DISCHI;
    if (numDischi < 1) numDischi = 1;

    sim.totaleDischi = numDischi;
    sim.erroreRegole = false;
    sim.messaggioErrore = "";
    
    // Inizializza i conteggi dei tre pioli
    sim.conteggio[0] = numDischi;
    sim.conteggio[1] = 0;
    sim.conteggio[2] = 0;
    
    // Riempie il primo piolo (indice 0) con i dischi dal più grande al più piccolo
    for (int i = 0; i < numDischi; i++) {
        sim.pioli[0][i] = numDischi - i;
    }

// --- AGGIUNGI QUESTA RIGA QUI ------------------------------------------
    // Disabilita completamente i messaggi di testo iniziali di Raylib
    SetTraceLogLevel(LOG_NONE); 
    // -----------------------------------------------------------------------

    // Inizializza la finestra bloccata a 60 FPS fluidi
    InitWindow(800, 450, "Esercizio: Torre di Hanoi");
    SetTargetFPS(60);
}

// Funzione di disegno interna (Resta pura, non chiama Begin/End Drawing)
static void DisegnaElementiScena(int discoInMovimento, float movX, float movY) {
    float posizioniX[] = { 200.0f, 400.0f, 600.0f };
    float basePegY = 350.0f;
    float altezzaDisco = 25.0f;
    float larghezzaUnita = 20.0f;
    
    // Dichiarazione e assegnazione separata dei colori
    Color colori[8];
    colori[0] = GetColor(0xe12a2aff); // Rosso
    colori[1] = GetColor(0xff8100ff); // Arancione
    colori[2] = GetColor(0xffd300ff); // Giallo
    colori[3] = GetColor(0x27ae60ff); // Verde
    colori[4] = GetColor(0x2980b9ff); // Blu
    colori[5] = GetColor(0x8e44adff); // Viola
    colori[6] = GetColor(0xf1c40fff); // Giallo chiaro
    colori[7] = GetColor(0x2c3e50ff); // Blu scuro

    ClearBackground(RAYWHITE);

    if (sim.erroreRegole) {
        DrawText(sim.messaggioErrore, 20, 20, 20, RED);
    }

    // Base e pioli
    DrawRectangle(50, (int)basePegY, 700, 20, BROWN);
    for (int i = 0; i < 3; i++) {
        DrawRectangle((int)posizioniX[i] - 5, (int)basePegY - 220, 10, 220, GRAY);
    }

    // Dischi fermi
    for (int p = 0; p < 3; p++) {
        for (int d = 0; d < sim.conteggio[p]; d++) {
            int taglia = sim.pioli[p][d];
            float larghezza = taglia * larghezzaUnita + 30;
            float x = posizioniX[p] - (larghezza / 2);
            float y = basePegY - ((d + 1) * altezzaDisco);

            DrawRectangleRounded((Rectangle){x, y, larghezza, altezzaDisco - 2}, 0.3f, 4, colori[taglia % 8]);
            DrawRectangleLinesEx((Rectangle){x, y, larghezza, altezzaDisco - 2}, 1, BLACK);
        }
    }

    // Disco in movimento animato
    if (discoInMovimento > 0) {
        float larghezza = discoInMovimento * larghezzaUnita + 30;
        float x = movX - (larghezza / 2);
        float y = movY;
        DrawRectangleRounded((Rectangle){x, y, larghezza, altezzaDisco - 2}, 0.3f, 4, colori[discoInMovimento % 8]);
        DrawRectangleLinesEx((Rectangle){x, y, larghezza, altezzaDisco - 2}, 1, BLACK);
    }
}

// Chiude la finestra di simulazione alla fine
void ChiudiSimulazione(void) {
    while (!WindowShouldClose()) {
        BeginDrawing();
        DisegnaElementiScena(0, 0, 0);
        EndDrawing();
    }
    CloseWindow();
}

/**
 * Muove un disco rispettando le regole con un'animazione a tempo costante e fluida.
 */
void MuoviDisco(int da, int a, int velocita) {
    if (sim.erroreRegole || WindowShouldClose()) return;

    if (velocita < 1) velocita = 1;
    if (velocita > 100) velocita = 100;

    int idxDa = da - 1;
    int idxA = a - 1;

    // Controlli didattici di sicurezza
    if (idxDa < 0 || idxDa > 2 || idxA < 0 || idxA > 2) {
        sim.erroreRegole = true;
        sim.messaggioErrore = "ERRORE: Indici dei pioli non validi! Usa valori da 1 a 3.";
        while (!WindowShouldClose()) { BeginDrawing(); DisegnaElementiScena(0, 0, 0); EndDrawing(); }
        return;
    }
    if (sim.conteggio[idxDa] <= 0) {
        sim.erroreRegole = true;
        sim.messaggioErrore = "ERRORE: Tentativo di muovere un disco da un piolo vuoto!";
        while (!WindowShouldClose()) { BeginDrawing(); DisegnaElementiScena(0, 0, 0); EndDrawing(); }
        return;
    }
    int tagliaDiscoDaMuovere = sim.pioli[idxDa][sim.conteggio[idxDa] - 1];
    if (sim.conteggio[idxA] > 0) {
        int tagliaDiscoDestinazione = sim.pioli[idxA][sim.conteggio[idxA] - 1];
        if (tagliaDiscoDaMuovere > tagliaDiscoDestinazione) {
            sim.erroreRegole = true;
            sim.messaggioErrore = "ERRORE: Impossibile mettere un disco piu' grande sopra uno piu' piccolo!";
            while (!WindowShouldClose()) { BeginDrawing(); DisegnaElementiScena(0, 0, 0); EndDrawing(); }
            return;
        }
    }

    // Estrazione logica temporanea per l'effetto visivo
    sim.conteggio[idxDa]--;

    // Configurazione geometrica
    float posizioniX[] = { 200.0f, 400.0f, 600.0f };
    float basePegY = 350.0f;
    float altezzaDisco = 25.0f;
    float altezzaMassimaSollevamento = basePegY - 240.0f;

    float startX = posizioniX[idxDa];
    float startY = basePegY - ((sim.conteggio[idxDa] + 1) * altezzaDisco);
    float endX = posizioniX[idxA];
    float endY = basePegY - ((sim.conteggio[idxA] + 1) * altezzaDisco);

    // Se la velocità è al massimo, esegui all'istante senza frame intermedi
    if (velocita >= 98) {
        sim.pioli[idxA][sim.conteggio[idxA]] = tagliaDiscoDaMuovere;
        sim.conteggio[idxA]++;
        BeginDrawing();
        DisegnaElementiScena(0, 0, 0);
        EndDrawing();
        return;
    }

    // Calcolo del tempo totale del movimento (da 1.2 secondi a 0.05 secondi)
    float tempoTotaleAnimazione = 1.2f * (1.0f - (float)(velocita - 1) / 97.0f);
    if (tempoTotaleAnimazione < 0.05f) tempoTotaleAnimazione = 0.05f;

    float tempoTrascorso = 0.0f;
    float curX = startX;
    float curY = startY;

    // UNICO LOOP DI ANIMAZIONE SINCRONIZZATO CON IL MONITOR (60 FPS)
    while (tempoTrascorso < tempoTotaleAnimazione && !WindowShouldClose()) {
        tempoTrascorso += GetFrameTime();
        if (tempoTrascorso > tempoTotaleAnimazione) tempoTrascorso = tempoTotaleAnimazione;

        // Frazione globale del tempo (va da 0.0 a 1.0)
        float p = tempoTrascorso / tempoTotaleAnimazione;

        // Suddividiamo il tempo totale equamente in 3 sotto-fasi (0.33 ciascuna)
        if (p <= 0.333f) {
            // Fase 1: Salita
            float tFase = p / 0.333f;
            curX = startX;
            curY = startY + (altezzaMassimaSollevamento - startY) * tFase;
        } 
        else if (p <= 0.666f) {
            // Fase 2: Spostamento orizzontale
            float tFase = (p - 0.333f) / 0.333f;
            curX = startX + (endX - startX) * tFase;
            curY = altezzaMassimaSollevamento;
        } 
        else {
            // Fase 3: Discesa
            float tFase = (p - 0.666f) / 0.334f;
            curX = endX;
            curY = altezzaMassimaSollevamento + (endY - altezzaMassimaSollevamento) * tFase;
        }

        // Singolo rendering a frame, fluido e privo di lag
        BeginDrawing();
        DisegnaElementiScena(tagliaDiscoDaMuovere, curX, curY);
        EndDrawing();
    }

    // Inserimento definitivo
    sim.pioli[idxA][sim.conteggio[idxA]] = tagliaDiscoDaMuovere;
    sim.conteggio[idxA]++;
}

#endif // HANOI_SIM_H
