#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <FastLED.h>

#define NUM_COLS 64 // 8x32 pixels
#define NUM_ROWS 8
#define NUM_LEDS 512 // 8x32 pixels
#define DATA_PIN 5   // Pin de l'ESP32 connecté aux LEDs

const char *ssid = "CHAPEAU_MAGIQUE";
const char *password = "12345678";

const String htmlPage = R"rawliteral(
<!DOCTYPE HTML>
<html>

<head>
    <title>LED Matrix control</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            background-color: #f0f0f0;
            color: #333;
            margin-top: 50px;
        }

        h2 {
            color: #444;
        }

        form {
            display: inline-block;
            margin-top: 20px;
        }

        input[type="text"] {
            width: 80%;
            padding: 12px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 16px;
        }

        input[type="submit"] {
            width: 85%;
            padding: 12px;
            background-color: #4CAF50;
            border: none;
            color: white;
            border-radius: 4px;
            font-size: 16px;
            cursor: pointer;
        }

        input[type="submit"]:hover {
            background-color: #45a049;
        }

        .slider {
            width: 85%;
            margin-top: 15px;
        }

        .radio-group {
            display: flex;
            gap: 15px;
            /* Espace entre les boutons radio */
        }

        .radio-group label {
            display: flex;
            align-items: center;
            cursor: pointer;
        }
    </style>
</head>

<body>
    <h2>Magic Hat Control</h2>
    <form action="/sendText" method="POST">
        <label for="sens">Hat way :</label><br><br>
        <div>
            <div class="radio-group">
                <label for="normal">
                    <input type="radio" id="normal" name="sens" value="0" checked>
                    Normal
                </label>
                <label for="reverse">
                    <input type="radio" id="reverse" name="sens" value="1">
                    Reverse
                </label>
            </div>
        </div><br>
        <label>Animation choice :</label><br><br>
        <div>
            <div class="radio-group">
                <label for="lettre">
                    <input type="radio" id="lettre" name="animation" value="lettre" checked>
                    Text
                </label>
                <label for="gouttes">
                    <input type="radio" id="gouttes" name="animation" value="gouttes">
                    Drops
                </label>
                <label for="coeur">
                    <input type="radio" id="coeur" name="animation" value="coeur">
                    Hearts
                </label>
                <label for="invaders">
                    <input type="radio" id="invaders" name="animation" value="invaders">
                    Invaders
                </label>
            </div>
            <div class="radio-group">
                <label for="life">
                    <input type="radio" id="life" name="animation" value="life">
                    Game of life
                </label>
                <label for="eclair">
                    <input type="radio" id="eclair" name="animation" value="eclair">
                    lightning
                </label>
                <label for="spider">
                    <input type="radio" id="spider" name="animation" value="spider">
                    Spider (none)
                </label>
            </div>
        </div>
        <br>
        <div class="slider-label">Speed selection : <span id="speedValue">100</span> ms</div>
        <input type="range" min="50" max="500" value="100" class="slider" name="speed" id="speedRange">
        <br>
        <input type="text" name="text" placeholder="Enter your text">
        <br>
        <div class="slider-label">Color selection : </div>
        <input type="range" min="0" max="5" value="0" class="slider" name="color" id="colorRange">
        <input type="hidden" name="colorValue" id="colorValue" value="0xF0F8FF">
        <br>
        <div id="colorTest" style="background-color: #FFFFFF;" value="0x8B0000"> 0 </div>
        <br>
        <input type="submit" value="Send">
    </form>

    <script>
        const color = [
            0xFFFFFF, 0x8B0000, 0xED8F11, 0x68ED11, 0x800080, 0x00008B
        ]

        var slider = document.getElementById("speedRange");
        var output = document.getElementById("speedValue");
        slider.oninput = function () {
            output.innerHTML = this.value;
        }
        var sliderColor = document.getElementById("colorRange");
        var colorTest = document.getElementById("colorTest");
        var colorValue = document.getElementById("colorValue");
        sliderColor.oninput = function () {
            colorTest.innerText = this.value;
            colorTest.style.backgroundColor = '#' + color[this.value].toString(16).padStart(6, '0').toUpperCase();
            colorValue.value = color[this.value];
        }
    </script>
</body>

</html>
)rawliteral";

const uint8_t font5x7[][5] = {
    // 0x20 - 0x2F : Caractères spéciaux
    {0x00, 0x00, 0x00, 0x00, 0x00}, // (espace)
    {0x00, 0x00, 0x5F, 0x00, 0x00}, // !
    {0x00, 0x07, 0x00, 0x07, 0x00}, // "
    {0x14, 0x7F, 0x14, 0x7F, 0x14}, // #
    {0x24, 0x2A, 0x7F, 0x2A, 0x12}, // $
    {0x23, 0x13, 0x08, 0x64, 0x62}, // %
    {0x36, 0x49, 0x55, 0x22, 0x50}, // &
    {0x00, 0x05, 0x03, 0x00, 0x00}, // '
    {0x00, 0x1C, 0x22, 0x41, 0x00}, // (
    {0x00, 0x41, 0x22, 0x1C, 0x00}, // )
    {0x14, 0x08, 0x3E, 0x08, 0x14}, // *
    {0x08, 0x08, 0x3E, 0x08, 0x08}, // +
    {0x00, 0x50, 0x30, 0x00, 0x00}, // ,
    {0x08, 0x08, 0x08, 0x08, 0x08}, // -
    {0x00, 0x60, 0x60, 0x00, 0x00}, // .
    {0x20, 0x10, 0x08, 0x04, 0x02}, // /

    // 0x30 - 0x39 : Chiffres 0-9
    {0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0
    {0x00, 0x42, 0x7F, 0x40, 0x00}, // 1
    {0x62, 0x51, 0x49, 0x49, 0x46}, // 2
    {0x22, 0x49, 0x49, 0x49, 0x36}, // 3
    {0x18, 0x14, 0x12, 0x7F, 0x10}, // 4
    {0x27, 0x45, 0x45, 0x45, 0x39}, // 5
    {0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6
    {0x01, 0x71, 0x09, 0x05, 0x03}, // 7
    {0x36, 0x49, 0x49, 0x49, 0x36}, // 8
    {0x06, 0x49, 0x49, 0x29, 0x1E}, // 9

    // 0x3A - 0x40 : Autres caractères spéciaux
    {0x00, 0x36, 0x36, 0x00, 0x00}, // :
    {0x00, 0x56, 0x36, 0x00, 0x00}, // ;
    {0x08, 0x14, 0x22, 0x41, 0x00}, // <
    {0x14, 0x14, 0x14, 0x14, 0x14}, // =
    {0x00, 0x41, 0x22, 0x14, 0x08}, // >
    {0x02, 0x01, 0x51, 0x09, 0x06}, // ?
    {0x3E, 0x41, 0x5D, 0x59, 0x4E}, // @

    // 0x41 - 0x5A : Lettres majuscules A-Z
    {0x7E, 0x11, 0x11, 0x11, 0x7E}, // A
    {0x7F, 0x49, 0x49, 0x49, 0x36}, // B
    {0x3E, 0x41, 0x41, 0x41, 0x22}, // C
    {0x7F, 0x41, 0x41, 0x41, 0x3E}, // D
    {0x7F, 0x49, 0x49, 0x49, 0x41}, // E
    {0x7F, 0x09, 0x09, 0x09, 0x01}, // F
    {0x3E, 0x41, 0x49, 0x49, 0x7A}, // G
    {0x7F, 0x08, 0x08, 0x08, 0x7F}, // H
    {0x00, 0x41, 0x7F, 0x41, 0x00}, // I
    {0x20, 0x40, 0x40, 0x40, 0x3F}, // J
    {0x7F, 0x08, 0x14, 0x22, 0x41}, // K
    {0x7F, 0x40, 0x40, 0x40, 0x40}, // L
    {0x7F, 0x02, 0x04, 0x02, 0x7F}, // M
    {0x7F, 0x04, 0x08, 0x10, 0x7F}, // N
    {0x3E, 0x41, 0x41, 0x41, 0x3E}, // O
    {0x7F, 0x09, 0x09, 0x09, 0x06}, // P
    {0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q
    {0x7F, 0x09, 0x19, 0x29, 0x46}, // R
    {0x46, 0x49, 0x49, 0x49, 0x31}, // S
    {0x01, 0x01, 0x7F, 0x01, 0x01}, // T
    {0x3F, 0x40, 0x40, 0x40, 0x3F}, // U
    {0x1F, 0x20, 0x40, 0x20, 0x1F}, // V
    {0x7F, 0x20, 0x10, 0x20, 0x7F}, // W
    {0x63, 0x14, 0x08, 0x14, 0x63}, // X
    {0x03, 0x04, 0x78, 0x04, 0x03}, // Y
    {0x61, 0x51, 0x49, 0x45, 0x43}, // Z

    // 0x5B - 0x60 : Caractères spéciaux
    {0x00, 0x7F, 0x41, 0x41, 0x00}, // [
    {0x02, 0x04, 0x08, 0x10, 0x20}, // \.
    {0x00, 0x41, 0x41, 0x7F, 0x00}, // ]
    {0x04, 0x02, 0x01, 0x02, 0x04}, // ^
    {0x40, 0x40, 0x40, 0x40, 0x40}, // _
    {0x00, 0x60, 0x60, 0x00, 0x00}, // ...

    // 0x61 - 0x7A : Lettres minuscules a-z
    {0x20, 0x54, 0x54, 0x54, 0x78}, // a
    {0x7F, 0x48, 0x44, 0x44, 0x38}, // b
    {0x38, 0x44, 0x44, 0x44, 0x20}, // c
    {0x38, 0x44, 0x44, 0x48, 0x7F}, // d
    {0x38, 0x54, 0x54, 0x54, 0x18}, // e
    {0x08, 0x7E, 0x09, 0x01, 0x02}, // f
    {0x0C, 0x52, 0x52, 0x52, 0x3E}, // g
    {0x7F, 0x08, 0x04, 0x04, 0x78}, // h
    {0x00, 0x44, 0x7D, 0x40, 0x00}, // i
    {0x20, 0x40, 0x40, 0x3D, 0x00}, // j
    {0x7F, 0x10, 0x28, 0x44, 0x00}, // k
    {0x00, 0x41, 0x7F, 0x40, 0x00}, // l
    {0x7C, 0x04, 0x18, 0x04, 0x78}, // m
    {0x7C, 0x08, 0x04, 0x04, 0x78}, // n
    {0x38, 0x44, 0x44, 0x44, 0x38}, // o
    {0x7C, 0x14, 0x14, 0x14, 0x08}, // p
    {0x08, 0x14, 0x14, 0x18, 0x7C}, // q
    {0x7C, 0x08, 0x04, 0x04, 0x08}, // r
    {0x48, 0x54, 0x54, 0x54, 0x20}, // s
    {0x04, 0x3F, 0x44, 0x40, 0x20}, // t
    {0x3C, 0x40, 0x40, 0x20, 0x7C}, // u
    {0x1C, 0x20, 0x40, 0x20, 0x1C}, // v
    {0x3C, 0x40, 0x30, 0x40, 0x3C}, // w
    {0x44, 0x28, 0x10, 0x28, 0x44}, // x
    {0x0C, 0x50, 0x50, 0x50, 0x3C}, // y
    {0x44, 0x64, 0x54, 0x4C, 0x44}, // z
};

// Variables Globales
AsyncWebServer server(80);
CRGB leds[NUM_LEDS];

String texte = "HELLO WORLD ";   // Texte à faire défiler
int vitesse = 100;               // Vitesse de défilement en millisecondes
int offset = 0;                  // Position de décalage actuelle dans le défilement
int couleur = CRGB::White;       // Couleur des LEDs
String animationType = "lettre"; // Mode
int reverse = 0;                 // sens du chapeau

void afficherLettre(char lettre, int positionCol)
{
    // Vérifie si le caractère est dans la plage définie (entre ' ' et 'z')
    if (lettre < 32 || lettre > 122)
    {
        lettre = '.'; // Si hors plage, remplace par un espace
    }

    // Index du caractère dans font5x7
    int index = lettre - 32;

    for (int col = 0; col < 5; col++)
    {                                          // Parcourt chaque colonne de la lettre
        uint8_t colonne = font5x7[index][col]; // Obtient les bits de la colonne dans la police

        for (int row = 0; row < 7; row++)
        { // Parcourt chaque ligne du caractère
            int ledIndex;

            if (reverse) {
              ledIndex = getPixelIndex(positionCol - col, row);
            } else {
              ledIndex = getPixelIndex(positionCol + col, row);
            }

            if (ledIndex < NUM_LEDS && ledIndex >= 0)
            {
                // Allume ou éteint la LED en fonction du bit correspondant
                if (colonne & (1 << row))
                {
                    leds[ledIndex] = couleur; // Pixel allumé
                }
                else
                {
                    leds[ledIndex] = CRGB::Black; // Pixel éteint
                }
            }
        }
    }
}

void afficherTexte(const char *texte)
{
    Serial.println(texte);
    // Effacer l'écran
    fill_solid(leds, NUM_LEDS, CRGB::Black);
    int longueurTexte = strlen(texte);
    for (int i = 0; i < longueurTexte; i++)
    {
        int position = i * 6; // Position sur l'écran
        Serial.println(i);
        if (position >= 0 && position < NUM_COLS - 5)
        {
            Serial.println(i);
            afficherLettre(texte[i], position);
        }
    }

    FastLED.show();
}

void afficherTexteEnDefilement()
{

    Serial.println(texte);

    int longueurTexte = texte.length() * 6; // Largeur totale du texte en pixels (5 + 1 espace par lettre)
    // Efface l'écran avant d'afficher le nouveau cadre
    fill_solid(leds, NUM_LEDS, CRGB::Black);

    int nb_letter = texte.length();

    // Afficher chaque lettre avec le décalage
    for (int i = 0; i < nb_letter; i++)
    {
        int positionCol = (i * 6) - offset; // Calcul de la position de chaque lettre en fonction de l'offset

        // Affiche uniquement les lettres visibles sur la matrice 8x64
        if (positionCol >= -5 && positionCol < NUM_COLS)
        {
            if ( reverse ) {
                afficherLettre(texte[nb_letter-1-i], positionCol);
            } else {
                afficherLettre(texte[i], positionCol);
            }
        }
    }

    // Afficher chaque lettre avec le décalage une deuxième fois (pour que ca boucle)
    for (int i = 0; i < nb_letter; i++)
    {
        int positionCol = (i * 6) - offset + longueurTexte + 6; // Calcul de la position de chaque lettre en fonction de l'offset

        // Affiche uniquement les lettres visibles sur la matrice 8x64
        if (positionCol >= -5 && positionCol < NUM_COLS)
        {
            if ( reverse ) {
                afficherLettre(texte[nb_letter-1-i], positionCol);
            } else {
                afficherLettre(texte[i], positionCol);
            }
        }
    }

    // Met à jour l'écran
    FastLED.show();

    // Augmente l'offset pour déplacer le texte vers la droite
    offset++;

    // Réinitialise l'offset pour boucler le texte lorsqu'il a défilé entièrement
    if (offset >= longueurTexte + 6)
    {
        offset = 0;
    }
}

void setup()
{
    // Démarrer le mode point d'accès
    WiFi.softAP(ssid, password);

    // Afficher l'adresse IP
    Serial.begin(115200);
    Serial.println("Point d'accès démarré");
    Serial.print("Adresse IP : ");
    Serial.println(WiFi.softAPIP());

    // Route pour afficher la page HTML
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
              { request->send(200, "text/html", htmlPage.c_str()); });

    // Route pour recevoir le texte
    server.on("/sendText", HTTP_POST, [](AsyncWebServerRequest *request)
              {
    String text = "";
    String speed = "";
    String color = "";
    String way = "";
    if (request->hasParam("text", true)) {
      text = request->getParam("text", true)->value();
      offset = 0;
      texte = text;
    }
    if (request->hasParam("speed", true)) {
      speed = request->getParam("speed", true)->value();
      vitesse = atoi(speed.c_str());
    }
    if (request->hasParam("colorValue", true)) {
      color = request->getParam("colorValue", true)->value();
      couleur = atoi(color.c_str());
    }
    if (request->hasParam("animation", true)) {
          animationType = request->getParam("animation", true)->value();
    }
    if (request->hasParam("sens", true)) {
          way = request->getParam("sens", true)->value();
          reverse = atoi(way.c_str());
    }
    request->send(200, "text/plain", "New parameters received"); });

    // Configuration du serveur web
    server.begin();

    FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
    FastLED.setBrightness(5);

    // starting test
    for (int i = 0; i < NUM_LEDS; i++)
    {
        fill_solid(leds, NUM_LEDS, CRGB::Black);
        leds[i] = CRGB::White;
        FastLED.show();
    }

    // init Animation
    initInvaders();
    initGrille();
}

void loop()
{
    if (animationType == "lettre")
    {
        afficherTexteEnDefilement();
    }
    else if (animationType == "gouttes")
    {
        afficherGouttes();
    }
    else if (animationType == "coeur")
    {
        defilementContinuCoeurs();
    }
    else if (animationType == "invaders")
    {
        afficherInvaders();
    }
    else if (animationType == "eclair")
    {
        animationEclair();
    }
    else if (animationType == "spider")
    {
    }
    else if (animationType == "life")
    {
        afficherGrille();      // Affiche l'état actuel
        prochaineGeneration(); // Calcule la prochaine génération

        // Introduit une perturbation aléatoire toutes les quelques générations
        if (random(10) < 2)
        { // 20% de chance d'ajouter une perturbation chaque cycle
            perturberGrille();
        }
    }

    FastLED.delay(vitesse);
}

// Fonction pour gérer l'index en zigzag de la matrice
int getPixelIndex(int x, int y)
{
    if (reverse) {
        if (x % 2 == 0)
        {
            return x * NUM_ROWS + (NUM_ROWS - 1 - y); // Colonne de bas en haut
        }
        else
        {
            return x * NUM_ROWS + y; // Colonne de haut en bas
        }
    } else {
        if (x % 2 == 0)
        {
            return x * NUM_ROWS + y; // Colonne de haut en bas
        }
        else
        {
            return x * NUM_ROWS + (NUM_ROWS - 1 - y); // Colonne de bas en haut
        }  
    }
}

void afficherGouttes()
{
    for (int x = 0; x < NUM_COLS; x++)
    {
        leds[getPixelIndex(x, 0)] = CRGB::Black;
    }

    // Ajoute des nouvelles gouttes rouges au sommet avec une probabilité de 20%
    for (int x = 0; x < NUM_COLS; x++)
    {
        if (random(100) < 20)
        { // Probabilité de 20% d'apparition d'une goutte par colonne
            leds[getPixelIndex(x, 0)] = CRGB::Red;
        }
    }

    // Fait descendre les gouttes existantes
    for (int y = NUM_ROWS; y > 0; y--)
    {
        for (int x = 0; x < NUM_COLS; x++)
        {
            int currentPixel = getPixelIndex(x, y);
            int abovePixel = getPixelIndex(x, y - 1);
            leds[currentPixel] = leds[abovePixel];
            leds[abovePixel] = CRGB::Black;
        }
    }

    for (int x = 0; x < NUM_COLS; x++)
    {
        leds[getPixelIndex(x, 0)] = CRGB::Red;
    }

    FastLED.show();
}

int heartPattern[8][8] = { // Motif de coeur 8x8
    {0, 1, 1, 0, 0, 1, 1, 0},
    {1, 1, 1, 1, 1, 1, 1, 1},
    {1, 1, 1, 1, 1, 1, 1, 1},
    {0, 1, 1, 1, 1, 1, 1, 0},
    {0, 0, 1, 1, 1, 1, 0, 0},
    {0, 0, 0, 1, 1, 0, 0, 0},
    {0, 0, 0, 1, 1, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0}};

// Fonction pour afficher un coeur à une position donnée
void afficherCoeur(int xOffset, const CRGB color)
{
    for (int y = 0; y < 8; y++)
    {
        for (int x = 0; x < 8; x++)
        {
            int pixelIndex = getPixelIndex(x + xOffset, y);
            if (heartPattern[y][x] == 1 && x + xOffset >= 0 && x + xOffset < NUM_COLS)
            {
                leds[pixelIndex] = color;
            }
        }
    }
}

void defilementContinuCoeurs()
{
    static int offset = 0;           // Position de départ du cœur le plus à droite
    const int espacementCoeurs = 16; // Distance entre chaque cœur en défilement

    // Efface la matrice pour le défilement
    FastLED.clear();

    // Affiche des cœurs régulièrement espacés pour un défilement continu
    for (int i = 0; i < (NUM_COLS / espacementCoeurs + 2) * 2; i++)
    { // Ajout de deux cœurs supplémentaires pour un effet continu
        int xOffset = offset + i * espacementCoeurs;
        if (random(100) < 10)
        { // Probabilité de 10% pour le clignotement en rose
            afficherCoeur(xOffset, CRGB(255, 105, 180));
        }
        else
        {
            afficherCoeur(xOffset, CRGB::Red);
        }
    }

    FastLED.show();

    // Déplace les cœurs vers la gauche
    offset--;
    if (offset < -NUM_COLS)
    { // Réinitialise l'offset pour recommencer le défilement sans interruption
        offset = 0;
    }
}

const int NUM_INVADERS = 10; // Nombre d'invaders
// Motif d'un Space Invader (5x5) pour simplifier
int invaderPattern[5][5] = {
    {0, 1, 1, 1, 0},
    {1, 0, 1, 0, 1},
    {1, 1, 1, 1, 1},
    {0, 1, 1, 1, 0},
    {0, 1, 1, 1, 0}};

// Structure pour gérer les informations de chaque invader
struct Invader
{
    int x;      // Position X de l'invader
    int y;      // Position Y de l'invader
    int dx;     // Déplacement X (-1, 0, 1)
    int dy;     // Déplacement Y (-1, 0, 1)
    CRGB color; // Couleur de l'invader
};

Invader invaders[NUM_INVADERS];

// Initialise les invaders avec des positions et des directions aléatoires
void initInvaders()
{
    for (int i = 0; i < NUM_INVADERS; i++)
    {
        invaders[i].x = random(0, NUM_COLS);
        invaders[i].y = random(0, NUM_ROWS);
        invaders[i].dx = random(-1, 2);                     // Mouvement horizontal aléatoire (-1, 0, 1)
        invaders[i].dy = random(-1, 2);                     // Mouvement vertical aléatoire (-1, 0, 1)
        invaders[i].color = CHSV(random(0, 255), 255, 255); // Couleur aléatoire pour chaque invader
    }
}

// Fonction pour afficher un invader à une position donnée avec une couleur donnée
void afficherInvader(int xOffset, int yOffset, CRGB color)
{
    for (int y = 0; y < 5; y++)
    {
        for (int x = 0; x < 5; x++)
        {
            int pixelIndex = getPixelIndex(x + xOffset, y + yOffset);
            if (invaderPattern[y][x] == 1 && x + xOffset >= 0 && x + xOffset < NUM_COLS && y + yOffset >= 0 && y + yOffset < NUM_ROWS)
            {
                leds[pixelIndex] = color;
            }
        }
    }
}

void afficherInvaders()
{
    // Efface la matrice
    FastLED.clear();

    for (int i = 0; i < NUM_INVADERS; i++)
    {
        Invader &invader = invaders[i];

        // Affiche l'invader
        afficherInvader(invader.x, invader.y, invader.color);

        // Met à jour la position de l'invader
        invader.x += invader.dx;
        invader.y += invader.dy;

        // Gère le passage des invaders hors de l'écran
        if (invader.x < -5 || invader.x >= NUM_COLS)
        {
            invader.x = (invader.dx < 0) ? NUM_COLS : -5; // Réapparaît de l'autre côté
            invader.y = random(0, NUM_ROWS);              // Nouvelle position Y aléatoire
        }
        if (invader.y < -5 || invader.y >= NUM_ROWS)
        {
            invader.y = (invader.dy < 0) ? NUM_ROWS : -5; // Réapparaît de l'autre côté
            invader.x = random(0, NUM_COLS);              // Nouvelle position X aléatoire
        }

        // Change la direction aléatoirement pour chaque invader (petite probabilité)
        if (random(0, 10) < 1)
        {
            invader.dx = random(-1, 2);
            invader.dy = random(-1, 2);
        }
    }

    FastLED.show();
}

void dessinerEclair()
{
    // Point de départ de l'éclair (une position aléatoire en haut)
    int x = random(NUM_COLS);
    int y = 0;

    // Couleur de l'éclair (blanc avec une légère teinte bleutée)
    CRGB couleurEclair = CRGB(180, 180, 255);

    // Déplacement aléatoire en zigzag
    for (int i = 0; i < NUM_ROWS; i++)
    {
        // Allume la LED de l'éclair
        leds[getPixelIndex(x, y)] = couleurEclair;
        FastLED.show();

        y = constrain(y + 1, 0, NUM_ROWS - 1);
        if (random(2) == 0)
        {
            int new_x = x + random(-1, 2);
            x = constrain(new_x, 0, NUM_COLS - 1); // Mouvement aléatoire vers la gauche ou la droite
        }
    }
}

// Fonction pour générer des éclairs de manière aléatoire
void animationEclair()
{
    fill_solid(leds, NUM_LEDS, CRGB(0, 0, random(0, 20)));
    int nb_eclair = random(10);
    for (int i = 0; i < nb_eclair; i++)
    {
        dessinerEclair();
    }
}

bool grille[NUM_COLS][NUM_ROWS];
bool prochaineGrille[NUM_COLS][NUM_ROWS];

CRGB DeepRed = CRGB(139, 0, 0);

// Initialisation de la grille avec un état aléatoire
void initGrille()
{
    for (int x = 0; x < NUM_COLS; x++)
    {
        for (int y = 0; y < NUM_ROWS; y++)
        {
            grille[x][y] = random(2); // Cellule vivante ou morte aléatoirement
        }
    }
}

// Fonction pour compter les voisins vivants d'une cellule
int compterVoisins(int x, int y)
{
    int voisins = 0;
    for (int dx = -1; dx <= 1; dx++)
    {
        for (int dy = -1; dy <= 1; dy++)
        {
            if (dx != 0 || dy != 0)
            {                                            // Ignore la cellule elle-même
                int nx = (x + dx + NUM_COLS) % NUM_COLS; // Gestion des bords (tore)
                int ny = (y + dy + NUM_ROWS) % NUM_ROWS;
                voisins += grille[nx][ny];
            }
        }
    }
    return voisins;
}

// Calcul de la prochaine génération
void prochaineGeneration()
{
    for (int x = 0; x < NUM_COLS; x++)
    {
        for (int y = 0; y < NUM_ROWS; y++)
        {
            int voisins = compterVoisins(x, y);
            if (grille[x][y])
            { // Cellule vivante
                prochaineGrille[x][y] = (voisins == 2 || voisins == 3);
            }
            else
            { // Cellule morte
                prochaineGrille[x][y] = (voisins == 3);
            }
        }
    }

    // Mise à jour de la grille
    for (int x = 0; x < NUM_COLS; x++)
    {
        for (int y = 0; y < NUM_ROWS; y++)
        {
            grille[x][y] = prochaineGrille[x][y];
        }
    }
}

// Affichage de la grille actuelle sur la matrice LED
void afficherGrille()
{
    for (int x = 0; x < NUM_COLS; x++)
    {
        for (int y = 0; y < NUM_ROWS; y++)
        {
            if (grille[x][y])
            {
                leds[getPixelIndex(x, y)] = DeepRed; // Cellule vivante (rouge)
            }
            else
            {
                leds[getPixelIndex(x, y)] = CRGB::Black; // Cellule morte (noire)
            }
        }
    }
    FastLED.show();
}

void perturberGrille()
{
    for (int i = 0; i < NUM_LEDS / 10; i++)
    { // Perturbe environ 10% des cellules
        int x = random(NUM_COLS);
        int y = random(NUM_ROWS);
        grille[x][y] = !grille[x][y]; // Inverse l'état de la cellule (vivante -> morte, morte -> vivante)
    }
}
