#include <Servo.h>
#include <ESP8266WiFi.h>
#include <WiFiClientSecureBearSSL.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>

// ================================================================
//  OTA CONFIG
// ================================================================
#define FW_VERSION   1
#define GITEA_HOST   "https://git.nacho.myds.me"
#define GITEA_OWNER  "Natxo"
#define GITEA_REPO   "Arduino-Car"
#define GITEA_BRANCH "main"
// WiFi — mismas credenciales que usa el ESP32-CAM
#define WIFI_SSID    "Natxo"
#define WIFI_PASS    "Suprak1llm1ndS23S"
// ================================================================

// ==== Pines motores ====
const int ENA = D1;
const int ENB = D5;
const int IN1 = D6;
const int IN2 = D7;
const int IN3 = D2;
const int IN4 = D0;

// ==== Pines servos ====
const int SERVO_X_PIN = D3;
const int SERVO_Y_PIN = D4;

// ==== Pines buzzer ====
const int buzzerPin = D8;

Servo servoX;
Servo servoY;

int currentSpeed = 150;
int posX = 90;
int posY = 90;

// ── OTA: comprueba si hay firmware nuevo y actualiza si hace falta ──
static void checkOTA() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);

  int tries = 0;
  while (WiFi.status() != WL_CONNECTED && tries < 20) {
    delay(500);
    tries++;
  }

  if (WiFi.status() != WL_CONNECTED) {
    WiFi.disconnect();
    WiFi.mode(WIFI_OFF);
    return;   // sin WiFi, arranque normal
  }

  BearSSL::WiFiClientSecure client;
  client.setInsecure();   // cert Let's Encrypt/autofirmado — OK para LAN privada
  HTTPClient http;

  String base = String(GITEA_HOST) + "/" + GITEA_OWNER + "/" + GITEA_REPO
              + "/raw/branch/" + GITEA_BRANCH + "/firmware/esp8266/";

  http.begin(client, base + "version.txt");
  http.setTimeout(5000);
  int code = http.GET();
  String body;
  if (code == HTTP_CODE_OK) body = http.getString();
  http.end();

  if (code == HTTP_CODE_OK) {
    body.trim();
    int remoteVer = body.toInt();
    if (remoteVer > FW_VERSION) {
      BearSSL::WiFiClientSecure updateClient;
      updateClient.setInsecure();
      ESPhttpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
      ESPhttpUpdate.rebootOnUpdate(true);
      ESPhttpUpdate.update(updateClient, base + "firmware.bin");
      // Si llega aquí, el update falló — continúa arranque normal
    }
  }

  WiFi.disconnect();
  WiFi.mode(WIFI_OFF);   // apaga radio para ahorrar energía
}

void setup() {
  // OTA primero (usa WiFi)
  checkOTA();

  // Esperar hasta que hayan pasado ~15s desde el arranque
  // para dar tiempo al ESP32-CAM a conectarse y levantar el servidor
  unsigned long elapsed = millis();
  if (elapsed < 15000) {
    delay(15000 - elapsed);
  }

  Serial.begin(9600);

  pinMode(ENA, OUTPUT);
  pinMode(ENB, OUTPUT);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);

  stopMotors();

  analogWriteRange(255);
  analogWriteFreq(1000);

  servoX.attach(SERVO_X_PIN);
  servoY.attach(SERVO_Y_PIN);

  pinMode(buzzerPin, OUTPUT);

  servoX.write(posX);
  servoY.write(posY);
}

void loop() {
  if (Serial.available()) {
    char c = Serial.read();

    if      (c == 'F') forward();
    else if (c == 'B') backward();
    else if (c == 'L') left();
    else if (c == 'R') right();
    else if (c == 'S') stopMotors();

    else if (c == 'V') {
      int val = Serial.parseInt();
      if (val >= 0 && val <= 255) currentSpeed = val;
    }
    else if (c == 'X') {
      int val = Serial.parseInt();
      if (val >= 0 && val <= 180) { posX = val; servoX.write(posX); }
    }
    else if (c == 'Y') {
      int val = Serial.parseInt();
      if (val >= 0 && val <= 180) { posY = val; servoY.write(posY); }
    }
    else if (c == 'H') {
      int val = Serial.parseInt();
      digitalWrite(buzzerPin, val > 0 ? HIGH : LOW);
    }
  }
}

// ==== Funciones de movimiento ====
void forward() {
  analogWrite(ENA, currentSpeed);
  analogWrite(ENB, currentSpeed);
  digitalWrite(IN1, LOW);  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, LOW);  digitalWrite(IN4, HIGH);
}

void backward() {
  analogWrite(ENA, currentSpeed);
  analogWrite(ENB, currentSpeed);
  digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW);
  digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW);
}

void left() {
  analogWrite(ENA, currentSpeed);
  analogWrite(ENB, currentSpeed);
  digitalWrite(IN1, LOW);  digitalWrite(IN2, HIGH);
  digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW);
}

void right() {
  analogWrite(ENA, currentSpeed);
  analogWrite(ENB, currentSpeed);
  digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);  digitalWrite(IN4, HIGH);
}

void stopMotors() {
  analogWrite(ENA, 0);
  analogWrite(ENB, 0);
  digitalWrite(IN1, LOW); digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW); digitalWrite(IN4, LOW);
}
