/// 自然写互动课堂手机端应用软件 V1.0
/// 笔迹渲染组件 - CustomPainter实现高性能笔迹绘制与回放
///
/// 功能说明：
/// 1. 自定义CustomPainter实现60fps笔迹渲染
/// 2. 贝塞尔曲线平滑算法（消除锯齿）
/// 3. 压力感应笔锋效果（笔画粗细随压力变化）
/// 4. 笔迹回放动画（逐点重放书写过程）
/// 5. 多种笔迹颜色和宽度支持
/// 6. 笔迹缩放与平移（手势操作）
/// 7. 双缓冲渲染优化（离屏缓存已绘制内容）

import 'dart:async';
import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';

/* ========== 笔迹数据结构 ========== */

/// 笔迹点数据
class StrokePointData {
  final double x;
  final double y;
  final double pressure;
  final int timestamp;

  const StrokePointData({
    required this.x,
    required this.y,
    this.pressure = 0.5,
    required this.timestamp,
  });
}

/// 笔画数据（一次落笔到抬笔的完整路径）
class StrokeData {
  final List<StrokePointData> points;
  final Color color;
  final double baseWidth;

  StrokeData({
    required this.points,
    this.color = Colors.black,
    this.baseWidth = 2.0,
  });
}

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

/// 笔迹画布Widget - 展示笔迹渲染与回放
class StrokeCanvasWidget extends StatefulWidget {
  /// 笔迹数据列表
  final List<StrokeData> strokes;

  /// 是否启用回放模式
  final bool enableReplay;

  /// 回放速度倍率（1.0=原速，2.0=两倍速）
  final double replaySpeed;

  /// 画布背景色
  final Color backgroundColor;

  /// 是否显示坐标网格
  final bool showGrid;

  const StrokeCanvasWidget({
    super.key,
    required this.strokes,
    this.enableReplay = false,
    this.replaySpeed = 1.0,
    this.backgroundColor = Colors.white,
    this.showGrid = false,
  });

  @override
  State<StrokeCanvasWidget> createState() => _StrokeCanvasWidgetState();
}

class _StrokeCanvasWidgetState extends State<StrokeCanvasWidget>
    with SingleTickerProviderStateMixin {
  /// 回放动画控制器
  AnimationController? _replayController;

  /// 当前回放进度（0.0-1.0）
  double _replayProgress = 0.0;

  /// 缩放比例
  double _scale = 1.0;

  /// 平移偏移量
  Offset _offset = Offset.zero;

  /// 缩放手势起始比例
  double _previousScale = 1.0;

  /// 离屏缓存（已绘制的静态笔迹）
  ui.Image? _cachedImage;

  /// 是否需要重建缓存
  bool _needsRebuildCache = true;

  @override
  void initState() {
    super.initState();
    if (widget.enableReplay) {
      _startReplay();
    }
  }

  @override
  void didUpdateWidget(covariant StrokeCanvasWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.strokes != oldWidget.strokes) {
      _needsRebuildCache = true;
    }
    if (widget.enableReplay && !oldWidget.enableReplay) {
      _startReplay();
    }
  }

  @override
  void dispose() {
    _replayController?.dispose();
    _cachedImage?.dispose();
    super.dispose();
  }

  /// 启动笔迹回放动画
  void _startReplay() {
    // 计算总回放时长（基于笔迹时间跨度）
    if (widget.strokes.isEmpty) return;

    int totalDuration = 0;
    for (final stroke in widget.strokes) {
      if (stroke.points.isNotEmpty) {
        totalDuration = max(totalDuration,
            stroke.points.last.timestamp - stroke.points.first.timestamp);
      }
    }

    // 根据回放速度调整时长
    final durationMs = (totalDuration / widget.replaySpeed).round();

    _replayController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: max(durationMs, 1000)),
    );

    _replayController!.addListener(() {
      setState(() {
        _replayProgress = _replayController!.value;
      });
    });

    _replayController!.forward();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      // 缩放手势
      onScaleStart: (details) {
        _previousScale = _scale;
      },
      onScaleUpdate: (details) {
        setState(() {
          _scale = (_previousScale * details.scale).clamp(0.5, 5.0);
          _offset += details.focalPointDelta;
        });
      },
      // 双击重置缩放
      onDoubleTap: () {
        setState(() {
          _scale = 1.0;
          _offset = Offset.zero;
        });
      },
      child: ClipRect(
        child: CustomPaint(
          painter: StrokePainter(
            strokes: widget.strokes,
            replayProgress: widget.enableReplay ? _replayProgress : 1.0,
            scale: _scale,
            offset: _offset,
            backgroundColor: widget.backgroundColor,
            showGrid: widget.showGrid,
          ),
          size: Size.infinite,
        ),
      ),
    );
  }
}

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

/// CustomPainter实现 - 高性能笔迹绘制
class StrokePainter extends CustomPainter {
  final List<StrokeData> strokes;
  final double replayProgress;
  final double scale;
  final Offset offset;
  final Color backgroundColor;
  final bool showGrid;

  StrokePainter({
    required this.strokes,
    this.replayProgress = 1.0,
    this.scale = 1.0,
    this.offset = Offset.zero,
    this.backgroundColor = Colors.white,
    this.showGrid = false,
  });

  @override
  void paint(Canvas canvas, Size size) {
    // 绘制背景
    canvas.drawRect(
      Rect.fromLTWH(0, 0, size.width, size.height),
      Paint()..color = backgroundColor,
    );

    // 绘制网格（可选）
    if (showGrid) {
      _drawGrid(canvas, size);
    }

    // 保存画布状态，应用变换
    canvas.save();
    canvas.translate(offset.dx, offset.dy);
    canvas.scale(scale);

    // 计算当前回放应显示的总点数
    int totalPoints = 0;
    for (final stroke in strokes) {
      totalPoints += stroke.points.length;
    }
    final visiblePoints = (totalPoints * replayProgress).round();

    // 逐笔画渲染
    int pointCounter = 0;
    for (final stroke in strokes) {
      if (pointCounter >= visiblePoints) break;

      final strokeVisibleCount = min(
        stroke.points.length,
        visiblePoints - pointCounter,
      );

      if (strokeVisibleCount > 1) {
        _drawStroke(canvas, stroke, strokeVisibleCount);
      }

      pointCounter += stroke.points.length;
    }

    canvas.restore();
  }

  /// 绘制单个笔画（贝塞尔曲线平滑 + 压力笔锋）
  void _drawStroke(Canvas canvas, StrokeData stroke, int visibleCount) {
    if (visibleCount < 2) return;

    final paint = Paint()
      ..color = stroke.color
      ..strokeCap = StrokeCap.round
      ..strokeJoin = StrokeJoin.round
      ..style = PaintingStyle.stroke
      ..isAntiAlias = true;

    // 使用压力感应笔锋渲染
    for (int i = 1; i < visibleCount; i++) {
      final prev = stroke.points[i - 1];
      final curr = stroke.points[i];

      // 根据压力值计算笔画宽度
      // 压力越大，笔画越粗；落笔和抬笔时笔画变细（模拟笔锋效果）
      final pressureWidth = _calculatePressureWidth(
        stroke.baseWidth, prev.pressure, curr.pressure,
        i, visibleCount,
      );

      paint.strokeWidth = pressureWidth;

      if (i >= 2 && i < visibleCount) {
        // 三次贝塞尔曲线平滑（消除折线锯齿）
        final prevPrev = stroke.points[i - 2];
        final cp1x = prev.x + (curr.x - prevPrev.x) / 6.0;
        final cp1y = prev.y + (curr.y - prevPrev.y) / 6.0;
        final cp2x = curr.x - (curr.x - prev.x) / 6.0;
        final cp2y = curr.y - (curr.y - prev.y) / 6.0;

        final path = Path()
          ..moveTo(prev.x, prev.y)
          ..cubicTo(cp1x, cp1y, cp2x, cp2y, curr.x, curr.y);

        canvas.drawPath(path, paint);
      } else {
        // 前两个点使用直线连接
        canvas.drawLine(
          ui.Offset(prev.x, prev.y),
          ui.Offset(curr.x, curr.y),
          paint,
        );
      }
    }
  }

  /// 根据压力值计算笔画宽度（模拟笔锋效果）
  /// 落笔时宽度从细变粗，行笔中根据压力变化，抬笔时由粗变细
  double _calculatePressureWidth(
    double baseWidth,
    double prevPressure,
    double currPressure,
    int index,
    int totalPoints,
  ) {
    // 压力插值
    final avgPressure = (prevPressure + currPressure) / 2.0;

    // 基础宽度根据压力缩放（0.3x - 2.0x）
    double width = baseWidth * (0.3 + avgPressure * 1.7);

    // 落笔效果：前5个点逐渐增加宽度
    if (index < 5) {
      width *= (index / 5.0);
    }

    // 抬笔效果：最后5个点逐渐减小宽度
    final remaining = totalPoints - index;
    if (remaining < 5) {
      width *= (remaining / 5.0);
    }

    return max(width, 0.5); // 最小宽度0.5
  }

  /// 绘制辅助网格
  void _drawGrid(Canvas canvas, Size size) {
    final gridPaint = Paint()
      ..color = Colors.grey.withValues(alpha: 0.2)
      ..strokeWidth = 0.5;

    const gridSize = 20.0;

    // 竖线
    for (double x = 0; x < size.width; x += gridSize) {
      canvas.drawLine(
        ui.Offset(x, 0),
        ui.Offset(x, size.height),
        gridPaint,
      );
    }

    // 横线
    for (double y = 0; y < size.height; y += gridSize) {
      canvas.drawLine(
        ui.Offset(0, y),
        ui.Offset(size.width, y),
        gridPaint,
      );
    }
  }

  @override
  bool shouldRepaint(covariant StrokePainter oldDelegate) {
    return oldDelegate.replayProgress != replayProgress ||
        oldDelegate.strokes != strokes ||
        oldDelegate.scale != scale ||
        oldDelegate.offset != offset;
  }
}

/* ========== 笔迹工具函数 ========== */

/// 笔迹数据工具类
class StrokeUtils {
  /// 道格拉斯-普克算法简化笔迹点（减少数据量）
  /// epsilon: 简化阈值（越大简化越多）
  static List<StrokePointData> simplifyStroke(
    List<StrokePointData> points, {
    double epsilon = 1.0,
  }) {
    if (points.length <= 2) return points;

    // 找到距离首尾连线最远的点
    double maxDistance = 0;
    int maxIndex = 0;

    final first = points.first;
    final last = points.last;

    for (int i = 1; i < points.length - 1; i++) {
      final d = _perpendicularDistance(points[i], first, last);
      if (d > maxDistance) {
        maxDistance = d;
        maxIndex = i;
      }
    }

    // 如果最大距离大于阈值，递归简化
    if (maxDistance > epsilon) {
      final left = simplifyStroke(points.sublist(0, maxIndex + 1), epsilon: epsilon);
      final right = simplifyStroke(points.sublist(maxIndex), epsilon: epsilon);
      return [...left.sublist(0, left.length - 1), ...right];
    } else {
      return [first, last];
    }
  }

  /// 计算点到线段的垂直距离
  static double _perpendicularDistance(
    StrokePointData point,
    StrokePointData lineStart,
    StrokePointData lineEnd,
  ) {
    final dx = lineEnd.x - lineStart.x;
    final dy = lineEnd.y - lineStart.y;

    if (dx == 0 && dy == 0) {
      return sqrt(pow(point.x - lineStart.x, 2) + pow(point.y - lineStart.y, 2));
    }

    final t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) /
        (dx * dx + dy * dy);
    final clampedT = t.clamp(0.0, 1.0);

    final closestX = lineStart.x + clampedT * dx;
    final closestY = lineStart.y + clampedT * dy;

    return sqrt(pow(point.x - closestX, 2) + pow(point.y - closestY, 2));
  }

  /// 计算笔迹边界框（用于视窗适配）
  static Rect calculateBounds(List<StrokeData> strokes) {
    double minX = double.infinity, minY = double.infinity;
    double maxX = double.negativeInfinity, maxY = double.negativeInfinity;

    for (final stroke in strokes) {
      for (final point in stroke.points) {
        minX = min(minX, point.x);
        minY = min(minY, point.y);
        maxX = max(maxX, point.x);
        maxY = max(maxY, point.y);
      }
    }

    if (minX == double.infinity) return Rect.zero;
    return Rect.fromLTRB(minX, minY, maxX, maxY);
  }

  /// 计算笔迹书写速度（像素/毫秒）
  static double calculateWritingSpeed(List<StrokePointData> points) {
    if (points.length < 2) return 0;

    double totalDistance = 0;
    for (int i = 1; i < points.length; i++) {
      totalDistance += sqrt(
        pow(points[i].x - points[i - 1].x, 2) +
        pow(points[i].y - points[i - 1].y, 2),
      );
    }

    final totalTime = points.last.timestamp - points.first.timestamp;
    return totalTime > 0 ? totalDistance / totalTime : 0;
  }
}
