#define CAMERA_DEBUG 0
#include "esp_camera.h"
#include <WiFi.h>
#include <WiFiManager.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
#include <WiFiClientSecure.h>
#include "board_config.h"

// ================================================================
//  OTA CONFIG
// ================================================================
#define FW_VERSION   18
#define GITEA_HOST   "https://git.nacho.myds.me"
#define GITEA_OWNER  "Natxo"
#define GITEA_REPO   "Arduino-Car"
#define GITEA_BRANCH "main"
#define GITEA_TOKEN  "d3b0b32eb0311c9b4ef569f22c8392c373ff3f9f"
// ================================================================

const char *ssid     = "Natxo";
const char *password = "Suprak1llm1ndS23S";

void startCameraServer();
void setupLedFlash();

static void handshakeTask(void *) {
  unsigned long deadline = millis() + 30000UL;
  String buf = "";
  while (millis() < deadline) {
    Serial.print("GO\n");
    unsigned long t = millis();
    while (millis() - t < 300) {
      while (Serial.available()) {
        char c = Serial.read();
        if (c == '\n') {
          buf.trim();
          if (buf == "OK") { vTaskDelete(NULL); return; }
          buf = "";
        } else if (c != '\r' && buf.length() < 8) {
          buf += c;
        }
      }
      vTaskDelay(pdMS_TO_TICKS(5));
    }
  }
  vTaskDelete(NULL);
}


// ── OTA: comprueba si hay firmware nuevo y actualiza si hace falta ──
static void checkOTA() {
  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/esp32cam/";
  String token = "?token=" + String(GITEA_TOKEN);

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

  if (code != HTTP_CODE_OK) return;   // Gitea no disponible, continúa normal

  body.trim();
  int remoteVer = body.toInt();
  if (remoteVer <= FW_VERSION) return; // ya tenemos la última versión

  // 3 pitidos = ESP32-CAM actualizando (via Serial al ESP8266)
  for (int i = 0; i < 3; i++) {
    Serial.print("H1");
    delay(150);
    Serial.print("H0");
    delay(150);
  }
  delay(300);

  // Hay versión nueva → descargar y flashear
  WiFiClientSecure updateClient;
  updateClient.setInsecure();
  httpUpdate.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
  httpUpdate.rebootOnUpdate(true);
  httpUpdate.update(updateClient, base + "firmware.bin" + token);
  // Si llega aquí, el update falló — continúa arranque normal
}

void setup() {
  Serial.begin(9600);
  Serial.setDebugOutput(false);

  // ── Cámara ──────────────────────────────────────────────────────
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer   = LEDC_TIMER_0;
  config.pin_d0       = Y2_GPIO_NUM;
  config.pin_d1       = Y3_GPIO_NUM;
  config.pin_d2       = Y4_GPIO_NUM;
  config.pin_d3       = Y5_GPIO_NUM;
  config.pin_d4       = Y6_GPIO_NUM;
  config.pin_d5       = Y7_GPIO_NUM;
  config.pin_d6       = Y8_GPIO_NUM;
  config.pin_d7       = Y9_GPIO_NUM;
  config.pin_xclk     = XCLK_GPIO_NUM;
  config.pin_pclk     = PCLK_GPIO_NUM;
  config.pin_vsync    = VSYNC_GPIO_NUM;
  config.pin_href     = HREF_GPIO_NUM;
  config.pin_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn     = PWDN_GPIO_NUM;
  config.pin_reset    = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.frame_size   = FRAMESIZE_UXGA;
  config.pixel_format = PIXFORMAT_JPEG;
  config.grab_mode    = CAMERA_GRAB_LATEST;   // siempre el frame más reciente → menos lag
  config.fb_location  = CAMERA_FB_IN_PSRAM;
  config.jpeg_quality = 12;
  config.fb_count     = 1;

  if (config.pixel_format == PIXFORMAT_JPEG) {
    if (psramFound()) {
      config.jpeg_quality = 10;
      config.fb_count     = 2;
    } else {
      config.frame_size  = FRAMESIZE_SVGA;
      config.fb_location = CAMERA_FB_IN_DRAM;
      config.fb_count    = 2;
      config.jpeg_quality = 18;
    }
  }

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) return;

  sensor_t *s = esp_camera_sensor_get();
  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1);
    s->set_brightness(s, 1);
    s->set_saturation(s, -2);
  }
  if (config.pixel_format == PIXFORMAT_JPEG) {
    s->set_framesize(s, FRAMESIZE_QVGA);
  }

#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
  s->set_vflip(s, 1);
  s->set_hmirror(s, 1);
#endif
#if defined(CAMERA_MODEL_ESP32S3_EYE)
  s->set_vflip(s, 1);
#endif

#if defined(LED_GPIO_NUM)
  setupLedFlash();
#endif

  // ── WiFi ─────────────────────────────────────────────────────────
  WiFiManager wm;
  wm.setDebugOutput(false);
  if (!wm.autoConnect("CocheRC", "12345678")) {
    ESP.restart();
  }

  // ── OTA (después de WiFi, antes de iniciar el servidor) ──────────
  checkOTA();

  // ── Servidor cámara ───────────────────────────────────────────────
  startCameraServer();

  xTaskCreate(handshakeTask, "handshake", 4096, NULL, 1, NULL);
}

void loop() {
  delay(10000);
}
