/*
 * 自然写互动课堂应用开发SDK软件 V1.0
 * StrokeCanvas - Android端笔迹渲染自定义View
 *
 * 功能说明：
 * 1. 实时笔迹渲染（贝塞尔曲线平滑绘制）
 * 2. 压力感应笔锋效果（根据压力值动态调整线宽）
 * 3. 多笔同屏渲染（不同颜色区分不同学生）
 * 4. 笔迹重播动画（按时间序列回放书写过程）
 * 5. 离屏缓冲双缓冲渲染（避免闪烁）
 * 6. 触摸与点阵笔混合输入支持
 */

package com.writech.sdk.android;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.RectF;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 笔迹渲染画布组件
 * 支持实时绘制点阵笔和触摸屏输入的笔迹数据
 */
public class StrokeCanvas extends View {

    private static final String TAG = "WritechStrokeCanvas";

    /* 默认画笔颜色 */
    private static final int DEFAULT_STROKE_COLOR = Color.BLACK;

    /* 默认最小线宽（像素） */
    private static final float MIN_STROKE_WIDTH = 1.5f;

    /* 默认最大线宽（像素） */
    private static final float MAX_STROKE_WIDTH = 8.0f;

    /* 最大压力值（点阵笔12位ADC） */
    private static final float MAX_PRESSURE = 4095.0f;

    /* ========== 内部数据结构 ========== */

    /** 单个采样点（包含坐标、压力、时间戳） */
    private static class StrokePoint {
        float x;
        float y;
        float pressure;      /* 归一化压力 0.0~1.0 */
        long timestamp;       /* 毫秒时间戳 */

        StrokePoint(float x, float y, float pressure, long timestamp) {
            this.x = x;
            this.y = y;
            this.pressure = pressure;
            this.timestamp = timestamp;
        }
    }

    /** 一笔数据（从落笔到抬笔） */
    private static class Stroke {
        String penMac;                     /* 来源笔MAC地址 */
        int color;                         /* 笔迹颜色 */
        List<StrokePoint> points;          /* 采样点列表 */

        Stroke(String penMac, int color) {
            this.penMac = penMac;
            this.color = color;
            this.points = new ArrayList<>();
        }
    }

    /* ========== 成员变量 ========== */

    /* 离屏缓冲Bitmap（双缓冲渲染） */
    private Bitmap mBufferBitmap;
    private Canvas mBufferCanvas;

    /* 绘制画笔 */
    private final Paint mStrokePaint;

    /* 背景清除画笔 */
    private final Paint mClearPaint;

    /* 已完成的笔画列表（历史记录） */
    private final List<Stroke> mCompletedStrokes = new ArrayList<>();

    /* 当前正在书写的笔画（按笔MAC索引） */
    private final Map<String, Stroke> mActiveStrokes = new HashMap<>();

    /* 每支笔的颜色映射 */
    private final Map<String, Integer> mPenColorMap = new HashMap<>();

    /* 笔迹颜色分配计数器 */
    private int mColorIndex = 0;

    /* 预定义的笔迹颜色列表（用于多学生区分） */
    private static final int[] STROKE_COLORS = {
        Color.BLACK,
        Color.parseColor("#1565C0"),     /* 蓝色 */
        Color.parseColor("#C62828"),     /* 红色 */
        Color.parseColor("#2E7D32"),     /* 绿色 */
        Color.parseColor("#E65100"),     /* 橙色 */
        Color.parseColor("#6A1B9A"),     /* 紫色 */
        Color.parseColor("#00838F"),     /* 青色 */
        Color.parseColor("#4E342E"),     /* 棕色 */
    };

    /* 是否启用压力感应笔锋 */
    private boolean mPressureEnabled = true;

    /* 笔迹重播相关 */
    private boolean mIsReplaying = false;
    private int mReplayStrokeIndex = 0;
    private int mReplayPointIndex = 0;
    private long mReplayStartTime = 0;

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

    public StrokeCanvas(Context context) {
        this(context, null);
    }

    public StrokeCanvas(Context context, AttributeSet attrs) {
        super(context, attrs);

        /* 初始化笔迹画笔 */
        mStrokePaint = new Paint();
        mStrokePaint.setAntiAlias(true);           /* 抗锯齿 */
        mStrokePaint.setDither(true);               /* 防抖动 */
        mStrokePaint.setStyle(Paint.Style.STROKE);
        mStrokePaint.setStrokeJoin(Paint.Join.ROUND);   /* 圆角连接 */
        mStrokePaint.setStrokeCap(Paint.Cap.ROUND);     /* 圆头笔触 */

        /* 初始化清除画笔 */
        mClearPaint = new Paint();
        mClearPaint.setColor(Color.WHITE);
    }

    /* ========== View生命周期 ========== */

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        /* 创建离屏缓冲Bitmap */
        if (mBufferBitmap != null) {
            mBufferBitmap.recycle();
        }
        mBufferBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mBufferCanvas = new Canvas(mBufferBitmap);
        mBufferCanvas.drawColor(Color.WHITE);

        /* 重绘所有历史笔画到缓冲区 */
        redrawAllStrokes();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        /* 将离屏缓冲Bitmap绘制到屏幕 */
        if (mBufferBitmap != null) {
            canvas.drawBitmap(mBufferBitmap, 0, 0, null);
        }

        /* 绘制当前活跃的笔画（实时部分） */
        for (Stroke stroke : mActiveStrokes.values()) {
            drawStrokeRealtime(canvas, stroke);
        }
    }

    /* ========== 点阵笔数据输入接口 ========== */

    /**
     * 接收笔落下事件（开始新的一笔）
     * @param penMac  笔设备MAC地址
     */
    public void onPenDown(String penMac) {
        int color = getPenColor(penMac);
        Stroke stroke = new Stroke(penMac, color);
        mActiveStrokes.put(penMac, stroke);
    }

    /**
     * 接收笔迹坐标数据
     * @param penMac    笔MAC
     * @param screenX   屏幕X坐标（已经过坐标变换）
     * @param screenY   屏幕Y坐标
     * @param pressure  原始压力值（0-4095）
     */
    public void onStrokePoint(String penMac, float screenX, float screenY,
                               int pressure) {
        Stroke stroke = mActiveStrokes.get(penMac);
        if (stroke == null) {
            /* 如果没有活跃笔画，自动创建 */
            onPenDown(penMac);
            stroke = mActiveStrokes.get(penMac);
        }

        /* 归一化压力值 */
        float normalizedPressure = Math.min(1.0f, (float) pressure / MAX_PRESSURE);
        long timestamp = SystemClock.elapsedRealtime();

        stroke.points.add(new StrokePoint(screenX, screenY, normalizedPressure, timestamp));

        /* 触发重绘（仅绘制增量部分，避免全量刷新） */
        int pointCount = stroke.points.size();
        if (pointCount >= 2) {
            StrokePoint prev = stroke.points.get(pointCount - 2);
            StrokePoint curr = stroke.points.get(pointCount - 1);

            /* 仅刷新受影响的矩形区域（性能优化） */
            float padding = MAX_STROKE_WIDTH + 2;
            float left = Math.min(prev.x, curr.x) - padding;
            float top = Math.min(prev.y, curr.y) - padding;
            float right = Math.max(prev.x, curr.x) + padding;
            float bottom = Math.max(prev.y, curr.y) + padding;

            invalidate((int) left, (int) top, (int) right, (int) bottom);
        }
    }

    /**
     * 接收笔抬起事件（一笔结束）
     * 将当前笔画固化到缓冲区并归档
     */
    public void onPenUp(String penMac) {
        Stroke stroke = mActiveStrokes.remove(penMac);
        if (stroke != null && stroke.points.size() > 1) {
            /* 绘制到离屏缓冲区（固化） */
            drawStrokeToBuffer(stroke);
            /* 添加到已完成列表 */
            mCompletedStrokes.add(stroke);
        }
        invalidate();
    }

    /* ========== 笔迹渲染核心算法 ========== */

    /**
     * 实时渲染笔画（使用贝塞尔曲线平滑）
     * 在每次onDraw中调用，绘制当前活跃的笔画
     */
    private void drawStrokeRealtime(Canvas canvas, Stroke stroke) {
        List<StrokePoint> points = stroke.points;
        if (points.size() < 2) return;

        mStrokePaint.setColor(stroke.color);

        for (int i = 1; i < points.size(); i++) {
            StrokePoint p0 = points.get(i - 1);
            StrokePoint p1 = points.get(i);

            /* 根据压力计算线宽 */
            float width = calculateStrokeWidth(p0.pressure, p1.pressure);
            mStrokePaint.setStrokeWidth(width);

            if (i >= 2) {
                /* 使用二次贝塞尔曲线平滑绘制 */
                StrokePoint pPrev = points.get(i - 2);
                float midX0 = (pPrev.x + p0.x) / 2;
                float midY0 = (pPrev.y + p0.y) / 2;
                float midX1 = (p0.x + p1.x) / 2;
                float midY1 = (p0.y + p1.y) / 2;

                Path path = new Path();
                path.moveTo(midX0, midY0);
                path.quadTo(p0.x, p0.y, midX1, midY1);
                canvas.drawPath(path, mStrokePaint);
            } else {
                /* 前两个点直接画直线 */
                canvas.drawLine(p0.x, p0.y, p1.x, p1.y, mStrokePaint);
            }
        }
    }

    /**
     * 将完成的笔画绘制到离屏缓冲区
     */
    private void drawStrokeToBuffer(Stroke stroke) {
        if (mBufferCanvas == null) return;
        drawStrokeRealtime(mBufferCanvas, stroke);
    }

    /**
     * 根据压力值计算线宽（笔锋效果）
     * 使用两个相邻点的平均压力，平滑过渡
     *
     * @param pressure0  前一点压力（归一化）
     * @param pressure1  当前点压力（归一化）
     * @return 线宽（像素）
     */
    private float calculateStrokeWidth(float pressure0, float pressure1) {
        if (!mPressureEnabled) {
            return (MIN_STROKE_WIDTH + MAX_STROKE_WIDTH) / 2;
        }

        float avgPressure = (pressure0 + pressure1) / 2.0f;

        /* 压力-宽度映射曲线（使用幂函数增加笔锋感） */
        float normalized = (float) Math.pow(avgPressure, 0.7);
        return MIN_STROKE_WIDTH + normalized * (MAX_STROKE_WIDTH - MIN_STROKE_WIDTH);
    }

    /* ========== 多笔颜色管理 ========== */

    /** 获取或分配笔的颜色 */
    private int getPenColor(String penMac) {
        Integer color = mPenColorMap.get(penMac);
        if (color == null) {
            color = STROKE_COLORS[mColorIndex % STROKE_COLORS.length];
            mPenColorMap.put(penMac, color);
            mColorIndex++;
        }
        return color;
    }

    /** 手动设置某支笔的颜色 */
    public void setPenColor(String penMac, int color) {
        mPenColorMap.put(penMac, color);
    }

    /* ========== 画布操作 ========== */

    /** 清除所有笔迹 */
    public void clearAll() {
        mCompletedStrokes.clear();
        mActiveStrokes.clear();
        if (mBufferCanvas != null) {
            mBufferCanvas.drawColor(Color.WHITE);
        }
        invalidate();
    }

    /** 撤销最后一笔 */
    public boolean undo() {
        if (mCompletedStrokes.isEmpty()) return false;
        mCompletedStrokes.remove(mCompletedStrokes.size() - 1);
        redrawAllStrokes();
        invalidate();
        return true;
    }

    /** 重绘所有历史笔画到缓冲区 */
    private void redrawAllStrokes() {
        if (mBufferCanvas == null) return;
        mBufferCanvas.drawColor(Color.WHITE);
        for (Stroke stroke : mCompletedStrokes) {
            drawStrokeToBuffer(stroke);
        }
    }

    /** 导出当前画布为Bitmap */
    public Bitmap exportBitmap() {
        Bitmap export = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas exportCanvas = new Canvas(export);
        draw(exportCanvas);
        return export;
    }

    /** 获取已完成的笔画数量 */
    public int getStrokeCount() {
        return mCompletedStrokes.size();
    }

    /** 设置是否启用压力笔锋效果 */
    public void setPressureEnabled(boolean enabled) {
        mPressureEnabled = enabled;
    }

    /* ========== 触摸屏输入支持 ========== */

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        /* 使用"touch"作为虚拟笔MAC */
        String touchMac = "touch_input";

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                onPenDown(touchMac);
                onStrokePoint(touchMac, event.getX(), event.getY(),
                    (int)(event.getPressure() * MAX_PRESSURE));
                return true;

            case MotionEvent.ACTION_MOVE:
                /* 处理历史点（Android会批量发送MOVE事件） */
                for (int i = 0; i < event.getHistorySize(); i++) {
                    onStrokePoint(touchMac,
                        event.getHistoricalX(i),
                        event.getHistoricalY(i),
                        (int)(event.getHistoricalPressure(i) * MAX_PRESSURE));
                }
                onStrokePoint(touchMac, event.getX(), event.getY(),
                    (int)(event.getPressure() * MAX_PRESSURE));
                return true;

            case MotionEvent.ACTION_UP:
                onStrokePoint(touchMac, event.getX(), event.getY(),
                    (int)(event.getPressure() * MAX_PRESSURE));
                onPenUp(touchMac);
                return true;
        }
        return super.onTouchEvent(event);
    }
}
