/**
 * 自然写教室智能算力盒边缘计算软件 V1.0
 * 笔迹预处理模块 - 笔迹坐标数据预处理管道
 *
 * 对网关转发的原始笔迹坐标进行预处理：
 * 去噪滤波、坐标归一化、笔画分割、特征提取
 * 预处理结果作为NPU/GPU推理的标准化输入
 */

#ifndef STROKE_PREPROCESSOR_H
#define STROKE_PREPROCESSOR_H

#include <vector>
#include <cmath>
#include <algorithm>
#include <numeric>
#include <cstring>

// ==================== 基础数据结构 ====================

/** 原始笔迹坐标点（来自网关gRPC数据流） */
struct RawPoint {
    float x;            // X坐标（点阵单位，约300DPI）
    float y;            // Y坐标
    float pressure;     // 压力值 (0.0-1.0)
    uint32_t timestamp; // 采集时间戳（毫秒）
    bool pen_up;        // 抬笔标记
};

/** 归一化后的坐标点 */
struct NormalizedPoint {
    float x;            // 归一化X (0.0-1.0)
    float y;            // 归一化Y (0.0-1.0)
    float pressure;     // 压力值 (0.0-1.0)
};

/** 笔画数据 */
struct Stroke {
    std::vector<NormalizedPoint> points; // 归一化坐标点序列
    int stroke_index;                    // 笔画序号
    float length;                        // 笔画路径长度
    int duration_ms;                     // 书写耗时（毫秒）
};

/** 预处理输出（用于NPU推理输入） */
struct PreprocessedData {
    std::vector<float> image;      // 渲染后的灰度图像 (H*W)
    int image_width;               // 图像宽度
    int image_height;              // 图像高度
    std::vector<Stroke> strokes;   // 分割后的笔画列表
    int total_points;              // 总坐标点数
    int stroke_count;              // 笔画数量
};

// ==================== 去噪滤波器 ====================

/**
 * 笔迹去噪滤波器
 * 消除点阵笔采集过程中的抖动噪声和异常跳跃点
 * 多级滤波策略：异常点剔除 → 中值滤波 → 移动平均平滑
 */
class StrokeNoiseFilter {
public:
    /**
     * 构造函数
     * max_jump: 最大允许跳跃距离（超过则视为异常点）
     * window_size: 滤波窗口大小（奇数）
     */
    StrokeNoiseFilter(float max_jump = 50.0f, int window_size = 3)
        : max_jump_(max_jump), window_size_(window_size) {}

    /**
     * 剔除异常跳跃点
     * 点阵笔摄像头短暂遮挡会导致坐标突变，需要过滤
     */
    std::vector<RawPoint> remove_outliers(const std::vector<RawPoint>& points) {
        if (points.size() < 3) return points;

        std::vector<RawPoint> result;
        result.push_back(points[0]);

        for (size_t i = 1; i < points.size(); i++) {
            float dx = points[i].x - points[i-1].x;
            float dy = points[i].y - points[i-1].y;
            float dist = std::sqrt(dx * dx + dy * dy);

            // 跳跃距离在合理范围内才保留该点
            if (dist <= max_jump_) {
                result.push_back(points[i]);
            }
        }
        return result;
    }

    /**
     * 中值滤波去噪
     * 对X和Y坐标分别进行一维中值滤波
     * 有效消除脉冲噪声同时保留笔画转折特征
     */
    std::vector<RawPoint> median_filter(const std::vector<RawPoint>& points) {
        int n = static_cast<int>(points.size());
        if (n < window_size_) return points;

        int half = window_size_ / 2;
        std::vector<RawPoint> result(n);

        for (int i = 0; i < n; i++) {
            // 收集窗口内的X和Y值
            std::vector<float> wx, wy;
            for (int j = std::max(0, i - half); j <= std::min(n - 1, i + half); j++) {
                wx.push_back(points[j].x);
                wy.push_back(points[j].y);
            }
            // 排序取中值
            std::sort(wx.begin(), wx.end());
            std::sort(wy.begin(), wy.end());

            result[i] = points[i];
            result[i].x = wx[wx.size() / 2];
            result[i].y = wy[wy.size() / 2];
        }
        return result;
    }

    /**
     * 移动平均平滑
     * 进一步减少微小抖动，使笔画更流畅
     */
    std::vector<RawPoint> moving_average(const std::vector<RawPoint>& points) {
        int n = static_cast<int>(points.size());
        if (n < 3) return points;

        std::vector<RawPoint> result(n);
        int half = window_size_ / 2;

        for (int i = 0; i < n; i++) {
            float sum_x = 0, sum_y = 0;
            int count = 0;
            for (int j = std::max(0, i - half); j <= std::min(n - 1, i + half); j++) {
                sum_x += points[j].x;
                sum_y += points[j].y;
                count++;
            }
            result[i] = points[i];
            result[i].x = sum_x / count;
            result[i].y = sum_y / count;
        }
        return result;
    }

    /** 执行完整去噪流程 */
    std::vector<RawPoint> apply(const std::vector<RawPoint>& points) {
        auto step1 = remove_outliers(points);
        auto step2 = median_filter(step1);
        auto step3 = moving_average(step2);
        return step3;
    }

private:
    float max_jump_;
    int window_size_;
};

// ==================== 坐标归一化器 ====================

/**
 * 坐标归一化器
 * 将不同纸张尺寸和分辨率的原始坐标统一归一化到[0,1]范围
 * 保持宽高比以避免笔迹变形
 */
class CoordinateNormalizer {
public:
    CoordinateNormalizer(bool preserve_aspect = true) : preserve_aspect_(preserve_aspect) {}

    /**
     * Min-Max归一化，映射到[0,1]范围
     */
    std::vector<NormalizedPoint> normalize(const std::vector<RawPoint>& points) {
        if (points.empty()) return {};

        // 计算坐标范围
        float min_x = points[0].x, max_x = points[0].x;
        float min_y = points[0].y, max_y = points[0].y;
        for (const auto& p : points) {
            min_x = std::min(min_x, p.x);
            max_x = std::max(max_x, p.x);
            min_y = std::min(min_y, p.y);
            max_y = std::max(max_y, p.y);
        }

        float range_x = max_x - min_x;
        float range_y = max_y - min_y;

        // 保持宽高比时使用统一的缩放因子
        float scale = 1.0f;
        if (preserve_aspect_) {
            scale = std::max(range_x, range_y);
            if (scale < 1e-6f) scale = 1.0f;
        }

        std::vector<NormalizedPoint> result;
        result.reserve(points.size());

        for (const auto& p : points) {
            NormalizedPoint np;
            if (preserve_aspect_) {
                np.x = (p.x - min_x) / scale;
                np.y = (p.y - min_y) / scale;
            } else {
                np.x = (range_x > 1e-6f) ? (p.x - min_x) / range_x : 0.5f;
                np.y = (range_y > 1e-6f) ? (p.y - min_y) / range_y : 0.5f;
            }
            np.pressure = p.pressure;
            result.push_back(np);
        }
        return result;
    }

private:
    bool preserve_aspect_;
};

// ==================== 笔画分割器 ====================

/**
 * 笔画分割器
 * 根据抬笔事件和时间间隔将连续坐标流分割为独立笔画
 */
class StrokeSegmenter {
public:
    StrokeSegmenter(int time_threshold_ms = 200, int min_points = 3)
        : time_threshold_(time_threshold_ms), min_points_(min_points) {}

    /**
     * 将原始点序列分割为笔画列表
     */
    std::vector<std::vector<RawPoint>> segment(const std::vector<RawPoint>& points) {
        if (points.empty()) return {};

        std::vector<std::vector<RawPoint>> strokes;
        std::vector<RawPoint> current;
        current.push_back(points[0]);

        for (size_t i = 1; i < points.size(); i++) {
            bool is_break = points[i].pen_up;
            int time_gap = static_cast<int>(points[i].timestamp - points[i-1].timestamp);

            if ((is_break || time_gap > time_threshold_) &&
                static_cast<int>(current.size()) >= min_points_) {
                strokes.push_back(current);
                current.clear();
            }
            if (!points[i].pen_up) {
                current.push_back(points[i]);
            }
        }
        if (static_cast<int>(current.size()) >= min_points_) {
            strokes.push_back(current);
        }
        return strokes;
    }

private:
    int time_threshold_;
    int min_points_;
};

// ==================== 图像渲染器 ====================

/**
 * 笔迹图像渲染器
 * 将归一化坐标渲染为灰度图像作为CNN模型输入
 * 使用Bresenham直线算法连接相邻坐标点
 */
class StrokeImageRenderer {
public:
    StrokeImageRenderer(int width = 64, int height = 64)
        : width_(width), height_(height) {}

    /**
     * 将坐标序列渲染为灰度图像
     * 输出一维浮点数组，值域[0,1]，1表示笔迹
     */
    std::vector<float> render(const std::vector<NormalizedPoint>& points) {
        std::vector<float> image(width_ * height_, 0.0f);

        for (size_t i = 1; i < points.size(); i++) {
            int x0 = static_cast<int>(points[i-1].x * (width_ - 1));
            int y0 = static_cast<int>(points[i-1].y * (height_ - 1));
            int x1 = static_cast<int>(points[i].x * (width_ - 1));
            int y1 = static_cast<int>(points[i].y * (height_ - 1));

            // 裁剪到图像范围
            x0 = std::clamp(x0, 0, width_ - 1);
            y0 = std::clamp(y0, 0, height_ - 1);
            x1 = std::clamp(x1, 0, width_ - 1);
            y1 = std::clamp(y1, 0, height_ - 1);

            float pressure = (points[i-1].pressure + points[i].pressure) * 0.5f;

            // Bresenham直线算法
            draw_line(image, x0, y0, x1, y1, pressure);
        }
        return image;
    }

private:
    void draw_line(std::vector<float>& image, int x0, int y0, int x1, int y1, float value) {
        int dx = std::abs(x1 - x0);
        int dy = std::abs(y1 - y0);
        int sx = (x0 < x1) ? 1 : -1;
        int sy = (y0 < y1) ? 1 : -1;
        int err = dx - dy;

        while (true) {
            int idx = y0 * width_ + x0;
            if (idx >= 0 && idx < width_ * height_) {
                image[idx] = std::max(image[idx], value);
            }
            if (x0 == x1 && y0 == y1) break;
            int e2 = 2 * err;
            if (e2 > -dy) { err -= dy; x0 += sx; }
            if (e2 < dx)  { err += dx; y0 += sy; }
        }
    }

    int width_;
    int height_;
};

// ==================== 预处理管道（整合） ====================

/**
 * 笔迹预处理管道
 * 整合去噪、归一化、分割、渲染的完整处理流程
 * 输入原始坐标点序列，输出标准化的推理输入数据
 */
class StrokePreprocessor {
public:
    StrokePreprocessor(int image_size = 64)
        : noise_filter_(50.0f, 3),
          normalizer_(true),
          segmenter_(200, 3),
          renderer_(image_size, image_size),
          image_size_(image_size) {}

    /**
     * 执行完整预处理管道
     * 流程：原始坐标 → 去噪 → 归一化 → 笔画分割 → 图像渲染
     */
    PreprocessedData process(const std::vector<RawPoint>& raw_points) {
        PreprocessedData result;

        // 步骤1：去噪滤波
        auto denoised = noise_filter_.apply(raw_points);

        // 步骤2：坐标归一化
        auto normalized = normalizer_.normalize(denoised);

        // 步骤3：笔画分割
        auto stroke_groups = segmenter_.segment(denoised);

        // 构建笔画数据
        for (int i = 0; i < static_cast<int>(stroke_groups.size()); i++) {
            Stroke stroke;
            stroke.stroke_index = i;
            auto norm_group = normalizer_.normalize(stroke_groups[i]);
            stroke.points = norm_group;
            stroke.length = calc_path_length(norm_group);
            if (stroke_groups[i].size() >= 2) {
                stroke.duration_ms = static_cast<int>(
                    stroke_groups[i].back().timestamp - stroke_groups[i].front().timestamp);
            }
            result.strokes.push_back(stroke);
        }

        // 步骤4：渲染为灰度图像
        result.image = renderer_.render(normalized);
        result.image_width = image_size_;
        result.image_height = image_size_;
        result.total_points = static_cast<int>(denoised.size());
        result.stroke_count = static_cast<int>(result.strokes.size());

        return result;
    }

private:
    float calc_path_length(const std::vector<NormalizedPoint>& points) {
        float total = 0.0f;
        for (size_t i = 1; i < points.size(); i++) {
            float dx = points[i].x - points[i-1].x;
            float dy = points[i].y - points[i-1].y;
            total += std::sqrt(dx * dx + dy * dy);
        }
        return total;
    }

    StrokeNoiseFilter noise_filter_;
    CoordinateNormalizer normalizer_;
    StrokeSegmenter segmenter_;
    StrokeImageRenderer renderer_;
    int image_size_;
};

#endif // STROKE_PREPROCESSOR_H
