/*
 * 自然写智能点阵笔嵌入式固件软件 V1.0
 * image_capture_task.c - 图像采集任务
 *
 * 功能说明：
 * 1. 以100Hz频率驱动CMOS图像传感器采集点阵图案
 * 2. DMA方式高速传输图像数据
 * 3. 笔尖接触检测（上升沿/下降沿中断）
 * 4. 图像帧质量快速评估（丢弃模糊帧）
 * 5. 采集参数自适应调节（曝光、增益）
 */

#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"

#include "camera_driver.h"
#include "hal_spi.h"
#include "hal_dma.h"
#include "hal_gpio.h"

/* ========== 常量定义 ========== */

/* 采集频率（Hz） */
#define CAPTURE_FREQUENCY_HZ        100

/* 采集周期（毫秒） */
#define CAPTURE_PERIOD_MS           (1000 / CAPTURE_FREQUENCY_HZ)

/* 图像传感器分辨率 */
#define SENSOR_WIDTH                32
#define SENSOR_HEIGHT               32
#define SENSOR_PIXELS               (SENSOR_WIDTH * SENSOR_HEIGHT)

/* 单帧图像字节数（8位灰度） */
#define FRAME_SIZE_BYTES            SENSOR_PIXELS

/* DMA传输缓冲区（双缓冲） */
#define DMA_BUFFER_COUNT            2

/* 图像质量阈值（低于此值判定为模糊/无效帧） */
#define QUALITY_THRESHOLD           30

/* 最大连续无效帧数（超过则认为笔离开纸面） */
#define MAX_INVALID_FRAMES          5

/* 自动曝光调节步长 */
#define AUTO_EXPOSURE_STEP          4

/* ========== 数据结构 ========== */

/* 图像帧元数据（放入队列传递给坐标计算任务） */
typedef struct {
    uint8_t *pixel_buffer;          /* 指向DMA缓冲区的像素数据 */
    uint32_t frame_id;              /* 帧序号 */
    uint32_t timestamp_ms;          /* 采集时间戳 */
    uint8_t quality_score;          /* 图像质量评分（0-255） */
    uint8_t exposure_value;         /* 当前曝光值 */
    uint16_t pressure_raw;          /* 同步采集的压力原始ADC值 */
} ImageFrameMetadata;

/* 传感器配置参数 */
typedef struct {
    uint8_t exposure;               /* 曝光时间（寄存器值） */
    uint8_t gain;                   /* 模拟增益 */
    uint8_t threshold;              /* 二值化阈值 */
    bool auto_exposure_enabled;     /* 是否启用自动曝光 */
} SensorConfig;

/* ========== 外部引用 ========== */

extern QueueHandle_t g_image_data_queue;
extern EventGroupHandle_t g_ble_event_group;

/* ========== 静态变量 ========== */

/* DMA双缓冲区 */
static uint8_t s_dma_buffer[DMA_BUFFER_COUNT][FRAME_SIZE_BYTES]
    __attribute__((aligned(4)));

/* 当前活跃的DMA缓冲区索引 */
static volatile uint8_t s_active_buffer = 0;

/* 帧计数器 */
static uint32_t s_frame_counter = 0;

/* 连续无效帧计数 */
static uint8_t s_invalid_frame_count = 0;

/* 传感器配置 */
static SensorConfig s_sensor_config;

/* 笔尖状态 */
static volatile bool s_pen_touching = false;

/* ========== 笔尖接触检测 ========== */

/**
 * 笔尖接触检测GPIO中断回调
 * 通过检测微动开关或红外反射判断笔尖是否接触纸面
 */
void pen_tip_irq_handler(void) {
    bool state = hal_gpio_read(GPIO_PEN_TIP_PIN);

    BaseType_t higher_priority_woken = pdFALSE;

    if (state && !s_pen_touching) {
        /* 笔落下（接触纸面） */
        s_pen_touching = true;
        xEventGroupSetBitsFromISR(g_ble_event_group, EVT_PEN_DOWN,
                                   &higher_priority_woken);
    } else if (!state && s_pen_touching) {
        /* 笔抬起（离开纸面） */
        s_pen_touching = false;
        xEventGroupSetBitsFromISR(g_ble_event_group, EVT_PEN_UP,
                                   &higher_priority_woken);
    }

    portYIELD_FROM_ISR(higher_priority_woken);
}

/* ========== DMA传输完成回调 ========== */

/**
 * SPI DMA传输完成中断回调
 * 图像数据已从传感器通过DMA传输到内存缓冲区
 */
void spi_dma_complete_handler(void) {
    /* 切换到另一个DMA缓冲区（乒乓缓冲） */
    s_active_buffer = (s_active_buffer + 1) % DMA_BUFFER_COUNT;
}

/* ========== 图像质量评估 ========== */

/**
 * 快速评估图像帧质量
 * 通过计算图像的对比度（标准差近似）来判断是否有效
 * 有效的点阵图案应该有清晰的明暗对比
 *
 * @param pixels    像素数据
 * @param length    数据长度
 * @return 质量评分（0-255，越高越好）
 */
static uint8_t evaluate_frame_quality(const uint8_t *pixels, uint16_t length) {
    uint32_t sum = 0;
    uint32_t sum_sq = 0;
    uint16_t i;

    /* 采样统计（每4个像素取1个，减少计算量） */
    uint16_t sample_count = 0;
    for (i = 0; i < length; i += 4) {
        uint32_t val = pixels[i];
        sum += val;
        sum_sq += val * val;
        sample_count++;
    }

    if (sample_count == 0) return 0;

    /* 计算方差的近似值（反映对比度） */
    uint32_t mean = sum / sample_count;
    uint32_t variance = (sum_sq / sample_count) - (mean * mean);

    /* 方差映射到0-255评分 */
    if (variance > 2000) return 255;
    return (uint8_t)(variance * 255 / 2000);
}

/* ========== 自动曝光调节 ========== */

/**
 * 根据图像亮度自动调节传感器曝光参数
 * 目标：使图像平均亮度保持在中间范围（100-150）
 */
static void auto_exposure_adjust(const uint8_t *pixels, uint16_t length) {
    if (!s_sensor_config.auto_exposure_enabled) {
        return;
    }

    /* 计算平均亮度 */
    uint32_t sum = 0;
    uint16_t i;
    for (i = 0; i < length; i += 8) {
        sum += pixels[i];
    }
    uint8_t avg_brightness = (uint8_t)(sum / (length / 8));

    /* 根据亮度偏差调节曝光值 */
    if (avg_brightness < 80 && s_sensor_config.exposure < 250) {
        /* 过暗，增加曝光 */
        s_sensor_config.exposure += AUTO_EXPOSURE_STEP;
        camera_set_exposure(s_sensor_config.exposure);
    } else if (avg_brightness > 170 && s_sensor_config.exposure > AUTO_EXPOSURE_STEP) {
        /* 过亮，减少曝光 */
        s_sensor_config.exposure -= AUTO_EXPOSURE_STEP;
        camera_set_exposure(s_sensor_config.exposure);
    }
}

/* ========== 传感器初始化 ========== */

/**
 * 配置CMOS图像传感器初始参数
 */
static void sensor_setup(void) {
    /* 设置默认曝光和增益 */
    s_sensor_config.exposure = 128;
    s_sensor_config.gain = 64;
    s_sensor_config.threshold = 128;
    s_sensor_config.auto_exposure_enabled = true;

    /* 写入传感器寄存器 */
    camera_set_exposure(s_sensor_config.exposure);
    camera_set_gain(s_sensor_config.gain);

    /* 配置为连续帧模式 */
    camera_set_mode(CAMERA_MODE_CONTINUOUS);

    /* 配置SPI DMA接收 */
    hal_dma_config(DMA_CHANNEL_SPI_RX,
                    (uint32_t)camera_get_data_register(),
                    (uint32_t)s_dma_buffer[0],
                    FRAME_SIZE_BYTES);
}

/* ========== 图像采集主任务 ========== */

/**
 * 图像采集任务（FreeRTOS任务函数）
 *
 * 运行流程：
 * 1. 等待笔尖接触纸面
 * 2. 以100Hz频率触发CMOS传感器拍摄
 * 3. DMA传输图像数据到双缓冲区
 * 4. 评估图像质量，丢弃无效帧
 * 5. 将有效帧元数据放入队列供坐标计算任务处理
 * 6. 笔抬起后暂停采集，进入低功耗等待
 */
void image_capture_task(void *pvParameters) {
    (void)pvParameters;

    TickType_t last_wake_time;

    /* 初始化传感器参数 */
    sensor_setup();

    /* 注册笔尖GPIO中断 */
    hal_gpio_set_irq(GPIO_PEN_TIP_PIN, GPIO_IRQ_BOTH_EDGE, pen_tip_irq_handler);

    while (1) {
        /* 等待笔落下事件（低功耗阻塞） */
        xEventGroupWaitBits(g_ble_event_group, EVT_PEN_DOWN,
                            pdTRUE, pdFALSE, portMAX_DELAY);

        /* 笔已接触纸面，启动高速采集 */
        camera_power_on();

        /* 重置帧计数和无效帧计数 */
        s_frame_counter = 0;
        s_invalid_frame_count = 0;

        /* 记录采集起始时间 */
        last_wake_time = xTaskGetTickCount();

        /* 采集循环：持续采集直到笔抬起 */
        while (s_pen_touching) {
            /* 触发传感器拍摄 */
            camera_trigger_capture();

            /* 启动DMA传输（异步，CPU可做其他事） */
            uint8_t current_buffer = s_active_buffer;
            hal_dma_start(DMA_CHANNEL_SPI_RX,
                          (uint32_t)s_dma_buffer[current_buffer],
                          FRAME_SIZE_BYTES);

            /* 等待DMA完成（通常< 1ms） */
            hal_dma_wait_complete(DMA_CHANNEL_SPI_RX, 5);

            /* 同步读取压力传感器ADC值 */
            uint16_t pressure_raw = pressure_sensor_read_raw();

            /* 评估图像质量 */
            uint8_t quality = evaluate_frame_quality(
                s_dma_buffer[current_buffer], FRAME_SIZE_BYTES);

            if (quality >= QUALITY_THRESHOLD) {
                /* 有效帧，放入队列 */
                ImageFrameMetadata metadata;
                metadata.pixel_buffer = s_dma_buffer[current_buffer];
                metadata.frame_id = s_frame_counter;
                metadata.timestamp_ms = xTaskGetTickCount() * portTICK_PERIOD_MS;
                metadata.quality_score = quality;
                metadata.exposure_value = s_sensor_config.exposure;
                metadata.pressure_raw = pressure_raw;

                /* 非阻塞方式入队（如果队列满则丢弃） */
                xQueueSend(g_image_data_queue, &metadata, 0);

                s_invalid_frame_count = 0;
            } else {
                s_invalid_frame_count++;

                /* 连续多个无效帧，可能笔已离开但中断未触发 */
                if (s_invalid_frame_count >= MAX_INVALID_FRAMES) {
                    s_pen_touching = false;
                    break;
                }
            }

            /* 每16帧调整一次曝光（避免频繁调节） */
            if ((s_frame_counter & 0x0F) == 0) {
                auto_exposure_adjust(s_dma_buffer[current_buffer], FRAME_SIZE_BYTES);
            }

            s_frame_counter++;

            /* 精确定时：等待到下一个采集时间点 */
            vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(CAPTURE_PERIOD_MS));
        }

        /* 笔抬起，关闭传感器降低功耗 */
        camera_power_off();
    }
}
