/*
 * Yeux animés — Kaihatsu Lab
 * SSD1306 128x64 (I2C). Regard qui se déplace, clignements, quatre humeurs.
 * Bouton optionnel entre PIN_MOOD et GND pour changer d'humeur à la main.
 *
 * Écran avec des broches I2C personnalisées (ESP32) : appelle
 * Wire.begin(SDA, SCL) dans setup() AVANT display.begin().
 */
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_W  128
#define SCREEN_H  64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_W, SCREEN_H, &Wire, -1);

const uint8_t PIN_MOOD = 25;          // bouton optionnel (INPUT_PULLUP)
const bool    AUTO_MOOD = true;       // change d'humeur toute seule

// ---------- Forme des yeux ----------
const int EYE_W = 36;
const int EYE_H = 36;
const int EYE_R = 10;                 // arrondi des coins
const int EYE_GAP = 12;               // espace entre les deux yeux
const int LOOK_MAX_X = 12;            // amplitude du regard (pixels)
const int LOOK_MAX_Y = 8;

enum Mood : uint8_t { MOOD_NORMAL, MOOD_HAPPY, MOOD_ANGRY, MOOD_TIRED, MOOD_COUNT };
Mood mood = MOOD_NORMAL;

// ---------- État de l'animation ----------
float lookX = 0, lookY = 0;           // position actuelle du regard
int   targetX = 0, targetY = 0;       // position visée
float openness = 1.0f;                // 1 = ouvert, 0 = fermé
uint8_t blinkPhase = 0;               // 0 = repos, 1 = se ferme, 2 = se rouvre
float lidAmount = 0;                  // intensité de la paupière (0 à 1)
uint32_t nextBlink = 1500, nextLook = 0, nextMood = 7000, lastFrame = 0;
bool lastButton = false;

void setMood(Mood m) {
  mood = m;
  lidAmount = 0;                      // la paupière se remet en place en douceur
}

// ---------- Dessin ----------
void drawEye(int cx, int cy, bool isLeft) {
  int h = max(3, (int)(EYE_H * openness));
  int x = cx - EYE_W / 2;
  int y = cy - h / 2;
  int r = min(EYE_R, h / 2);
  display.fillRoundRect(x, y, EYE_W, h, r, SSD1306_WHITE);

  int lid = (int)(lidAmount * EYE_H * 0.45f);   // hauteur de la paupière
  if (lid < 1) return;

  switch (mood) {
    case MOOD_TIRED:                  // coins extérieurs abaissés
      if (isLeft) display.fillTriangle(x, y, x + EYE_W, y, x, y + lid, SSD1306_BLACK);
      else        display.fillTriangle(x, y, x + EYE_W, y, x + EYE_W, y + lid, SSD1306_BLACK);
      break;
    case MOOD_ANGRY:                  // coins intérieurs abaissés
      if (isLeft) display.fillTriangle(x, y, x + EYE_W, y, x + EYE_W, y + lid, SSD1306_BLACK);
      else        display.fillTriangle(x, y, x + EYE_W, y, x, y + lid, SSD1306_BLACK);
      break;
    case MOOD_HAPPY:                  // le bas de l'œil remonte en arc
      display.fillRoundRect(x - 2, y + h - lid, EYE_W + 4, EYE_H, EYE_R, SSD1306_BLACK);
      break;
    default:
      break;
  }
}

void drawEyes() {
  display.clearDisplay();
  int cy = SCREEN_H / 2 + (int)lookY;
  int leftX  = SCREEN_W / 2 - (EYE_W + EYE_GAP) / 2 + (int)lookX;
  int rightX = SCREEN_W / 2 + (EYE_W + EYE_GAP) / 2 + (int)lookX;
  drawEye(leftX,  cy, true);
  drawEye(rightX, cy, false);
  display.display();
}

// ---------- Animation ----------
void updateEyes(uint32_t now) {
  // Regard : nouvelle cible de temps en temps, avec un déplacement lissé
  if (now >= nextLook) {
    targetX = random(-LOOK_MAX_X, LOOK_MAX_X + 1);
    targetY = random(-LOOK_MAX_Y, LOOK_MAX_Y + 1);
    if (random(3) == 0) { targetX = 0; targetY = 0; }   // parfois, droit devant
    nextLook = now + random(1200, 3500);
  }
  lookX += (targetX - lookX) * 0.25f;
  lookY += (targetY - lookY) * 0.25f;

  // Clignement en deux temps : fermeture rapide, réouverture un peu plus lente
  if (blinkPhase == 0 && now >= nextBlink) blinkPhase = 1;
  if (blinkPhase == 1) {
    openness -= 0.34f;
    if (openness <= 0.08f) { openness = 0.08f; blinkPhase = 2; }
  } else if (blinkPhase == 2) {
    openness += 0.28f;
    if (openness >= 1.0f) { openness = 1.0f; blinkPhase = 0; nextBlink = now + random(2000, 5500); }
  }

  // Paupières : montent doucement quand l'humeur n'est pas neutre
  float lidTarget = (mood == MOOD_NORMAL) ? 0.0f : 1.0f;
  lidAmount += (lidTarget - lidAmount) * 0.2f;
}

void setup() {
  pinMode(PIN_MOOD, INPUT_PULLUP);
  randomSeed(micros());

  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
    for (;;) delay(1000);             // écran introuvable : lance le scanner I2C
  }
  Wire.setClock(400000);              // I2C rapide pour une animation fluide
  display.clearDisplay();
  display.display();
}

void loop() {
  uint32_t now = millis();

  // Changement d'humeur : bouton ou automatique
  bool button = (digitalRead(PIN_MOOD) == LOW);
  if (button && !lastButton) { setMood((Mood)((mood + 1) % MOOD_COUNT)); nextMood = now + 7000; }
  lastButton = button;
  if (AUTO_MOOD && now >= nextMood) { setMood((Mood)((mood + 1) % MOOD_COUNT)); nextMood = now + 7000; }

  // 30 images par seconde
  if (now - lastFrame >= 33) {
    lastFrame = now;
    updateEyes(now);
    drawEyes();
  }
}
