/*
 * 自然写互动课堂应用开发SDK软件 V1.0
 * StrokePath - 笔迹路径数据模型
 *
 * 描述：封装一条完整笔画的坐标序列、属性和元数据
 */

package com.writech.sdk.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * 笔迹路径模型
 * 代表从落笔到抬笔的一条完整笔画数据
 */
public class StrokePath implements Serializable {

    private static final long serialVersionUID = 1L;

    /* ========== 采样点内部类 ========== */

    /** 单个笔迹采样点 */
    public static class Point implements Serializable {
        private static final long serialVersionUID = 1L;

        /** X坐标（屏幕像素或物理mm，取决于坐标空间） */
        public float x;

        /** Y坐标 */
        public float y;

        /** 压力值（归一化 0.0~1.0） */
        public float pressure;

        /** 时间戳（相对于笔画开始时间的毫秒偏移） */
        public long timeOffset;

        /** 笔尖倾斜角度（度，0-90，0为垂直，部分笔支持） */
        public float tiltAngle;

        /** 笔尖方位角（度，0-360，部分笔支持） */
        public float azimuthAngle;

        public Point() {}

        public Point(float x, float y, float pressure, long timeOffset) {
            this.x = x;
            this.y = y;
            this.pressure = pressure;
            this.timeOffset = timeOffset;
        }

        @Override
        public String toString() {
            return "(" + x + "," + y + ",p=" + pressure + ",t=" + timeOffset + ")";
        }
    }

    /* ========== 笔画属性 ========== */

    /** 笔画唯一ID */
    private String strokeId;

    /** 来源笔设备MAC地址 */
    private String penMac;

    /** 学生ID */
    private String studentId;

    /** 页面ID（标识书写所在页面） */
    private String pageId;

    /** 笔画开始时间（绝对时间戳毫秒） */
    private long startTimestamp;

    /** 笔画结束时间 */
    private long endTimestamp;

    /** 笔画颜色（ARGB） */
    private int color = 0xFF000000;

    /** 笔画基础线宽（像素） */
    private float baseWidth = 3.0f;

    /** 采样点列表 */
    private List<Point> points;

    /* ========== 分析结果（由OCR/AI引擎填充） ========== */

    /** 识别的文字内容 */
    private String recognizedText;

    /** 识别置信度 */
    private float recognitionConfidence;

    /** 笔顺序号（在整个书写序列中的顺序） */
    private int strokeOrder;

    /** 是否为有效笔画（排除误触等） */
    private boolean isValid = true;

    /* ========== 构造函数 ========== */

    public StrokePath() {
        this.points = new ArrayList<>();
    }

    public StrokePath(String strokeId, String penMac) {
        this.strokeId = strokeId;
        this.penMac = penMac;
        this.points = new ArrayList<>();
        this.startTimestamp = System.currentTimeMillis();
    }

    /* ========== 点操作方法 ========== */

    /** 添加采样点 */
    public void addPoint(float x, float y, float pressure, long timeOffset) {
        points.add(new Point(x, y, pressure, timeOffset));
    }

    /** 添加采样点（含倾斜角） */
    public void addPointWithTilt(float x, float y, float pressure,
                                  long timeOffset, float tilt, float azimuth) {
        Point p = new Point(x, y, pressure, timeOffset);
        p.tiltAngle = tilt;
        p.azimuthAngle = azimuth;
        points.add(p);
    }

    /** 获取采样点数量 */
    public int getPointCount() {
        return points.size();
    }

    /** 获取指定索引的采样点 */
    public Point getPoint(int index) {
        if (index >= 0 && index < points.size()) {
            return points.get(index);
        }
        return null;
    }

    /** 获取所有采样点 */
    public List<Point> getPoints() {
        return points;
    }

    /* ========== 笔画几何计算 ========== */

    /** 计算笔画总长度（像素） */
    public float calculateLength() {
        float length = 0;
        for (int i = 1; i < points.size(); i++) {
            Point p0 = points.get(i - 1);
            Point p1 = points.get(i);
            float dx = p1.x - p0.x;
            float dy = p1.y - p0.y;
            length += (float) Math.sqrt(dx * dx + dy * dy);
        }
        return length;
    }

    /** 计算笔画包围盒 */
    public float[] getBoundingBox() {
        if (points.isEmpty()) return new float[]{0, 0, 0, 0};

        float minX = Float.MAX_VALUE, minY = Float.MAX_VALUE;
        float maxX = Float.MIN_VALUE, maxY = Float.MIN_VALUE;

        for (Point p : points) {
            if (p.x < minX) minX = p.x;
            if (p.y < minY) minY = p.y;
            if (p.x > maxX) maxX = p.x;
            if (p.y > maxY) maxY = p.y;
        }

        return new float[]{minX, minY, maxX, maxY};
    }

    /** 计算平均书写速度（像素/毫秒） */
    public float calculateAverageSpeed() {
        if (points.size() < 2) return 0;

        float totalLength = calculateLength();
        long duration = points.get(points.size() - 1).timeOffset - points.get(0).timeOffset;

        return duration > 0 ? totalLength / duration : 0;
    }

    /** 计算平均压力 */
    public float calculateAveragePressure() {
        if (points.isEmpty()) return 0;
        float sum = 0;
        for (Point p : points) {
            sum += p.pressure;
        }
        return sum / points.size();
    }

    /** 获取书写持续时间（毫秒） */
    public long getDuration() {
        if (points.size() < 2) return 0;
        return points.get(points.size() - 1).timeOffset - points.get(0).timeOffset;
    }

    /* ========== 序列化方法 ========== */

    /**
     * 将笔画数据序列化为紧凑的二进制格式
     * 用于BLE传输和本地缓存
     *
     * 格式：
     * [4字节 点数][每个点: 4字节x + 4字节y + 2字节pressure + 4字节timeOffset]
     */
    public byte[] toBytes() {
        int pointCount = points.size();
        byte[] data = new byte[4 + pointCount * 14];

        /* 写入点数（大端序） */
        data[0] = (byte) ((pointCount >> 24) & 0xFF);
        data[1] = (byte) ((pointCount >> 16) & 0xFF);
        data[2] = (byte) ((pointCount >> 8) & 0xFF);
        data[3] = (byte) (pointCount & 0xFF);

        int offset = 4;
        for (Point p : points) {
            /* 写入X坐标（float → 4字节） */
            int fx = Float.floatToIntBits(p.x);
            data[offset++] = (byte) ((fx >> 24) & 0xFF);
            data[offset++] = (byte) ((fx >> 16) & 0xFF);
            data[offset++] = (byte) ((fx >> 8) & 0xFF);
            data[offset++] = (byte) (fx & 0xFF);

            /* 写入Y坐标 */
            int fy = Float.floatToIntBits(p.y);
            data[offset++] = (byte) ((fy >> 24) & 0xFF);
            data[offset++] = (byte) ((fy >> 16) & 0xFF);
            data[offset++] = (byte) ((fy >> 8) & 0xFF);
            data[offset++] = (byte) (fy & 0xFF);

            /* 写入压力值（归一化后*65535转uint16） */
            int pressure16 = (int) (p.pressure * 65535);
            data[offset++] = (byte) ((pressure16 >> 8) & 0xFF);
            data[offset++] = (byte) (pressure16 & 0xFF);

            /* 写入时间偏移（uint32） */
            long t = p.timeOffset;
            data[offset++] = (byte) ((t >> 24) & 0xFF);
            data[offset++] = (byte) ((t >> 16) & 0xFF);
            data[offset++] = (byte) ((t >> 8) & 0xFF);
            data[offset++] = (byte) (t & 0xFF);
        }

        return data;
    }

    /* ========== Getter / Setter ========== */

    public String getStrokeId() { return strokeId; }
    public void setStrokeId(String strokeId) { this.strokeId = strokeId; }

    public String getPenMac() { return penMac; }
    public void setPenMac(String penMac) { this.penMac = penMac; }

    public String getStudentId() { return studentId; }
    public void setStudentId(String studentId) { this.studentId = studentId; }

    public String getPageId() { return pageId; }
    public void setPageId(String pageId) { this.pageId = pageId; }

    public long getStartTimestamp() { return startTimestamp; }
    public void setStartTimestamp(long t) { this.startTimestamp = t; }

    public long getEndTimestamp() { return endTimestamp; }
    public void setEndTimestamp(long t) { this.endTimestamp = t; }

    public int getColor() { return color; }
    public void setColor(int color) { this.color = color; }

    public float getBaseWidth() { return baseWidth; }
    public void setBaseWidth(float w) { this.baseWidth = w; }

    public String getRecognizedText() { return recognizedText; }
    public void setRecognizedText(String text) { this.recognizedText = text; }

    public float getRecognitionConfidence() { return recognitionConfidence; }
    public void setRecognitionConfidence(float c) { this.recognitionConfidence = c; }

    public int getStrokeOrder() { return strokeOrder; }
    public void setStrokeOrder(int order) { this.strokeOrder = order; }

    public boolean isValid() { return isValid; }
    public void setValid(boolean valid) { isValid = valid; }

    @Override
    public String toString() {
        return "StrokePath{id='" + strokeId + "', points=" + points.size()
               + ", duration=" + getDuration() + "ms"
               + ", text='" + recognizedText + "'}";
    }
}
