/**
 * @file qdx_preprocess.c
 * @brief Zero-Copy Image Preprocessing Implementation
 */

#include "qdx_preprocess.h"
#include "qdx_port.h"
#include <string.h>

/* ============================================================
 * Internal State & Configuration Cache
 * ============================================================ */

/* Static allocation for column sums to avoid malloc */
#define PREPROCESS_MAX_WIDTH 256
static uint32_t g_col_sums[PREPROCESS_MAX_WIDTH];
static uint8_t g_is_initialized = 0;

/* 配置读写互斥锁，防止 Execute 与 Settings_Change 并发冲突 */
static qdx_mutex_t g_preprocess_mutex = NULL;

static struct {
  Config2D_t cfg2d;
  Config1D_t cfg1d;
  ConfigCommon_t common;
} g_PreprocessCfg;

/* ============================================================
 * API Implementation
 * ============================================================ */

int8_t Preprocess_Init(uint16_t maxWidth, uint16_t maxHeight) {
  (void)maxHeight; /* 列累加仅依赖宽度 */

  if (maxWidth > PREPROCESS_MAX_WIDTH) {
    return -1; /* 超出静态分配缓冲区上限 */
  }

  memset(g_col_sums, 0, sizeof(g_col_sums));
  memset(&g_PreprocessCfg, 0, sizeof(g_PreprocessCfg));

  /* 创建配置互斥锁 */
  g_preprocess_mutex = qdx_port_mutex_create();
  if (g_preprocess_mutex == NULL)
    return -1;

  /* 最小默认配置，防止除零或无限循环 */
  g_PreprocessCfg.cfg2d.TargetWidth = 1;
  g_PreprocessCfg.cfg2d.TargetHeight = 1;

  g_is_initialized = 1;
  return 0;
}

int8_t Preprocess_Settings_Change(const Config2D_t *newConfig2D,
                                  const Config1D_t *newConfig1D,
                                  const ConfigCommon_t *newCommon) {
  if (!g_is_initialized)
    return -1;

  /* 加锁保护配置更新，防止与 Execute 读取产生竞态 */
  qdx_port_mutex_lock(g_preprocess_mutex);

  if (newConfig2D)
    memcpy(&g_PreprocessCfg.cfg2d, newConfig2D, sizeof(Config2D_t));
  if (newConfig1D)
    memcpy(&g_PreprocessCfg.cfg1d, newConfig1D, sizeof(Config1D_t));
  if (newCommon)
    memcpy(&g_PreprocessCfg.common, newCommon, sizeof(ConfigCommon_t));

  /* 安全检查 */
  if (g_PreprocessCfg.cfg2d.TargetWidth == 0)
    g_PreprocessCfg.cfg2d.TargetWidth = 1;
  if (g_PreprocessCfg.cfg2d.TargetHeight == 0)
    g_PreprocessCfg.cfg2d.TargetHeight = 1;

  qdx_port_mutex_unlock(g_preprocess_mutex);
  return 0;
}

int8_t Preprocess_Execute(const RawImageBuffer_t *input,
                          TcpTxBuffer_t *out_buffer,
                          PreprocessResult_t *output_meta) {
  if (!g_is_initialized || !input || !input->pData || !out_buffer ||
      !out_buffer->pBuffer || !output_meta)
    return -1;

  /* 加锁快照当前配置，最小化持锁时间 */
  qdx_port_mutex_lock(g_preprocess_mutex);
  uint16_t tgt_w = g_PreprocessCfg.cfg2d.TargetWidth;
  uint16_t tgt_h = g_PreprocessCfg.cfg2d.TargetHeight;
  int16_t thresh = g_PreprocessCfg.cfg2d.TriggerTemperatureThreshold;
  qdx_port_mutex_unlock(g_preprocess_mutex);

  uint16_t w = input->Width;
  uint16_t h = input->Height;

  /* 目标超过输入时回退到整幅图像 */
  if (tgt_w > w)
    tgt_w = w;
  if (tgt_h > h)
    tgt_h = h;

  /* 输出缓冲区容量限制：等比缩小目标尺寸直到适合 */
  uint32_t max_payload = out_buffer->TotalCapacity - out_buffer->HeadOffset;
  if ((uint32_t)tgt_w * tgt_h * 2u > max_payload) {
    uint16_t orig_w = tgt_w, orig_h = tgt_h;
    while ((uint32_t)tgt_w * tgt_h * 2u > max_payload && (tgt_w > 1 || tgt_h > 1)) {
      tgt_w = (tgt_w + 1) / 2;
      tgt_h = (tgt_h + 1) / 2;
    }
    DBG_INIT("PP: target clamped %dx%d -> %dx%d (buf=%d)\r\n",
             (int)orig_w, (int)orig_h, (int)tgt_w, (int)tgt_h, (int)max_payload);
  }

  uint32_t required_bytes = (uint32_t)tgt_w * tgt_h * 2u;

  /* 判断是否需要滑窗计算，或直接导出全图 */
  if (tgt_w == w && tgt_h == h) {
    /* 无需滑窗，仅做温度过滤与整体统计 */
    int16_t min_t = 32767;
    int16_t max_t = -32768;
    uint32_t total_sum = 0;
    uint32_t pixels = w * h;

    /* Write directly to out_buffer starting at HeadOffset */
    uint8_t *dest_ptr = out_buffer->pBuffer + out_buffer->HeadOffset;

    for (uint32_t i = 0; i < pixels; i++) {
      int16_t raww_val = (int16_t)input->pData[i];

      if (raww_val < min_t)
        min_t = raww_val;
      if (raww_val > max_t)
        max_t = raww_val;
      total_sum += raww_val;

      /* Encode Original Raw Value (Little-Endian sequence into uint8_t) */
      *dest_ptr++ = (uint8_t)(raww_val & 0xFF);
      *dest_ptr++ = (uint8_t)((raww_val >> 8) & 0xFF);
    }

    output_meta->pValidData = out_buffer->pBuffer + out_buffer->HeadOffset;
    output_meta->DataLength = pixels * 2;
    output_meta->ValidWidth = w;
    output_meta->ValidHeight = h;
    output_meta->MinTemp = min_t;
    output_meta->MaxTemp = max_t;
    output_meta->AvgTemp = (int16_t)(total_sum / pixels);
    output_meta->RoiTemp = output_meta->AvgTemp;
    output_meta->Status = 0;
    output_meta->FrameNumber = input->FrameNumber;

    out_buffer->ValidPayloadLen = output_meta->DataLength;
    return 0;
  }

  /* ------------------------------------------------------------------------
     Perform Sliding Window Search for Average Temperature Target Region
     ------------------------------------------------------------------------ */

  memset(g_col_sums, 0, w * sizeof(uint32_t));
  uint32_t max_region_sum = 0;
  uint16_t best_x = 0;
  uint16_t best_y = 0;

  for (uint16_t y = 0; y <= h - tgt_h; y++) {

    /* Step 1: Initialize column sums for this row strip
       If y == 0, we calculate the entire strip.
       Otherwise, we subtract the top row that left, and add the bottom row that
       entered. */

    if (y == 0) {
      for (uint16_t c = 0; c < w; c++) {
        uint32_t col_total = 0;
        for (uint16_t r = 0; r < tgt_h; r++) {
          int16_t val = (int16_t)input->pData[r * w + c];
          if (val < thresh)
            val = 90;
          col_total += val;
        }
        g_col_sums[c] = col_total;
      }
    } else {
      /* Slide down by 1 row */
      for (uint16_t c = 0; c < w; c++) {
        int16_t top_val = (int16_t)input->pData[(y - 1) * w + c];
        int16_t bot_val = (int16_t)input->pData[(y + tgt_h - 1) * w + c];

        if (top_val < thresh)
          top_val = 90;
        if (bot_val < thresh)
          bot_val = 90;

        g_col_sums[c] = g_col_sums[c] - top_val + bot_val;
      }
    }

    /* Step 2: Slide Across the Columns (Left to Right) */
    uint32_t current_window_sum = 0;

    /* Initialize first window */
    for (uint16_t c = 0; c < tgt_w; c++) {
      current_window_sum += g_col_sums[c];
    }

    if (current_window_sum > max_region_sum) {
      max_region_sum = current_window_sum;
      best_x = 0;
      best_y = y;
    }

    /* Slide Right */
    for (uint16_t x = 1; x <= w - tgt_w; x++) {
      current_window_sum =
          current_window_sum - g_col_sums[x - 1] + g_col_sums[x + tgt_w - 1];

      if (current_window_sum > max_region_sum) {
        max_region_sum = current_window_sum;
        best_x = x;
        best_y = y;
      }
    }
  }

  /* ------------------------------------------------------------------------
     Extract the Data
     ------------------------------------------------------------------------ */

  int16_t min_t = 32767;
  int16_t max_t = -32768;
  uint32_t total_sum = 0;
  uint32_t pixels = tgt_w * tgt_h;
  uint8_t *dest_ptr = out_buffer->pBuffer + out_buffer->HeadOffset;

  for (uint16_t r = 0; r < tgt_h; r++) {
    for (uint16_t c = 0; c < tgt_w; c++) {
      /* Raw offset into original image */
      uint32_t src_idx = (best_y + r) * w + (best_x + c);
      int16_t raww_val = (int16_t)input->pData[src_idx];

      if (raww_val < min_t)
        min_t = raww_val;
      if (raww_val > max_t)
        max_t = raww_val;
      total_sum += raww_val;

      /* Output Original Raw Values */
      *dest_ptr++ = (uint8_t)(raww_val & 0xFF);
      *dest_ptr++ = (uint8_t)((raww_val >> 8) & 0xFF);
    }
  }

  output_meta->pValidData = out_buffer->pBuffer + out_buffer->HeadOffset;
  output_meta->DataLength = pixels * 2;
  output_meta->ValidWidth = tgt_w;
  output_meta->ValidHeight = tgt_h;
  output_meta->MinTemp = min_t;
  output_meta->MaxTemp = max_t;
  output_meta->AvgTemp = (int16_t)(total_sum / pixels);
  output_meta->RoiTemp = output_meta->AvgTemp;
  output_meta->Status = 0;
  output_meta->FrameNumber = input->FrameNumber;

  out_buffer->ValidPayloadLen = output_meta->DataLength;

  return 0;
}

/* ============================================================
 * Internal Trigger Check
 * ============================================================ */
int8_t Preprocess_CheckInternalTrigger2D(const RawImageBuffer_t *input) {
  if (!g_is_initialized || !input || !input->pData)
    return -1;

  qdx_port_mutex_lock(g_preprocess_mutex);
  uint16_t roi_x = g_PreprocessCfg.cfg2d.TriggerRoiX;
  uint16_t roi_y = g_PreprocessCfg.cfg2d.TriggerRoiY;
  uint16_t roi_w = g_PreprocessCfg.cfg2d.TriggerRoiW;
  uint16_t roi_h = g_PreprocessCfg.cfg2d.TriggerRoiH;
  uint8_t condition = g_PreprocessCfg.cfg2d.TriggerCondition;
  int16_t thresh = g_PreprocessCfg.cfg2d.TriggerTemperatureThreshold;
  qdx_port_mutex_unlock(g_preprocess_mutex);

  uint16_t w = input->Width;
  uint16_t h = input->Height;

  /* Boundary Check & Clipping */
  if (roi_w == 0 || roi_h == 0)
    return 0;
  if (roi_x >= w || roi_y >= h)
    return 0;
  if (roi_x + roi_w > w)
    roi_w = w - roi_x;
  if (roi_y + roi_h > h)
    roi_h = h - roi_y;

  int16_t max_temp = -32768;
  int64_t sum_temp = 0;
  uint32_t count = roi_w * roi_h;

  for (uint16_t r = 0; r < roi_h; r++) {
    for (uint16_t c = 0; c < roi_w; c++) {
      int16_t val = (int16_t)input->pData[(roi_y + r) * w + (roi_x + c)];

      /* Temperature Filtration Preprocessing */
      if (val < thresh) {
        val = 90; /* Treat as 9.0C */
      }

      if (val > max_temp)
        max_temp = val;
      sum_temp += val;
    }
  }

  int16_t calc_val = 0;
  if (condition == 1) {
    /* 1: Max */
    calc_val = max_temp;
  } else {
    /* 0: Average */
    calc_val = (int16_t)(sum_temp / count);
  }

  if (calc_val >= thresh) {
    return 1; /* Triggered */
  }

  return 0; /* Not triggered */
}
