/**
 * 自然写互动课堂智慧黑板端应用软件 V1.0
 *
 * WhiteboardEngine.kt - 白板渲染引擎
 *
 * 功能说明：
 * - Canvas 2D高性能笔迹渲染（SurfaceView双缓冲）
 * - 教师触控书写（多点触控支持）
 * - 压力感应笔锋效果（贝塞尔曲线平滑）
 * - 撤销/重做操作栈
 * - 画布缩放/平移手势
 * - 笔迹序列化与反序列化
 * - 背景课件叠加渲染（PPT/PDF/图片）
 */

package com.writech.board.engine

import android.content.Context
import android.graphics.*
import android.util.Log
import android.view.MotionEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
import java.io.*
import java.util.LinkedList
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.math.*

/**
 * 笔迹点数据
 * @param x X坐标（屏幕像素）
 * @param y Y坐标（屏幕像素）
 * @param pressure 压力值 0.0-1.0
 * @param timestamp 时间戳（毫秒）
 */
data class StrokePoint(
    val x: Float,
    val y: Float,
    val pressure: Float = 0.5f,
    val timestamp: Long = System.currentTimeMillis()
)

/**
 * 单条笔画数据
 * 包含构成一笔的所有采样点
 */
data class Stroke(
    val points: MutableList<StrokePoint> = mutableListOf(),
    var color: Int = Color.BLACK,
    var baseWidth: Float = 4.0f,
    var isEraser: Boolean = false,
    val strokeId: Long = System.currentTimeMillis()
)

/**
 * 撤销/重做操作记录
 */
sealed class CanvasAction {
    data class AddStroke(val stroke: Stroke) : CanvasAction()
    data class RemoveStroke(val stroke: Stroke) : CanvasAction()
    data class ClearAll(val strokes: List<Stroke>) : CanvasAction()
}

/**
 * 白板渲染引擎
 *
 * 基于SurfaceView实现高性能笔迹渲染：
 * - 独立渲染线程，不阻塞UI线程
 * - 双缓冲绘制，避免画面撕裂
 * - 压力感应笔锋：笔迹宽度随压力动态变化
 * - 贝塞尔曲线平滑：消除采样锯齿
 */
class WhiteboardEngine(context: Context) : SurfaceView(context), SurfaceHolder.Callback {

    companion object {
        private const val TAG = "WhiteboardEngine"
        /** 撤销栈最大深度 */
        private const val MAX_UNDO_DEPTH = 50
        /** 贝塞尔平滑采样阈值（像素） */
        private const val SMOOTH_THRESHOLD = 2.0f
        /** 笔锋最小宽度比例 */
        private const val MIN_WIDTH_RATIO = 0.3f
        /** 笔锋最大宽度比例 */
        private const val MAX_WIDTH_RATIO = 1.5f
        /** 橡皮擦半径 */
        private const val ERASER_RADIUS = 30.0f
    }

    /* ==================== 渲染状态 ==================== */

    /** 所有已完成的笔画列表 */
    private val completedStrokes = CopyOnWriteArrayList<Stroke>()
    /** 当前正在绘制的笔画 */
    private var currentStroke: Stroke? = null
    /** 撤销栈 */
    private val undoStack = LinkedList<CanvasAction>()
    /** 重做栈 */
    private val redoStack = LinkedList<CanvasAction>()

    /* ==================== 绘图工具 ==================== */

    /** 笔迹画笔 */
    private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        style = Paint.Style.STROKE
        strokeCap = Paint.Cap.ROUND
        strokeJoin = Paint.Join.ROUND
        color = Color.BLACK
        strokeWidth = 4.0f
    }

    /** 橡皮擦画笔 */
    private val eraserPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        style = Paint.Style.STROKE
        strokeCap = Paint.Cap.ROUND
        strokeWidth = ERASER_RADIUS * 2
        xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
    }

    /** 背景课件位图 */
    private var backgroundBitmap: Bitmap? = null

    /** 离屏缓冲位图（已完成笔画的缓存） */
    private var offscreenBitmap: Bitmap? = null
    private var offscreenCanvas: Canvas? = null

    /* ==================== 画布变换 ==================== */

    /** 画布变换矩阵（缩放+平移） */
    private val canvasMatrix = Matrix()
    /** 逆矩阵（触摸坐标反变换） */
    private val inverseMatrix = Matrix()
    /** 当前缩放比例 */
    private var currentScale = 1.0f
    /** 当前偏移 */
    private var translateX = 0.0f
    private var translateY = 0.0f

    /* ==================== 工具状态 ==================== */

    /** 当前画笔颜色 */
    var penColor: Int = Color.BLACK
    /** 当前画笔宽度 */
    var penWidth: Float = 4.0f
    /** 是否使用橡皮擦模式 */
    var eraserMode: Boolean = false
    /** 是否启用压力感应 */
    var pressureSensitive: Boolean = true
    /** 渲染线程运行标志 */
    private var isRendering = false

    init {
        holder.addCallback(this)
        isFocusable = true
        isFocusableInTouchMode = true
    }

    /* ==================== SurfaceHolder回调 ==================== */

    override fun surfaceCreated(holder: SurfaceHolder) {
        Log.i(TAG, "Surface创建: ${holder.surfaceFrame.width()}x${holder.surfaceFrame.height()}")

        /* 创建离屏缓冲 */
        val w = holder.surfaceFrame.width()
        val h = holder.surfaceFrame.height()
        offscreenBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
        offscreenCanvas = Canvas(offscreenBitmap!!)

        isRendering = true
        renderFrame()
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
        Log.i(TAG, "Surface尺寸变更: ${width}x${height}")
        /* 重建离屏缓冲 */
        offscreenBitmap?.recycle()
        offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        offscreenCanvas = Canvas(offscreenBitmap!!)
        rebuildOffscreen()
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        isRendering = false
        offscreenBitmap?.recycle()
        offscreenBitmap = null
        Log.i(TAG, "Surface销毁")
    }

    /* ==================== 触摸事件处理 ==================== */

    override fun onTouchEvent(event: MotionEvent): Boolean {
        /* 将屏幕坐标通过逆矩阵转换为画布坐标 */
        val pts = floatArrayOf(event.x, event.y)
        canvasMatrix.invert(inverseMatrix)
        inverseMatrix.mapPoints(pts)

        val canvasX = pts[0]
        val canvasY = pts[1]
        val pressure = if (pressureSensitive) event.pressure.coerceIn(0.1f, 1.0f) else 0.5f

        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                onTouchDown(canvasX, canvasY, pressure)
            }
            MotionEvent.ACTION_MOVE -> {
                onTouchMove(canvasX, canvasY, pressure)
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                onTouchUp(canvasX, canvasY, pressure)
            }
        }

        return true
    }

    /**
     * 触摸按下 - 开始新笔画
     */
    private fun onTouchDown(x: Float, y: Float, pressure: Float) {
        if (eraserMode) {
            eraseAtPoint(x, y)
            return
        }

        currentStroke = Stroke(
            color = penColor,
            baseWidth = penWidth,
            isEraser = false
        )
        currentStroke?.points?.add(StrokePoint(x, y, pressure))
    }

    /**
     * 触摸移动 - 添加采样点并实时渲染
     */
    private fun onTouchMove(x: Float, y: Float, pressure: Float) {
        if (eraserMode) {
            eraseAtPoint(x, y)
            return
        }

        val stroke = currentStroke ?: return
        val lastPoint = stroke.points.lastOrNull() ?: return

        /* 距离过近时跳过采样（减少冗余点） */
        val dx = x - lastPoint.x
        val dy = y - lastPoint.y
        val dist = sqrt(dx * dx + dy * dy)
        if (dist < SMOOTH_THRESHOLD) return

        stroke.points.add(StrokePoint(x, y, pressure))

        /* 增量渲染当前笔画的最新线段 */
        renderCurrentStroke()
    }

    /**
     * 触摸抬起 - 完成笔画并加入撤销栈
     */
    private fun onTouchUp(x: Float, y: Float, pressure: Float) {
        val stroke = currentStroke ?: return

        if (stroke.points.size >= 2) {
            completedStrokes.add(stroke)

            /* 记入撤销栈 */
            pushUndoAction(CanvasAction.AddStroke(stroke))

            /* 将笔画绘制到离屏缓冲 */
            drawStrokeToOffscreen(stroke)

            Log.d(TAG, "笔画完成: ${stroke.points.size}个点, 颜色=#${Integer.toHexString(stroke.color)}")
        }

        currentStroke = null
        renderFrame()
    }

    /* ==================== 笔迹渲染 ==================== */

    /**
     * 在离屏缓冲上绘制一条完整笔画
     * 使用贝塞尔曲线平滑 + 压力感应笔锋
     */
    private fun drawStrokeToOffscreen(stroke: Stroke) {
        val canvas = offscreenCanvas ?: return
        val points = stroke.points
        if (points.size < 2) return

        strokePaint.color = stroke.color

        for (i in 1 until points.size) {
            val prev = points[i - 1]
            val curr = points[i]

            /* 压力感应笔锋：宽度随压力变化 */
            val pressureWidth = stroke.baseWidth *
                (MIN_WIDTH_RATIO + (MAX_WIDTH_RATIO - MIN_WIDTH_RATIO) * curr.pressure)
            strokePaint.strokeWidth = pressureWidth

            if (i >= 2) {
                /* 使用二次贝塞尔曲线平滑 */
                val prevPrev = points[i - 2]
                val midX1 = (prevPrev.x + prev.x) / 2f
                val midY1 = (prevPrev.y + prev.y) / 2f
                val midX2 = (prev.x + curr.x) / 2f
                val midY2 = (prev.y + curr.y) / 2f

                val path = Path()
                path.moveTo(midX1, midY1)
                path.quadTo(prev.x, prev.y, midX2, midY2)
                canvas.drawPath(path, strokePaint)
            } else {
                /* 前两个点直接连线 */
                canvas.drawLine(prev.x, prev.y, curr.x, curr.y, strokePaint)
            }
        }
    }

    /**
     * 渲染当前正在绘制的笔画（增量渲染最新线段）
     */
    private fun renderCurrentStroke() {
        if (!isRendering) return

        val canvas = holder.lockCanvas() ?: return
        try {
            /* 绘制离屏缓冲（已完成笔画） */
            canvas.save()
            canvas.setMatrix(canvasMatrix)

            offscreenBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }

            /* 绘制当前笔画 */
            currentStroke?.let { stroke ->
                drawStrokeOnCanvas(canvas, stroke)
            }

            canvas.restore()
        } finally {
            holder.unlockCanvasAndPost(canvas)
        }
    }

    /**
     * 在指定Canvas上直接绘制笔画
     */
    private fun drawStrokeOnCanvas(canvas: Canvas, stroke: Stroke) {
        val points = stroke.points
        if (points.size < 2) return

        strokePaint.color = stroke.color

        for (i in 1 until points.size) {
            val prev = points[i - 1]
            val curr = points[i]

            val pressureWidth = stroke.baseWidth *
                (MIN_WIDTH_RATIO + (MAX_WIDTH_RATIO - MIN_WIDTH_RATIO) * curr.pressure)
            strokePaint.strokeWidth = pressureWidth

            canvas.drawLine(prev.x, prev.y, curr.x, curr.y, strokePaint)
        }
    }

    /**
     * 完整帧渲染（背景+离屏缓冲+当前笔画）
     */
    private fun renderFrame() {
        if (!isRendering) return

        val canvas = holder.lockCanvas() ?: return
        try {
            canvas.drawColor(Color.WHITE)

            canvas.save()
            canvas.setMatrix(canvasMatrix)

            /* 绘制背景课件 */
            backgroundBitmap?.let { bmp ->
                canvas.drawBitmap(bmp, 0f, 0f, null)
            }

            /* 绘制离屏缓冲 */
            offscreenBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }

            canvas.restore()
        } finally {
            holder.unlockCanvasAndPost(canvas)
        }
    }

    /**
     * 重建离屏缓冲（Surface尺寸变化或撤销操作后）
     */
    private fun rebuildOffscreen() {
        val canvas = offscreenCanvas ?: return
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)

        completedStrokes.forEach { stroke ->
            drawStrokeToOffscreen(stroke)
        }

        renderFrame()
    }

    /* ==================== 橡皮擦 ==================== */

    /**
     * 在指定点擦除笔迹
     * 检查所有笔画中是否有点落在橡皮擦范围内
     */
    private fun eraseAtPoint(x: Float, y: Float) {
        val toRemove = mutableListOf<Stroke>()

        completedStrokes.forEach { stroke ->
            val hit = stroke.points.any { pt ->
                val dx = pt.x - x
                val dy = pt.y - y
                sqrt(dx * dx + dy * dy) < ERASER_RADIUS
            }
            if (hit) {
                toRemove.add(stroke)
            }
        }

        if (toRemove.isNotEmpty()) {
            toRemove.forEach { stroke ->
                completedStrokes.remove(stroke)
                pushUndoAction(CanvasAction.RemoveStroke(stroke))
            }
            rebuildOffscreen()
            Log.d(TAG, "橡皮擦删除${toRemove.size}条笔画")
        }
    }

    /* ==================== 撤销/重做 ==================== */

    /**
     * 记录操作到撤销栈
     */
    private fun pushUndoAction(action: CanvasAction) {
        undoStack.push(action)
        if (undoStack.size > MAX_UNDO_DEPTH) {
            undoStack.removeLast()
        }
        redoStack.clear()
    }

    /**
     * 撤销上一步操作
     */
    fun undo() {
        val action = undoStack.pollFirst() ?: return

        when (action) {
            is CanvasAction.AddStroke -> {
                completedStrokes.remove(action.stroke)
                redoStack.push(action)
            }
            is CanvasAction.RemoveStroke -> {
                completedStrokes.add(action.stroke)
                redoStack.push(action)
            }
            is CanvasAction.ClearAll -> {
                completedStrokes.addAll(action.strokes)
                redoStack.push(action)
            }
        }

        rebuildOffscreen()
        Log.d(TAG, "撤销操作, 剩余撤销=${undoStack.size}")
    }

    /**
     * 重做操作
     */
    fun redo() {
        val action = redoStack.pollFirst() ?: return

        when (action) {
            is CanvasAction.AddStroke -> {
                completedStrokes.add(action.stroke)
                undoStack.push(action)
            }
            is CanvasAction.RemoveStroke -> {
                completedStrokes.remove(action.stroke)
                undoStack.push(action)
            }
            is CanvasAction.ClearAll -> {
                completedStrokes.clear()
                undoStack.push(action)
            }
        }

        rebuildOffscreen()
        Log.d(TAG, "重做操作, 剩余重做=${redoStack.size}")
    }

    /**
     * 清空所有笔迹
     */
    fun clearAll() {
        if (completedStrokes.isEmpty()) return

        val backup = completedStrokes.toList()
        pushUndoAction(CanvasAction.ClearAll(backup))
        completedStrokes.clear()
        rebuildOffscreen()
        Log.i(TAG, "清空画布, ${backup.size}条笔画已备份到撤销栈")
    }

    /* ==================== 课件背景 ==================== */

    /**
     * 设置背景课件图片
     */
    fun setBackground(bitmap: Bitmap?) {
        backgroundBitmap?.recycle()
        backgroundBitmap = bitmap
        renderFrame()
    }

    /* ==================== 笔迹序列化 ==================== */

    /**
     * 将当前所有笔迹序列化为字节数组
     * 格式: [笔画数][笔画1数据][笔画2数据]...
     */
    fun serializeStrokes(): ByteArray {
        val bos = ByteArrayOutputStream()
        val dos = DataOutputStream(bos)

        dos.writeInt(completedStrokes.size)
        completedStrokes.forEach { stroke ->
            dos.writeInt(stroke.color)
            dos.writeFloat(stroke.baseWidth)
            dos.writeInt(stroke.points.size)
            stroke.points.forEach { pt ->
                dos.writeFloat(pt.x)
                dos.writeFloat(pt.y)
                dos.writeFloat(pt.pressure)
                dos.writeLong(pt.timestamp)
            }
        }

        dos.flush()
        Log.d(TAG, "笔迹序列化: ${completedStrokes.size}条笔画, ${bos.size()}字节")
        return bos.toByteArray()
    }

    /**
     * 从字节数组反序列化笔迹
     */
    fun deserializeStrokes(data: ByteArray) {
        val dis = DataInputStream(ByteArrayInputStream(data))

        completedStrokes.clear()
        val strokeCount = dis.readInt()
        repeat(strokeCount) {
            val color = dis.readInt()
            val width = dis.readFloat()
            val pointCount = dis.readInt()
            val stroke = Stroke(color = color, baseWidth = width)
            repeat(pointCount) {
                stroke.points.add(StrokePoint(
                    x = dis.readFloat(),
                    y = dis.readFloat(),
                    pressure = dis.readFloat(),
                    timestamp = dis.readLong()
                ))
            }
            completedStrokes.add(stroke)
        }

        rebuildOffscreen()
        Log.i(TAG, "笔迹反序列化: ${strokeCount}条笔画已加载")
    }
}
