#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <AsyncUDP.h> // ✅【改动 1】替换头文件
#include "HardwareSerial.h"
#include "esp_camera.h"
#include "esp_heap_caps.h"
#include <freertos/queue.h>
#include <Preferences.h>
#include <esp_now.h>
#include <esp_wifi.h>

// ===== 修正后的管脚配置（严格匹配ESP32-S3-WROOM-1-N16R8）=====
#define PWDN_GPIO_NUM    -1
#define RESET_GPIO_NUM   -1
#define XCLK_GPIO_NUM     9
#define SIOD_GPIO_NUM    14  // SCCB SDA
#define SIOC_GPIO_NUM    13  // SCCB SCL
#define Y9_GPIO_NUM      3   // CSI_D7
#define Y8_GPIO_NUM      10  // CSI_D6
#define Y7_GPIO_NUM      8   // CSI_D5
#define Y6_GPIO_NUM      17  // CSI_D4
#define Y5_GPIO_NUM      15  // CSI_D3
#define Y4_GPIO_NUM      6   // CSI_D2
#define Y3_GPIO_NUM      7   // CSI_D1
#define Y2_GPIO_NUM      16  // CSI_D0
#define VSYNC_GPIO_NUM   12  // CSI_VSYNC
#define HREF_GPIO_NUM    11  // CSI_HREF
#define PCLK_GPIO_NUM    18  // CSI_PCLK

// ===== SVGA 流传输配置 =====
const framesize_t STREAM_RESOLUTION = FRAMESIZE_XGA;
const uint32_t STREAM_XCLK_FREQ = 20000000;
const uint8_t STREAM_JPEG_QUALITY = 10;

// ===== 并行架构配置 =====
#define FRAME_QUEUE_SIZE 2
QueueHandle_t frame_queue = NULL;
volatile bool has_active_client = false;

// ===== 系统配置 =====
String ssid = "ESP32-S3-Camera";
String password = "12345678";
const unsigned long WIFI_TIMEOUT_MS = 15000;
const uint16_t WEB_SERVER_PORT = 80;

// ✅【改动 2】修改 UDP 对象定义，移除缓冲区变量 (AsyncUDP 不需要)
const uint16_t UDP_PORT = 8888;
AsyncUDP udp; 
// 以下变量不再需要，因为数据在回调中直接处理
// WiFiUDP udpServer; 
// char udpPacketBuffer[256]; 
// String udpReceiveBuffer = ""; 

// 为了在回调中使用，我们需要一个全局的缓冲区字符串 (注意：回调中操作 String 需小心，但此处逻辑简单可行)
String udpReceiveBuffer = ""; 

WebServer server(WEB_SERVER_PORT);

// ===== 帧率统计 =====
volatile float capture_fps = 0.0;
volatile float transmit_fps = 0.0;
static uint32_t failed_frames = 0;

// ✅【改动 3】新增 UDP 回调函数 (原 loop 中的逻辑移至此)
// 此函数在网络数据到达时自动触发，不占用 loop 时间，不阻塞推流
void onUdpPacket(AsyncUDPPacket packet) {
  if (packet.isBroadcast() || packet.isMulticast()) return;

  String payload = "";
  for (size_t i = 0; i < packet.length(); i++) {
    payload += (char)packet.data()[i];
  }

  // 激光控制
  if (payload.endsWith("LASEROFF")) {
    digitalWrite(2, HIGH);
    return;
  }
  if (payload.endsWith("LASERON")) {
    digitalWrite(2, LOW);
    return;
  }

  // 串口转发（截断长消息）
  if(payload.endsWith("ZHY")||payload.endsWith("\n"))
  {
   Serial.println(payload);
  }
  else if(payload.length() > 128) 
  { 
    payload = payload.substring(0, 128);
    Serial.println(payload);
  } 
} 

// ✅ 捕获任务（全力捕获模式）
void capture_task(void *pv) {
  static uint32_t cap_count = 0;
  static uint32_t last_time = 0;
  
  while (1) {
    if (!has_active_client) {
      vTaskDelay(pdMS_TO_TICKS(200));
      continue;
    }

    camera_fb_t *fb = esp_camera_fb_get();
    
    if (fb && fb->len > 1024 && fb->buf[0] == 0xFF && fb->buf[1] == 0xD8) {
      cap_count++;
      
      if (xQueueSend(frame_queue, &fb, 0) != pdTRUE) {
        esp_camera_fb_return(fb);
        failed_frames++;
      }
    } else {
      if (fb) esp_camera_fb_return(fb);
      failed_frames++;
    }
    
    if (millis() - last_time >= 1000) {
      capture_fps = (float)cap_count;
      cap_count = 0;
      last_time = millis();
    }
  }
}

// ✅ 高效帧发送
void sendMjpegFrame(WiFiClient &client, camera_fb_t *fb) {
  static char header[128];
  int len = sprintf(header,
    "--frame\r\n"
    "Content-Type: image/jpeg\r\n"
    "Content-Length: %u\r\n\r\n", fb->len);
  client.write((uint8_t*)header, len);
  client.write(fb->buf, fb->len);
  client.write((uint8_t*)"\r\n", 2);
  client.flush();
}

// ✅ 流处理（自适应传输帧率）- 保持阻塞模式
void handleStream() {
  has_active_client = true;
  Serial.println("✅ 客户端连接，开始推流");
  
  WiFiClient client = server.client();
  if (!client || !client.connected()) {
    Serial.println("⚠️  [STREAM] 客户端未连接");
    has_active_client = false;
    return;
  }

  client.setNoDelay(true);
  
  client.print("HTTP/1.1 200 OK\r\n");
  client.print("Content-Type: multipart/x-mixed-replace; boundary=frame\r\n");
  client.print("Cache-Control: no-cache\r\n");
  client.print("Pragma: no-cache\r\n");
  client.print("Connection: close\r\n");
  client.print("\r\n");

  uint32_t tx_count = 0;
  uint32_t last_fps_time = millis();
  
  float target_fps = 25.0;
  const float MIN_FPS = 15.0;
  const float MAX_FPS = 25.0;
  uint32_t next_frame_time = millis();

  while (client.connected()) {
    uint32_t now = millis();
    if (now < next_frame_time) {
      delayMicroseconds(300);
      continue;
    }

    camera_fb_t *fb = NULL;
    if (xQueueReceive(frame_queue, &fb, 0) == pdTRUE) {
      uint32_t send_start = millis();
      sendMjpegFrame(client, fb);
      esp_camera_fb_return(fb);
      uint32_t send_duration = millis() - send_start;

      tx_count++;
      
      if (send_duration > 45) {
        target_fps = max(MIN_FPS, target_fps);
      } else if (send_duration < 30) {
        target_fps = min(MAX_FPS, target_fps);
      }
      
      uint32_t target_interval = (uint32_t)(1000.0f / target_fps);
      next_frame_time = now + target_interval;
      if (next_frame_time < now) next_frame_time = now + 5;

    } else {
      next_frame_time = now + 2;
    }

    if (!client.connected()) break;
  }

  if (client.connected()) {
    client.write((const uint8_t*)"--frame--\r\n", 11);
    client.stop();
  }
  
  has_active_client = false;
  Serial.println("⚠️  客户端断开，暂停捕获");
}

bool verifyPsram() {
  Serial.println("\n=== PSRAM 验证 ===");
  if (!psramFound()) {
    Serial.println("❌ PSRAM 未检测到");
    return false;
  }
  
  size_t psram_size = ESP.getPsramSize();
  Serial.printf("✅ PSRAM 容量：%.2f MB\n", psram_size / 1024.0 / 1024.0);
  
  uint8_t* test = (uint8_t*)ps_malloc(1024 * 1024);
  if (!test) {
    Serial.println("❌ 无法分配 1MB PSRAM 内存");
    return false;
  }
  
  for (size_t i = 0; i < 1024; i++) test[i] = i & 0xFF;
  bool valid = true;
  for (size_t i = 0; i < 1024; i++) {
    if (test[i] != (i & 0xFF)) {
      valid = false;
      break;
    }
  }
  
  heap_caps_free(test);
  
  if (valid) {
    Serial.println("✅ PSRAM 读写验证通过");
    return true;
  } else {
    Serial.println("❌ PSRAM 读写错误");
    return false;
  }
}

bool initCamera() {  
  pinMode(21, OUTPUT);
  pinMode(2, OUTPUT);
  digitalWrite(21, LOW);
  digitalWrite(2, HIGH);

  camera_config_t config = {
    .pin_pwdn = PWDN_GPIO_NUM,
    .pin_reset = RESET_GPIO_NUM,
    .pin_xclk = XCLK_GPIO_NUM,
    .pin_sccb_sda = SIOD_GPIO_NUM,
    .pin_sccb_scl = SIOC_GPIO_NUM,
    .pin_d7 = Y9_GPIO_NUM,
    .pin_d6 = Y8_GPIO_NUM,
    .pin_d5 = Y7_GPIO_NUM,
    .pin_d4 = Y6_GPIO_NUM,
    .pin_d3 = Y5_GPIO_NUM,
    .pin_d2 = Y4_GPIO_NUM,
    .pin_d1 = Y3_GPIO_NUM,
    .pin_d0 = Y2_GPIO_NUM,
    .pin_vsync = VSYNC_GPIO_NUM,
    .pin_href = HREF_GPIO_NUM,
    .pin_pclk = PCLK_GPIO_NUM,
    .xclk_freq_hz = STREAM_XCLK_FREQ,
    .ledc_timer = LEDC_TIMER_0,
    .ledc_channel = LEDC_CHANNEL_0,
    .pixel_format = PIXFORMAT_JPEG,
    .frame_size = STREAM_RESOLUTION,
    .jpeg_quality = STREAM_JPEG_QUALITY,
    .fb_count = 3,
    .fb_location = CAMERA_FB_IN_PSRAM,
    .grab_mode = CAMERA_GRAB_LATEST
  };

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("❌ 摄像头初始化失败 (0x%X)\n", err);
    return false;
  }
  
  sensor_t *s = esp_camera_sensor_get();
  if (s) {
    s->set_hmirror(s, 0);
    s->set_vflip(s, 1);
    s->set_exposure_ctrl(s, 0);
    s->set_aec2(s, 0);
    s->set_gain_ctrl(s, 1);
    s->set_whitebal(s, 1);
    s->set_awb_gain(s, 0);
    s->set_aec_value(s, 200);
    s->set_wb_mode(s, 0);
    s->set_brightness(s, 0);
    s->set_contrast(s, 0);
    s->set_saturation(s, 2);
    s->set_framesize(s, STREAM_RESOLUTION);
    s->set_quality(s, STREAM_JPEG_QUALITY);
  }
  Serial.println("✅ 摄像头初始化成功");
  Serial.printf("⚡ XCLK: %d MHz | JPEG质量：%d | 缓冲区：3帧\n", 
                STREAM_XCLK_FREQ / 1000000, STREAM_JPEG_QUALITY);  
  return true;
}

void setup() { 
  Serial.begin(1000000);

  // 锁定 CPU 最高主频，确保流媒体性能
  setCpuFrequencyMhz(240);
  Serial.printf("⚡ CPU 主频: %d MHz\n", getCpuFrequencyMhz());
  
  Preferences prefs;
  prefs.begin("wifi_cfg", false);
  // String saved_ssid = prefs.getString("ssid", "FS");
  // String saved_pwd = prefs.getString("pwd", "zcw666666");
  String saved_ssid = "FS";
  String saved_pwd = "zcw666666";
  if (saved_ssid.length() > 0 && saved_ssid.length() <= 32) ssid = saved_ssid;
  if (saved_pwd.length() >= 8 && saved_pwd.length() <= 64) password = saved_pwd;
  prefs.end();
  
  Serial.println("\n");
  Serial.println("╔══════════════════════════════════════════════════════════════════════╗");
  Serial.println("║  ESP32-S3 + OV2640 双核并行流系统 v4.0 (STA 模式)             ║");
  Serial.println("╚══════════════════════════════════════════════════════════════════════╝");
  Serial.println("💡 串口指令: WIFI:名称:密码  (例如: WIFI:LabCam:Secure1234)");



  frame_queue = xQueueCreate(FRAME_QUEUE_SIZE, sizeof(camera_fb_t*));
  if (!frame_queue) {
    Serial.println("❌ 队列创建失败");
    while (1) delay(1000);
  }
  Serial.printf("✅ 帧队列创建成功 (深度=%d)\n", FRAME_QUEUE_SIZE);
  
  Serial.println("\n[1] PSRAM 验证...");
  if (!verifyPsram()) {
    Serial.println("❌ PSRAM 不可用，系统无法运行");
    while (1) delay(1000);
  }
  
  Serial.println("\n[2] WiFi 初始化 (STA 模式)...");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid.c_str(), password.c_str());

  Serial.printf("📡 连接中: %s", ssid.c_str());
  unsigned long startAttempt = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - startAttempt < WIFI_TIMEOUT_MS) {
    delay(500);
    Serial.print(".");
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\n✅ WiFi 已连接");
    WiFi.setSleep(false);
    Serial.print("📶 SSID: "); Serial.println(ssid);
    Serial.print("🌐 IP:   "); Serial.println(WiFi.localIP());
    Serial.printf("🌐 访问：http://%s\n", WiFi.localIP().toString().c_str());
  } else {
    Serial.println("\n❌ STA 连接失败，切换到 AP 模式...");
    WiFi.mode(WIFI_AP);
    if (WiFi.softAP("ESP32-S3-Camera", "12345678")) {
      Serial.println("✅ AP 模式已启动");
      Serial.println("📶 SSID: ESP32-S3-Camera");
      Serial.println("🔑 密码: 12345678");
      Serial.print("🌐 IP:   "); Serial.println(WiFi.localIP());
      Serial.printf("🌐 访问：http://%s\n", WiFi.localIP().toString().c_str());
    } else {
      Serial.println("❌ AP 启动失败，继续运行 (无网络)");
    }
  }
  
  Serial.println("\n[3] 摄像头初始化 (SVGA)...");
  if (!initCamera()) {
    Serial.println("❌ 摄像头初始化失败，系统退出");
    while (1) delay(1000);
  }
  
  xTaskCreatePinnedToCore(
    capture_task, "cam_capture", 3072, NULL, 5, NULL, 0
  );
  Serial.println("✅ 捕获任务已启动 (Core 0)");
  
  Serial.println("\n[4] WebServer 路由配置...");
  
  server.on("/", []() {
    String html = 
      "<!DOCTYPE html>"
      "<html lang=\"zh-CN\">"
      "<head>"
        "<meta charset=\"UTF-8\">"
        "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
        "<title>ESP32-S3 摄像头</title>"
        "<style>"
          "body{font-family:'Microsoft YaHei',Arial,sans-serif;padding:20px;background:#f5f5f5;margin:0}"
          "header{background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:white;padding:30px;text-align:center;border-radius:0 0 20px 20px;margin-bottom:25px;box-shadow:0 4px 12px rgba(0,0,0,0.1)}"
          "h1{margin:0;font-size:28px}"
          ".subtitle{margin-top:8px;opacity:0.9;font-size:16px}"
          ".camera-container{background:white;padding:20px;border-radius:12px;box-shadow:0 4px 12px rgba(0,0,0,0.1);margin-bottom:25px;text-align:center}"
          ".camera-container img{max-width:100%;height:auto;border-radius:8px}"
          ".footer{text-align:center;margin-top:25px;color:#666;font-size:14px;padding:15px;border-top:1px solid #eee}"
        "</style>"
      "</head>"
      "<body>"
        "<header>"
          "<h1>📸 局域物联网-" + String(ssid) + "</h1>"
          "<div class=\"subtitle\">ESP32-S3 双核并行摄像头 (STA 模式)</div>"
        "</header>"
        
        "<div class=\"camera-container\">"
          "<img src=\"/stream\" id=\"camera-stream\" alt=\"实时画面\">"
        "</div>"
        
        "<div class=\"footer\">"
          "<p>ESP32-S3 双核流系统 | STA 模式</p>"
        "</div>"
        
        "<script>"
          "const img = document.getElementById('camera-stream');"
          "img.onerror = () => {"
          "  console.log('Stream error, reconnecting...');"
          "  setTimeout(() => img.src = '/stream?' + Date.now(), 3000);"
          "};"
        "</script>"
      "</body>"
      "</html>";
    
    server.send(200, "text/html; charset=utf-8", html);
    Serial.println("✅ 首页响应已发送");
  });

  server.on("/stream", handleStream);

  server.on("/status", []() {
    char json_buffer[180];
    snprintf(json_buffer, sizeof(json_buffer),
      "{\"clients\":%d,\"capture_fps\":%.1f,\"transmit_fps\":%.1f,\"failed\":%lu,\"free_psram\":%lu,\"has_client\":%s}",
      (WiFi.status() == WL_CONNECTED) ? 1 : 0,
      capture_fps,
      transmit_fps,
      failed_frames,
      ESP.getFreePsram() / 1024,
      has_active_client ? "true" : "false"
    );
    server.send(200, "application/json", json_buffer);
  });

  server.onNotFound([]() {
    server.send(404, "text/plain; charset=utf-8", "404 - Not Found");
  });

  server.begin();
  Serial.printf("\n✅ Web 服务器已启动（端口 %d）\n", WEB_SERVER_PORT);
  
  // ✅【改动 4】AsyncUDP 初始化 (替换原来的 udpServer.begin)
  if (udp.listen(UDP_PORT)) {
    Serial.printf("✅ AsyncUDP 已监听端口 %d (中断回调模式)\n", UDP_PORT);
    // 注册回调函数
    udp.onPacket(onUdpPacket);
  } else {
    Serial.println("❌ AsyncUDP 监听失败");
  }

  Serial.printf("✅ 系统就绪！访问 http://%s\n\n", WiFi.localIP().toString().c_str());
  Serial.println("💡 提示：UDP 数据由中断处理，推流不阻塞通讯。");
  
}

void loop() {
  server.handleClient();
  // ✅【改动 5】删除了所有 udpServer.parsePacket() 相关代码
  // 现在 UDP 处理完全在 onUdpPacket 回调中自动完成，loop 中无需任何操作
  
  static uint32_t last_report = 0;
  // 系统状态报告可根据需要开启
  // if (millis() - last_report >= 20000) { ... }
  
  if (Serial.available()) {
    String cmd = Serial.readStringUntil('\n');
    cmd.trim();
    
    if (cmd.startsWith("WIFI:")) {
      int firstColon = cmd.indexOf(':', 5);
      if (firstColon != -1) {
        String new_ssid = cmd.substring(5, firstColon);
        String new_pwd = cmd.substring(firstColon + 1);
        
        if (new_ssid.length() >= 1 && new_ssid.length() <= 32 && 
            new_pwd.length() >= 8 && new_pwd.length() <= 64) {
          
          Preferences prefs;
          prefs.begin("wifi_cfg", false);
          prefs.putString("ssid", new_ssid);
          prefs.putString("pwd", new_pwd);
          prefs.end();
          
          Serial.println("\n✅ 配置已保存，2秒后自动重启...");
          Serial.printf("   SSID: %s\n", new_ssid.c_str());
          Serial.printf("   密码: %s\n", new_pwd.c_str());
          delay(2000);
          ESP.restart();
        } else {
          Serial.println("❌ 错误：SSID(1-32字符) 密码(8-64字符)");
        }
      } else {
        Serial.println("❌ 格式错误：请使用 'wifi:名称:密码' 格式");
      }
    }
  }
  
  delay(1);
}










// void onUdpPacket(AsyncUDPPacket packet) {
//   if (packet.isBroadcast() || packet.isMulticast()) return;

//   String payload = "";
//   for (size_t i = 0; i < packet.length(); i++) {
//     payload += (char)packet.data()[i];
//   }

//   // 激光控制
//   if (payload.endsWith("LASEROFF")) {
//     digitalWrite(2, HIGH);
//     return;
//   }
//   if (payload.endsWith("LASERON")) {
//     digitalWrite(2, LOW);
//     return;
//   }

//   // 串口转发（截断长消息）
//   if(payload.endsWith("ZHY")||payload.endsWith("\n"))
//   {
//    Serial.println(payload);
//   }
//   else if(payload.length() > 128) 
//   { 
//     payload = payload.substring(0, 128);
//     Serial.println(payload);
//   } 
// }



// if (Serial.available()) {
//     String cmd = Serial.readStringUntil('\n');
//     cmd.trim();
    
//     if (cmd.startsWith("WIFI:")) {
//       int firstColon = cmd.indexOf(':', 5);
//       if (firstColon != -1) {
//         String new_ssid = cmd.substring(5, firstColon);
//         String new_pwd = cmd.substring(firstColon + 1);
        
//         if (new_ssid.length() >= 1 && new_ssid.length() <= 32 && 
//             new_pwd.length() >= 8 && new_pwd.length() <= 64) {
          
//           Preferences prefs;
//           prefs.begin("wifi_cfg", false);
//           prefs.putString("ssid", new_ssid);
//           prefs.putString("pwd", new_pwd);
//           prefs.end();
          
//           Serial.println("\n✅ 配置已保存，2秒后自动重启...");
//           Serial.printf("   SSID: %s\n", new_ssid.c_str());
//           Serial.printf("   密码: %s\n", new_pwd.c_str());
//           delay(2000);
//           ESP.restart();
//         } else {
//           Serial.println("❌ 错误：SSID(1-32字符) 密码(8-64字符)");
//         }
//       } else {
//         Serial.println("❌ 格式错误：请使用 'wifi:名称:密码' 格式");
//       }
//     }
//   }








  
  