/*
 * =============================================
 *   SCAREDY CAT — Ultrasonic + LCD + Buzzer
 * =============================================
 */

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#define TRIG_PIN    9
#define ECHO_PIN    8
#define BUZZER_PIN  3

#define DIST_PANIC   100
#define DIST_SCARED  40

#define MODE_CONFIRM_MS 1000

LiquidCrystal_I2C MyLCD(0x27, 20, 4);

uint8_t fullPixel[] = {0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f};

// ── State ─────────────────────────────────────
enum CatState { HAPPY, PANIC, SCARED };
CatState confirmedState  = HAPPY;
CatState candidateState  = HAPPY;
CatState lastDrawnState  = SCARED;

unsigned long candidateStartTime = 0;

// ── Buzzer timing ─────────────────────────────
unsigned long lastBuzzerTime = 0;
int buzzerStep = 0;

// ── Panic animation timing ────────────────────
unsigned long lastPanicFrameTime = 0;
int panicFrame = 0;

// ─────────────────────────────────────────────
//  Distance
// ─────────────────────────────────────────────
long getDistance() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  long duration = pulseIn(ECHO_PIN, HIGH, 30000);
  if (duration == 0) return 999;
  return duration * 0.034 / 2;
}

CatState getStateForDistance(long dist) {
  if (dist < DIST_SCARED) return SCARED;
  if (dist < DIST_PANIC)  return PANIC;
  return HAPPY;
}

// ─────────────────────────────────────────────
//  LCD Faces (UNCHANGED)
// ─────────────────────────────────────────────

void drawHappy() {
  MyLCD.clear();
  MyLCD.setCursor(4, 1);  MyLCD.write(byte(0));
  MyLCD.setCursor(15, 1); MyLCD.write(byte(0));
  MyLCD.setCursor(3, 2);  MyLCD.write(byte(0));
  MyLCD.setCursor(5, 2);  MyLCD.write(byte(0));
  MyLCD.setCursor(14, 2); MyLCD.write(byte(0));
  MyLCD.setCursor(16, 2); MyLCD.write(byte(0));
  MyLCD.setCursor(2, 3);  MyLCD.write(byte(0));
  MyLCD.setCursor(6, 3);  MyLCD.write(byte(0));
  MyLCD.setCursor(13, 3); MyLCD.write(byte(0));
  MyLCD.setCursor(17, 3); MyLCD.write(byte(0));
}

void drawPanicFrame(int frame) {
  MyLCD.clear();

  if (frame == 0) {
    MyLCD.setCursor(1,1); MyLCD.write(byte(0));
    MyLCD.setCursor(2,1); MyLCD.write(byte(0));
    MyLCD.setCursor(12,1);MyLCD.write(byte(0));
    MyLCD.setCursor(13,1);MyLCD.write(byte(0));
  }
  else if (frame == 1) {
    MyLCD.setCursor(3,1); MyLCD.write(byte(0));
    MyLCD.setCursor(4,1); MyLCD.write(byte(0));
    MyLCD.setCursor(14,1);MyLCD.write(byte(0));
    MyLCD.setCursor(15,1);MyLCD.write(byte(0));
  }
  else {
    MyLCD.setCursor(6,1); MyLCD.write(byte(0));
    MyLCD.setCursor(7,1); MyLCD.write(byte(0));
    MyLCD.setCursor(17,1);MyLCD.write(byte(0));
    MyLCD.setCursor(18,1);MyLCD.write(byte(0));
  }
}

void drawScared() {
  MyLCD.clear();
  MyLCD.setCursor(2,1);  MyLCD.write(byte(0));
  MyLCD.setCursor(3,1);  MyLCD.write(byte(0));
  MyLCD.setCursor(14,1); MyLCD.write(byte(0));
  MyLCD.setCursor(15,1); MyLCD.write(byte(0));
}

// ─────────────────────────────────────────────
//  Buzzer patterns (UNCHANGED)
// ─────────────────────────────────────────────
struct BuzzerStep {
  int freq;
  int duration;
};

BuzzerStep happyPattern[]  = { {1200, 80}, {0, 1500} };
BuzzerStep panicPattern[]  = { {1200, 400}, {0, 400}  };
BuzzerStep scaredPattern[] = { {1200, 80}, {0, 60}, {1200, 60}, {0, 400} };

const int happySteps  = 2;
const int panicSteps  = 2;
const int scaredSteps = 4;

void handleBuzzer(CatState state, unsigned long now) {
  BuzzerStep* pattern;
  int steps;

  if (state == HAPPY)      { pattern = happyPattern;  steps = happySteps;  }
  else if (state == PANIC) { pattern = panicPattern;  steps = panicSteps;  }
  else                     { pattern = scaredPattern; steps = scaredSteps; }

  if (buzzerStep >= steps) buzzerStep = 0;

  if (now - lastBuzzerTime >= (unsigned long)pattern[buzzerStep].duration) {
    buzzerStep++;
    if (buzzerStep >= steps) buzzerStep = 0;

    if (pattern[buzzerStep].freq > 0) tone(BUZZER_PIN, pattern[buzzerStep].freq);
    else                              noTone(BUZZER_PIN);

    lastBuzzerTime = now;
  }
}

// ─────────────────────────────────────────────
//  Setup
// ─────────────────────────────────────────────
void setup() {
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(BUZZER_PIN, OUTPUT);

  MyLCD.init();
  MyLCD.backlight();
  MyLCD.createChar(0, fullPixel);
  MyLCD.clear();

  Serial.begin(9600);
}

// ─────────────────────────────────────────────
//  Loop
// ─────────────────────────────────────────────
void loop() {
  unsigned long now = millis();

  long dist        = getDistance();
  CatState reading = getStateForDistance(dist);

  if (reading != candidateState) {
    candidateState     = reading;
    candidateStartTime = now;
    buzzerStep = 0;
  }

  if (candidateState != confirmedState) {
    if (now - candidateStartTime >= MODE_CONFIRM_MS) {
      confirmedState = candidateState;
      buzzerStep     = 0;
      panicFrame     = 0;

      if (confirmedState == HAPPY)  drawHappy();
      if (confirmedState == PANIC)  drawPanicFrame(0);
      if (confirmedState == SCARED) drawScared();
      lastPanicFrameTime = now;
    }
  }

  if (confirmedState == PANIC && now - lastPanicFrameTime >= 1000) {
    panicFrame = (panicFrame + 1) % 3;
    drawPanicFrame(panicFrame);
    lastPanicFrameTime = now;
  }

  handleBuzzer(confirmedState, now);

  delay(100);
}
