#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

// Définition des broches pour l'écran TFT
#define TFT_CLK 13
#define TFT_MISO 12
#define TFT_MOSI 11
#define TFT_DC 5
#define TFT_CS 4
#define TFT_RST 8
#define ILI9341_BLACK 0x0000  ///< Couleur noir
#define ILI9341_YELLOW 0xFFE0 ///< Couleur jaune

// Définition des broches pour le joystick
#define JOYSTICK_X A0
#define JOYSTICK_Y A1

// Création de l'objet pour l'écran ILI9341
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

// Position initiale du graphisme (invader)
int posX = 120;
int posY = 160;
const int invaderSize = 2;  // Taille d'un pixel du Space Invader

// Position et direction du méchant
int poulpodisPosX = 10;
int poulpodisPosY = 10;
int poulpodisDirection = 1; // 1 pour droite, -1 pour gauche
const int poulpodisSpeed = 2; // Vitesse de déplacement du méchant

// Taille des entités (le héros et le méchant)
const int invaderWidth = invaderSize * 8;
const int invaderHeight = invaderSize * 8;

// Nombre maximum de missiles que le joueur peut avoir à l'écran en même temps
const int maxMissiles = 5;
int missileX[maxMissiles];
int missileY[maxMissiles];
bool missileActive[maxMissiles]; // Indique si le missile est actif (tiré)
const int missileWidth = 2;
const int missileHeight = 8;

// Définir une broche pour le bouton de tir
#define FIRE_BUTTON 2 // Exemple de broche pour le bouton

// Seuils pour éviter que le mouvement ne soit trop sensible
const int threshold = 100;

// Modèle Space Invader en binaire (8x8)
byte invader[8] = {
  0b01000010,
  0b00100100,
  0b00111100,
  0b11100111,
  0b10111101,
  0b10011001,
  0b00100100,
  0b01100110
};

byte poulpodis1000[8] = {
 0b00000000,
  0b01111100,
  0b01010100,
  0b01111100,
  0b01111100,
  0b00111000,
  0b01010100,
  0b00000000
};

byte poulpodis500[8] //monstroDanseur
{
  0b00010010,
  0b00111010,
  0b00111010,
  0b10010010,
  0b11111111,
  0b00010010,
  0b01111100,
  0b01000100
};

// Fonction d'initialisation
void setup() {
  // Initialisation de la communication avec l'écran
  tft.begin();
  tft.fillScreen(ILI9341_BLACK);
  
  // Initialisation des entrées pour le joystick
  pinMode(JOYSTICK_X, INPUT);
  pinMode(JOYSTICK_Y, INPUT);
    pinMode(FIRE_BUTTON, INPUT_PULLUP);

  // Initialiser tous les missiles comme inactifs
  for (int i = 0; i < maxMissiles; i++) {
    missileActive[i] = false;
  }

  // Dessin initial du graphisme
  drawInvader(posX, posY);
  
  // Initialiser le moniteur série 
 Serial.begin(9600);
}

// Fonction de la boucle principale
void loop() {  // Lecture des positions du joystick pour le joueur
  int xValue = analogRead(JOYSTICK_X) - 512;  // Centrer les valeurs à 0
  int yValue = analogRead(JOYSTICK_Y) - 512;

  // Sauvegarder l'ancienne position pour effacer le joueur plus tard
  int oldPosX = posX;
  int oldPosY = posY;

  // Tirer un missile si le bouton est pressé
  if (digitalRead(FIRE_BUTTON) == LOW) {
    fireMissile();  // Tirer un missile
  }

  // Gérer les missiles
  for (int i = 0; i < maxMissiles; i++) {
    if (missileActive[i]) {
      // Effacer l'ancienne position du missile
      tft.fillRect(missileX[i], missileY[i], missileWidth, missileHeight, ILI9341_BLACK);
      missileY[i] -= 5;  // Déplacer le missile vers le haut

      // Vérifier si le missile est hors de l'écran
      if (missileY[i] < 0) {
        missileActive[i] = false;  // Désactiver le missile
      }

      // Vérifier si le missile touche le méchant
      if (checkCollision(missileX[i], missileY[i], missileWidth, missileHeight, poulpodisPosX, poulpodisPosY, invaderWidth, invaderHeight)) {
        // Collision détectée : détruire le méchant et désactiver le missile
        Serial.println("Le méchant est touché !");
      
        // Effacer l'ancien méchant
        tft.fillRect(poulpodisPosX, poulpodisPosY, invaderWidth, invaderHeight, ILI9341_BLACK);

        missileActive[i] = false;
        // Vous pouvez aussi réinitialiser la position du méchant ou créer un nouvel ennemi ici
        poulpodisPosX = 10; // Réinitialiser le méchant (par exemple)
        poulpodisPosY = 10; // Réinitialiser sa position
      }

      // Redessiner le missile s'il est encore actif
      if (missileActive[i]) {
        tft.fillRect(missileX[i], missileY[i], missileWidth, missileHeight, ILI9341_YELLOW);
      }
    }
  }
  // Détection du mouvement sur l'axe X pour le joueur
  if (xValue > threshold) {
    posX -= 2;  // Déplace vers la droite
  } else if (xValue < -threshold) {
    posX += 2;  // Déplace vers la gauche
  }
  
  // Détection du mouvement sur l'axe Y pour le joueur
  if (yValue > threshold) {
    posY -= 2;  // Déplace vers le bas
  } else if (yValue < -threshold) {
    posY += 2;  // Déplace vers le haut
  }

  // Limiter les déplacements du joueur aux bords de l'écran
  if (posX < 0) posX = 0;
  if (posX > tft.width() - invaderWidth) posX = tft.width() - invaderWidth;
  if (posY < 0) posY = 0;
  if (posY > tft.height() - invaderHeight) posY = tft.height() - invaderHeight;

  // Effacer seulement l'ancienne position du joueur
  tft.fillRect(oldPosX, oldPosY, invaderWidth, invaderHeight, ILI9341_BLACK);

  // Redessiner le joueur à la nouvelle position
  drawInvader(posX, posY);

  // Effacer l'ancienne position du méchant "poulpodis"
  tft.fillRect(poulpodisPosX, poulpodisPosY, invaderWidth, invaderHeight, ILI9341_BLACK);

  // Déplacement du méchant "poulpodis"
  poulpodisPosX += poulpodisDirection * poulpodisSpeed;

  // Si le méchant atteint le bord de l'écran, il change de direction et descend d'une ligne
  if (poulpodisPosX <= 0 || poulpodisPosX >= tft.width() - invaderWidth) {
    poulpodisDirection *= -1;  // Inverse la direction
    poulpodisPosY += invaderHeight;  // Descend d'une ligne
  }

  // Redessiner le méchant à sa nouvelle position
  drawPoulpodis(poulpodisPosX, poulpodisPosY);

  // Détection de collision entre le héros et le méchant
  if (checkCollision(posX, posY, invaderWidth, invaderHeight, poulpodisPosX, poulpodisPosY, invaderWidth, invaderHeight)) {
    Serial.println("Collision détectée !");
  }

  // Petit délai pour rendre le mouvement fluide
  delay(50);
}

// Fonction pour vérifier la collision en boîte englobante
bool checkCollision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) {
  return !(x1 > x2 + w2 ||   // trop à droite
           x1 + w1 < x2 ||   // trop à gauche
           y1 > y2 + h2 ||   // trop en bas
           y1 + h1 < y2);    // trop en haut
}
// Fonction pour dessiner un mechant
void drawPoulpodis(int x, int y) {
  for (int row = 0; row < 8; row++) {
    for (int col = 0; col < 8; col++) {
      if (poulpodis500[row] & (1 << (7 - col))) {
        // Dessiner un pixel du Space Invader
        tft.fillRect(x + col * invaderSize, y + row * invaderSize, invaderSize, invaderSize, ILI9341_YELLOW);
      }
    }
  }
}
// Fonction pour dessiner un Space Invader
void drawInvader(int x, int y) {
  for (int row = 0; row < 8; row++) {
    for (int col = 0; col < 8; col++) {
      if (invader[row] & (1 << (7 - col))) {
        // Dessiner un pixel du Space Invader
        tft.fillRect(x + col * invaderSize, y + row * invaderSize, invaderSize, invaderSize, ILI9341_YELLOW);
      }
    }
  }
}

void fireMissile() {
  for (int i = 0; i < maxMissiles; i++) {
    if (!missileActive[i]) {
      // Si un missile est disponible, tire un nouveau missile
      missileX[i] = posX + invaderWidth / 2 - missileWidth / 2; // Positionner au centre du personnage
      missileY[i] = posY;  // Le missile part de la position du joueur
      missileActive[i] = true;
      break;  // On ne tire qu'un missile à la fois
    }
  }
}