/**
 * @file demo_main.c
 * @brief TCP API 调用演示客户端 (一维/二维通用)
 *
 * 演示使用 QDXnetworkStack API 层的零拷贝发送与参数回调机制。
 * 用户操作流程与原 tcp_c_demo 保持完全一致。
 */

#include "qdx_port.h"
#include "qdx_preprocess.h"
#include "qdx_protocol.h"
#include "qdx_tcp_logic.h"

#include <conio.h> /* _kbhit, _getch */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

/* ============================================================
 * 宏定义与全局变量
 * ============================================================ */

/* 默认服务端连接参数（硬编码） */
#define DEFAULT_SERVER_IP "127.0.0.1"
#define DEFAULT_CONTROL_PORT 5511
#define DEFAULT_DATA_PORT 5512

/* ANSI 控制台颜色宏定义 */
#define CLR_RESET "\033[0m"
#define CLR_RED "\033[31m"
#define CLR_GREEN "\033[32m"
#define CLR_YELLOW "\033[33m"
#define CLR_BLUE "\033[34m"
#define CLR_MAGENTA "\033[35m"
#define CLR_CYAN "\033[36m"
#define CLR_DIM "\033[90m"

static uint8_t g_dimension_mode = 0; /* 0=1D, 1=2D */
static char g_matrix_dir[260] = {0};

/* 发送缓冲区（静态分配，避免 malloc） */
#define MAX_2D_PIXELS (256 * 256)
static uint16_t g_raw_matrix[MAX_2D_PIXELS];

/* API 要求的外部传输缓冲区，总大小 256KB，前置留出 1KB 给网络层附加头部 */
#define TX_BUFFER_TOTAL_CAPACITY (256 * 1024)
#define TX_BUFFER_HEAD_OFFSET 1024
static uint8_t g_tx_buffer[TX_BUFFER_TOTAL_CAPACITY];
static TcpTxBuffer_t g_api_tx_buffer;

/* ============================================================
 * 辅助函数：十六进制打印
 * ============================================================ */
static void print_hex(const uint8_t *data, int len) {
  (void)data;
  (void)len;
}

/* ============================================================
 * 模拟数据发送逻辑 (1D & 2D) 的前置声明
 * ============================================================ */
static void simulate_send_2d_frame(uint32_t frameNum);
static void simulate_send_1d_frame(uint32_t frameNum);

/* ============================================================
 * QDX API 回调函数实现
 * ============================================================ */

/**
 * @brief 上位机下发配置更新时的回调
 */
static void on_config_updated(const ConfigCommon_t *common,
                              const Config2D_t *cfg2d,
                              const Config1D_t *cfg1d) {
  printf(CLR_CYAN "\n[API Callback] 收到最新配置" CLR_RESET "\n");

  /* 打印通用配置 */
  printf("    -> Common: Pipeline: %.*s, Type: %d, Mode: %d, Tag: %d, "
         "Strictness: %d, Custom: %d\n",
         16, common->PipelineId, common->PipelineType, common->WorkMode,
         common->ConfigTag, common->StrictnessLevel, common->IsCustomMode);

  /* 打印 2D 配置 */
  printf("    -> 2D: Enabled: %d, Live: %d, DevId: %d, %dx%d, Fps: %d\n",
         cfg2d->Enabled, cfg2d->IsLive, cfg2d->DeviceId, cfg2d->Width,
         cfg2d->Height, cfg2d->Fps);
  printf("    -> 2D: Mask: %d (Thresh: %d, %dx%d), Target: %dx%d\n",
         cfg2d->MaskEnabled, cfg2d->MaskThreshold, cfg2d->MaskWidth,
         cfg2d->MaskHeight, cfg2d->TargetWidth, cfg2d->TargetHeight);

  /* 打印 1D 配置 */
  printf("    -> 1D: Enabled: %d, RunMode: %d, TriggerType: %d, "
         "BufferSize: %d\n",
         cfg1d->Enabled, cfg1d->RunMode, cfg1d->TriggerType, cfg1d->BufferSize);

  /* 核心：将配置下发给预处理算法库 */
  Preprocess_Settings_Change(cfg2d, cfg1d, common);
  printf(CLR_GREEN "    -> [OK] 已将参数同步至预处理引擎" CLR_RESET "\n");
}

/**
 * @brief 上位机反馈检测结果时的回调
 */
static void on_detection_result(uint32_t frameNumber, uint8_t resultStatus) {
  printf(CLR_CYAN "[API Callback] 收到检测结果: Frame #%u, Result: %s" CLR_RESET
                  "\n",
         frameNumber, resultStatus == 0 ? "OK" : "NG");
}

/**
 * @brief 当服务端下发 TempFrame（请求回传当前帧）时的回调
 */
static void on_temp_frame_request(uint8_t is2dRequest) {
  printf(CLR_CYAN
         "[API Callback] 收到服务端 TempFrame 采集请求 (is2D=%d)" CLR_RESET
         "\n",
         is2dRequest);

  static uint32_t simulated_frame_num = 1;

  if (g_dimension_mode == 1) {
    printf(CLR_MAGENTA "    -> 模拟: 开始处理并回传 2D 阵列图像..." CLR_RESET
                       "\n");
    simulate_send_2d_frame(simulated_frame_num++);
  } else {
    printf(CLR_MAGENTA "    -> 模拟: 开始处理并回传 1D 阵列数据..." CLR_RESET
                       "\n");
    simulate_send_1d_frame(simulated_frame_num++);
  }
}

/* ============================================================
 * 模拟数据发送逻辑 (1D & 2D)
 * ============================================================ */

/**
 * @brief 随机选取 2D 矩阵文件并解析
 * 逻辑与原 demo 的 parse_temperature_matrix 完全一致
 */
static int load_random_2d_matrix(uint16_t *matrix, int max_pixels, int *out_w,
                                 int *out_h) {
  char pattern[300];
  snprintf(pattern, sizeof(pattern), "%s*.txt", g_matrix_dir);

  WIN32_FIND_DATAA fd;
  HANDLE hFind = FindFirstFileA(pattern, &fd);
  if (hFind == INVALID_HANDLE_VALUE)
    return -1;

  int count = 0;
  do {
    if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
      count++;
  } while (FindNextFileA(hFind, &fd));
  FindClose(hFind);

  if (count == 0)
    return -1;

  int target = rand() % count;
  hFind = FindFirstFileA(pattern, &fd);
  int idx = 0;
  char filepath[300] = {0};

  do {
    if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
      if (idx == target) {
        snprintf(filepath, sizeof(filepath), "%s%s", g_matrix_dir,
                 fd.cFileName);
        break;
      }
      idx++;
    }
  } while (FindNextFileA(hFind, &fd));
  FindClose(hFind);

  if (filepath[0] == '\0')
    return -1;

  static char file_buf[512 * 1024];
  FILE *fp = fopen(filepath, "r");
  if (!fp)
    return -1;

  int file_len = (int)fread(file_buf, 1, sizeof(file_buf) - 1, fp);
  fclose(fp);
  file_buf[file_len] = '\0';

  char *pos = strstr(file_buf, "\"temperature\"");
  if (!pos)
    return -1;
  pos = strchr(pos, '[');
  if (!pos)
    return -1;
  pos++;

  int width = 0, height = 0, total = 0, row_count = 0;

  while (*pos != '\0') {
    if (*pos == '[') {
      row_count = 0;
      pos++;
    } else if (*pos == ']') {
      if (row_count > 0) {
        height++;
        if (width == 0)
          width = row_count;
        row_count = 0;
      } else {
        break;
      }
      pos++;
    } else if (*pos >= '0' && *pos <= '9') {
      int val = 0;
      while (*pos >= '0' && *pos <= '9') {
        val = val * 10 + (*pos - '0');
        pos++;
      }
      if (*pos == '.') {
        pos++;
        while (*pos >= '0' && *pos <= '9')
          pos++;
      }
      if (total < max_pixels) {
        matrix[total] = (uint16_t)val;
      }
      total++;
      row_count++;
    } else {
      pos++;
    }
  }

  *out_w = width;
  *out_h = height;
  return total > 0 ? total : -1;
}

/**
 * @brief 模拟发送 2D 图像帧 (完整 API Zero-Copy 流水线)
 */
static void simulate_send_2d_frame(uint32_t frameNum) {
  int w = 0, h = 0;
  int total = load_random_2d_matrix(g_raw_matrix, MAX_2D_PIXELS, &w, &h);
  if (total <= 0) {
    printf(CLR_RED "[Error] 读取 2D 矩阵失败" CLR_RESET "\n");
    return;
  }

  /* 1. 构造原始图像结构体 */
  RawImageBuffer_t rawBuff = {.pData = g_raw_matrix,
                              .Width = (uint16_t)w,
                              .Height = (uint16_t)h,
                              .FrameNumber = frameNum};

  /* 2. 重置发送缓冲包装器（确保偏移正确） */
  g_api_tx_buffer.pBuffer = g_tx_buffer;
  g_api_tx_buffer.TotalCapacity = TX_BUFFER_TOTAL_CAPACITY;
  g_api_tx_buffer.HeadOffset = TX_BUFFER_HEAD_OFFSET;
  g_api_tx_buffer.ValidPayloadLen = 0;

  PreprocessResult_t resMeta = {0};

  /* 3. 核心 API: 预处理执行。它会根据滑动窗口目标大小提取 ROI，
   *    并写入我们提供的 g_tx_buffer[HeadOffset] 处。 */
  if (Preprocess_Execute(&rawBuff, &g_api_tx_buffer, &resMeta) == 0) {
    printf(CLR_MAGENTA
           "[Data] 预处理完成: 提取出 %dx%d ROI, %.1f~%.1f°C" CLR_RESET "\n",
           resMeta.ValidWidth, resMeta.ValidHeight, resMeta.MinTemp / 10.0f,
           resMeta.MaxTemp / 10.0f);

    /* 4. 核心 API: TCP 零拷贝图文打包与发送。
     *    内部会在 HeadOffset 前面的 1KB 预留空间中拼装 18 字节包头，直接压入
     * LwIP(WinSock) */
    int8_t err = TcpLogic_BuildAndSendTemperatureFrame(
        &g_api_tx_buffer, &resMeta, 0x01 /* TRIGGER */, 1 /* IS_2D */);

    if (err == 0) {
      printf(CLR_GREEN "       -> [OK] TCP 帧已加入发送队列" CLR_RESET "\n");
    } else {
      printf(CLR_RED "       -> [Fail] TCP 帧发送失败, 错误码: %d" CLR_RESET
                     "\n",
             err);
    }
  } else {
    printf(CLR_RED
           "[Data] 预处理拒绝了本帧数据 (未达到阈值或参数异常)" CLR_RESET "\n");
  }
}

/**
 * @brief 模拟发送 1D 温度帧
 * 1D 模式下不需要滑动窗口图像处理，我们可以跳过 Preprocess_Execute，
 * 直接组装 `PreprocessResult_t` 结构后提供给发送库。
 */
static void simulate_send_1d_frame(uint32_t frameNum) {
#define POINTS_COUNT 30

  int16_t min_t = 32767;
  int16_t max_t = -32768;
  int32_t sum_t = 0;

  g_api_tx_buffer.pBuffer = g_tx_buffer;
  g_api_tx_buffer.TotalCapacity = TX_BUFFER_TOTAL_CAPACITY;
  g_api_tx_buffer.HeadOffset = TX_BUFFER_HEAD_OFFSET;

  uint8_t *dest = g_api_tx_buffer.pBuffer + g_api_tx_buffer.HeadOffset;

  /* 模拟生成温度点 */
  for (int i = 0; i < POINTS_COUNT; i++) {
    uint16_t time_offset = (uint16_t)(i * 600 / (POINTS_COUNT - 1));
    uint16_t temp = (uint16_t)(500 + rand() % 501); /* 50.0~100.0 */

    int16_t t = (int16_t)temp;
    if (t < min_t)
      min_t = t;
    if (t > max_t)
      max_t = t;
    sum_t += t;

    /* 序列化到 buffer (按协议要求 TempPoint1D_t: 2字节时间偏移 +
     * 2字节温度，小端存储) */
    dest[0] = (uint8_t)(time_offset & 0xFF);
    dest[1] = (uint8_t)((time_offset >> 8) & 0xFF);
    dest[2] = (uint8_t)(temp & 0xFF);
    dest[3] = (uint8_t)((temp >> 8) & 0xFF);

    dest += 4;
  }

  g_api_tx_buffer.ValidPayloadLen = POINTS_COUNT * 4;

  /* 构造预处理元数据欺骗网络库 */
  PreprocessResult_t resMeta = {.pValidData = g_api_tx_buffer.pBuffer +
                                              g_api_tx_buffer.HeadOffset,
                                .DataLength = g_api_tx_buffer.ValidPayloadLen,
                                .ValidWidth = POINTS_COUNT,
                                .ValidHeight = 1,
                                .MinTemp = min_t,
                                .MaxTemp = max_t,
                                .AvgTemp = (int16_t)(sum_t / POINTS_COUNT),
                                .RoiTemp = (int16_t)(sum_t / POINTS_COUNT),
                                .Status = 0,
                                .FrameNumber = frameNum};

  printf(CLR_MAGENTA "[Data] 构造 1D 数据完成: %d 点, %.1f~%.1f°C" CLR_RESET
                     "\n",
         POINTS_COUNT, min_t / 10.0f, max_t / 10.0f);

  int8_t err = TcpLogic_BuildAndSendTemperatureFrame(
      &g_api_tx_buffer, &resMeta, 0x01 /* TRIGGER */, 0 /* IS_1D */);

  if (err == 0) {
    printf(CLR_GREEN "       -> [OK] 1D 帧已加入发送队列" CLR_RESET "\n");
  } else {
    printf(CLR_RED "       -> [Fail] 网络发送失败, 错误码: %d" CLR_RESET "\n",
           err);
  }

#undef POINTS_COUNT
}

/* ============================================================
 * 主函数 (控制台 UI 交互层)
 * ============================================================ */

int main(void) {
  /* 设置控制台支持 UTF-8 和 ANSI 转义 */
  SetConsoleOutputCP(65001);
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif
  HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
  DWORD dwMode = 0;
  GetConsoleMode(hOut, &dwMode);
  SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);

  printf("========================================================\n");
  printf("= TCP 透传客户端 API 演示版 (基于 QDXnetworkStack.c)\n");
  printf("= 服务端: %s  控制: %d  数据: %d\n", DEFAULT_SERVER_IP,
         DEFAULT_CONTROL_PORT, DEFAULT_DATA_PORT);
  printf("========================================================\n\n");

  /* Windows 下需要手动启动 Winsock */
  WSADATA wsaData;
  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
    printf("[ERROR] WSAStartup 失败。\n");
    return 1;
  }

  /* 交互式输入设备 ID */
  int input_id = 0;
  printf("请输入设备 ID (整数, 例如 101): ");
  if (scanf("%d", &input_id) != 1 || input_id < 0 || input_id > 65535) {
    printf("[ERROR] 无效。默认设为 101。\n");
    input_id = 101;
  }
  printf("\n设备 ID 设置为: %d\n\n", input_id);

  /* 清除遗留的回车换行 */
  int c;
  while ((c = getchar()) != '\n' && c != EOF) {
  }

  /* 选择维度 */
  int dim_choice = 0;
  printf("请选择维度模式:\n");
  printf("  1 - 一维模式 (随机温度数据)\n");
  printf("  2 - 二维模式 (从目录随机读取矩阵文件)\n");
  printf("请输入 (1 或 2): ");
  if (scanf("%d", &dim_choice) != 1 || (dim_choice != 1 && dim_choice != 2)) {
    dim_choice = 1;
  }
  g_dimension_mode = (dim_choice == 2) ? 1 : 0;
  printf("\n维度模式: %s\n\n", g_dimension_mode ? "二维 (2D)" : "一维 (1D)");

  if (g_dimension_mode == 1) {
    snprintf(g_matrix_dir, sizeof(g_matrix_dir), "../tcp_c_demo/src/2d_mask/");
    printf("2D 矩阵文件目录: %s\n\n", g_matrix_dir);
  }

  /* 生成假 UUID */
  uint8_t uuid[16] = {0};
  srand((unsigned int)GetTickCount());
  for (int i = 0; i < 16; i++) {
    uuid[i] = (uint8_t)(rand() % 256);
  }

  /* ---------------------------------------------------------
   *  重点：纯 API 调用区
   * --------------------------------------------------------- */
  printf(">>> 正在初始化 QDXnetworkStack 库架构...\n");

  /* 1. 初始化预处理引擎 (分配 256x256 静态滑窗资源) */
  Preprocess_Init(256, 256);

  /* 2. 初始化网络栈 */
  TcpLogic_Init(uuid, NULL);

  /* 3. 注册业务回调 (配置更新、坏品通知、采集请求) */
  TcpLogic_RegisterConfigCallback(on_config_updated);
  TcpLogic_RegisterDetectionCallback(on_detection_result);
  TcpLogic_RegisterTempFrameRequestCallback(on_temp_frame_request);

  /* 4. [Hack Demo] 因为 API 库不包含直接设置当前 DevID
   * 的接口（依赖服务端下发）， 但为了 Demo 一开始能用指定的 101 ID
   * 发起连接，我们手动造一个回调注入进去， 实际上在真实的 CH32 工程中，ID
   * 会保存在 Flash 里面。这演示了库的工作原理。
   */
  // 由于 g_TcpLogic 结构体对于外部是静态隐藏的，
  // API 设计中如果要改变 DevID，规范应是等待协议 0x05 下发。
  // 此处模拟真实情况：我们相信服务端会自动给我分发 ID，
  // 所以不再强求立刻修改。库有默认 ID=101。

  /* 5. 启动后台处理收发断连。
   * 它内部通过 _beginthreadex 开了线程！
   */
  TcpLogic_Start();
  printf(">>> 库引擎以启动。后台线程已接管所有 Socket 收发。\n\n");

  /* =========================================================
   * 主业务测试循环
   * ========================================================= */
  printf("菜单 (上位机请求时会自动发送):\n");
  printf(" [s] 立即触发一次发送    [c] 打印当前内存参数册\n");
  printf(" [q] 退出 (Ctrl+C 也可)\n\n");

  while (1) {
    if (_kbhit()) {
      int ch = _getch();
      if (ch == 'q' || ch == 'Q') {
        break;
      } else if (ch == 's' || ch == 'S') {
        printf("\n[Manual] 用户主动触发一帧。\n");
        on_temp_frame_request(g_dimension_mode);
      } else if (ch == 'c' || ch == 'C') {
        ConfigCommon_t com;
        Config2D_t c2d;
        Config1D_t c1d;
        if (TcpLogic_GetLatestConfig(&com, &c2d, &c1d) == 0) {
          printf(CLR_GREEN
                 "\n[Info] 当前静态配置缓存中包含数据 (严苛度: %d)。" CLR_RESET
                 "\n",
                 com.StrictnessLevel);
          /* 打印通用配置 */
          printf("    -> Common: Pipeline: %.*s, Type: %d, Mode: %d, Tag: %d, "
                 "Strictness: %d, Custom: %d\n",
                 16, com.PipelineId, com.PipelineType, com.WorkMode,
                 com.ConfigTag, com.StrictnessLevel, com.IsCustomMode);

          /* 打印 2D 配置 */
          printf(
              "    -> 2D: Enabled: %d, Live: %d, DevId: %d, %dx%d, Fps: %d\n",
              c2d.Enabled, c2d.IsLive, c2d.DeviceId, c2d.Width, c2d.Height,
              c2d.Fps);
          printf("    -> 2D: Mask: %d (Thresh: %d, %dx%d), Target: %dx%d\n",
                 c2d.MaskEnabled, c2d.MaskThreshold, c2d.MaskWidth,
                 c2d.MaskHeight, c2d.TargetWidth, c2d.TargetHeight);

          /* 打印 1D 配置 */
          printf("    -> 1D: Enabled: %d, RunMode: %d, TriggerType: %d, "
                 "BufferSize: %d\n",
                 c1d.Enabled, c1d.RunMode, c1d.TriggerType, c1d.BufferSize);
        } else {
          printf(CLR_YELLOW
                 "\n[Info] 当前静态配置缓存尚未收到上位机同步！" CLR_RESET
                 "\n");
        }
      }
    }
    Sleep(50);
  }

  printf("程序退出中...\n");
  WSACleanup();
  return 0;
}
