#include <Wire.h>
#include <RTClib.h>
#include <Servo.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <avr/wdt.h>

// =================================================
// OLED 128x32
// =================================================
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// =================================================
// PINES
// =================================================
#define PIN_BTN_HORAS        2
#define PIN_BTN_MINUTOS      3
#define PIN_BTN_CUENTABOLAS  4
#define PIN_BTN_STOP         A0
#define PIN_BTN_DIAG         12

#define PIN_SERVO_FLIPPER     9
#define PIN_SERVO_MOTOR      10
#define PIN_SERVO_CAMPANADAS 11

#define PIN_MOTOR_PWMA  5
#define PIN_MOTOR_AIN1  7
#define PIN_MOTOR_AIN2  8

// =================================================
// CONFIGURACIÓN (NO CAMBIADA)
// =================================================
const uint8_t CAMPANADA_DELAY_SEG = 0;

const unsigned long DEBOUNCE_MS    = 80;
const unsigned long LCD_ROTATE_MS  = 2000;
const unsigned long LCD_UPDATE_MS  = 200;

const int SERVO_MOTOR_REPOSO = 76;
const int SERVO_MOTOR_DELTA  = 40;

const unsigned long MOTOR_TIMEOUT_MS = 120000UL;
const unsigned long DIAG_LONG_PRESS_MS = 3000;
const unsigned long BALL_GUARD_MS = 400;

// =================================================
// OBJETOS
// =================================================
RTC_DS3231 rtc;

Servo servoFlipper;
Servo servoMotor;
Servo servoCampanadas;

// =================================================
// VARIABLES (NO CAMBIADAS)
// =================================================
enum { BTN_H, BTN_M, BTN_CB, BTN_STOP_I, BTN_TOTAL };

bool lastBtn[BTN_TOTAL];
unsigned long lastBtnTime[BTN_TOTAL];

unsigned long nowMs;
unsigned long servoMotorReturn = 0;
unsigned long servoCampReturn  = 0;

unsigned long lcdLastRotate = 0;
unsigned long lcdLastUpdate = 0;

int lastMinute = -1;
int prevMinute = -1;
int lastHour   = -1;

bool flipperAbierto     = false;
bool flipperHechoHora  = false;
bool campanadaHechaHora = false;

int bolasContadas = 0;
int bolasObjetivo = 0;

uint8_t lcdPage = 0;

unsigned long motorStartMs = 0;
bool motorBloqueado = false;

unsigned long diagPressStart = 0;
bool diagLongHandled = false;

unsigned long lastBallMs = 0;

bool lastHourBtn   = HIGH;
bool lastMinuteBtn = HIGH;

// =================================================
// FUNCIONES (NO CAMBIADAS)
// =================================================
bool botonPulsado(uint8_t idx, uint8_t pin) {
  bool estado = digitalRead(pin);
  unsigned long ahora = millis();

  if (estado != lastBtn[idx]) {
    lastBtn[idx] = estado;
    lastBtnTime[idx] = ahora;
    return false;
  }

  if (estado == LOW && (ahora - lastBtnTime[idx]) > DEBOUNCE_MS) {
    lastBtnTime[idx] = ahora + 1;
    return true;
  }
  return false;
}

int bolasPorHora(int hora24) {
  int h = hora24 % 12;
  if (h == 0) return 1;
  return h + 1;
}

// =================================================
// SETUP
// =================================================
void setup() {

  wdt_enable(WDTO_4S);

  Wire.begin();
  rtc.begin();

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setRotation(2);
  display.display();

  pinMode(PIN_BTN_HORAS, INPUT_PULLUP);
  pinMode(PIN_BTN_MINUTOS, INPUT_PULLUP);
  pinMode(PIN_BTN_CUENTABOLAS, INPUT_PULLUP);
  pinMode(PIN_BTN_STOP, INPUT_PULLUP);
  pinMode(PIN_BTN_DIAG, INPUT_PULLUP);

  for (int i = 0; i < BTN_TOTAL; i++) lastBtn[i] = HIGH;

  servoFlipper.attach(PIN_SERVO_FLIPPER);
  servoMotor.attach(PIN_SERVO_MOTOR);
  servoCampanadas.attach(PIN_SERVO_CAMPANADAS);

  servoFlipper.write(0);
  servoMotor.write(SERVO_MOTOR_REPOSO);
  servoCampanadas.write(0);

  pinMode(PIN_MOTOR_PWMA, OUTPUT);
  pinMode(PIN_MOTOR_AIN1, OUTPUT);
  pinMode(PIN_MOTOR_AIN2, OUTPUT);

  digitalWrite(PIN_MOTOR_AIN1, HIGH);
  digitalWrite(PIN_MOTOR_AIN2, LOW);

  DateTime now = rtc.now();
  lastMinute = now.minute();
  prevMinute = now.minute();
  lastHour   = now.hour();
}

// =================================================
// LOOP
// =================================================
void loop() {

  wdt_reset();

  DateTime now = rtc.now();
  nowMs = millis();
  int currentMinute = now.minute();

  // ---- RESET LARGO (IGUAL)
  if (digitalRead(PIN_BTN_DIAG) == LOW) {
    if (diagPressStart == 0) {
      diagPressStart = nowMs;
      diagLongHandled = false;
    }

    if (!diagLongHandled &&
        (nowMs - diagPressStart) > DIAG_LONG_PRESS_MS) {

      motorBloqueado = false;
      motorStartMs = 0;

      flipperAbierto = false;
      flipperHechoHora = false;

      bolasContadas = 0;
      bolasObjetivo = 0;

      servoFlipper.write(0);
      servoCampanadas.write(0);

      diagLongHandled = true;
    }
  } else {
    diagPressStart = 0;
    diagLongHandled = false;
  }

  // ---- AJUSTE RTC (IGUAL)
  bool hState = digitalRead(PIN_BTN_HORAS);
  bool mState = digitalRead(PIN_BTN_MINUTOS);

  if (lastHourBtn == HIGH && hState == LOW) {
    rtc.adjust(DateTime(now.year(), now.month(), now.day(),
                        (now.hour() + 1) % 24, 0, 0));
  }
  lastHourBtn = hState;

  if (lastMinuteBtn == HIGH && mState == LOW) {
    rtc.adjust(DateTime(now.year(), now.month(), now.day(),
                        now.hour(), (currentMinute + 1) % 60, 0));
  }
  lastMinuteBtn = mState;

  // ---- MOTOR (IGUAL)
  bool motorDebeGirar =
    (digitalRead(PIN_BTN_STOP) != LOW) &&
    !motorBloqueado;

  if (motorDebeGirar) {
    analogWrite(PIN_MOTOR_PWMA, 240);
    if (motorStartMs == 0) motorStartMs = nowMs;
  } else {
    analogWrite(PIN_MOTOR_PWMA, 0);
    motorStartMs = 0;
  }

  if (motorStartMs > 0 &&
    (nowMs - motorStartMs) > MOTOR_TIMEOUT_MS) {

  motorBloqueado = true;
  analogWrite(PIN_MOTOR_PWMA, 0);
}


  // ---- SERVO MINUTOS (IGUAL)
  if (currentMinute != lastMinute) {
    lastMinute = currentMinute;
    servoMotor.write(SERVO_MOTOR_REPOSO - SERVO_MOTOR_DELTA);
    servoMotorReturn = nowMs + 2000;
  }

  if (servoMotorReturn && nowMs > servoMotorReturn) {
    servoMotor.write(SERVO_MOTOR_REPOSO);
    servoMotorReturn = 0;
  }

  // ---- FLIPPER (IGUAL)
  if (!flipperHechoHora &&
      prevMinute == 29 &&
      currentMinute == 30) {

    flipperHechoHora = true;
    flipperAbierto = true;
    bolasContadas = 0;
    bolasObjetivo = bolasPorHora(now.hour());
    lastBallMs = 0;
    servoFlipper.write(53);
  }

  if (now.hour() != lastHour) {
    lastHour = now.hour();
    flipperHechoHora = false;
    campanadaHechaHora = false;
  }

  // ---- CONTEO BOLAS (IGUAL)
  if (flipperAbierto &&
      botonPulsado(BTN_CB, PIN_BTN_CUENTABOLAS) &&
      (nowMs - lastBallMs) > BALL_GUARD_MS) {

    lastBallMs = nowMs;
    bolasContadas++;

    if (bolasContadas >= bolasObjetivo) {
      flipperAbierto = false;
      servoFlipper.write(0);
    }
  }

  // ---- CAMPANADAS (IGUAL)
  if (!campanadaHechaHora &&
      currentMinute == 0 &&
      now.second() == CAMPANADA_DELAY_SEG) {

    servoCampanadas.write(55);
    servoCampReturn = nowMs + 12000;
    campanadaHechaHora = true;
  }

  if (servoCampReturn && nowMs > servoCampReturn) {
    servoCampanadas.write(0);
    servoCampReturn = 0;
  }

  // =================================================
  // OLED (SUSTITUYE SOLO LCD)
  // =================================================
  if (nowMs - lcdLastUpdate > LCD_UPDATE_MS) {

    display.clearDisplay();

    // Hora con segundos
    display.setCursor(0, 0);
    char buf0[20];
    snprintf(buf0, sizeof(buf0),
             "%02d:%02d:%02d",
             now.hour(), currentMinute, now.second());
    display.print(buf0);

    // Motor
    display.setCursor(0, 10);
    if (motorBloqueado) display.print("MOTOR BLOQUEADO");
    else display.print(digitalRead(PIN_BTN_STOP) == LOW ? "MOTOR STOP" : "MOTOR RUN");

   // BOLAS (LÓGICA CORRECTA DEFINIDA POR TI)
display.setCursor(0, 20);

int objetivoHora = bolasPorHora(now.hour());

display.print("BOLAS ");

if (currentMinute < 30) {
  // Antes de las :30 → siempre 0/N
  display.print("0/");
  display.print(objetivoHora);
} else {
  // Después de las :30 → conteo real
  display.print(bolasContadas);
  display.print("/");
  display.print(bolasObjetivo);
}


    display.display();
    lcdLastUpdate = nowMs;
  }

  prevMinute = currentMinute;
}
