import 'dart:ui';

/// Represents a single stroke/line drawn on the canvas.
///
/// Points are stored in normalized coordinates (0.0 to 1.0) where:
/// - (0, 0) is the top-left corner of the drawing area
/// - (1, 1) is the bottom-right corner of the drawing area
///
/// This allows drawings to scale correctly when the canvas size changes.
class DrawingLine {
  /// The minimal squared distance between to points which are normalized so that this point is allowed to be added to the line
  static const minNormalizedPointDistanceSquared = 0.001 * 0.001;

  /// Points in normalized coordinates (0.0 to 1.0)
  final List<Offset> points;

  /// Color of the line (stored as ARGB integer for JSON serialization)
  final Color color;

  /// Stroke width in normalized units (relative to canvas width)
  /// A value of 0.01 means the stroke is 1% of the canvas width
  final double strokeWidth;

  const DrawingLine({
    required this.points,
    required this.color,
    required this.strokeWidth,
  });

  /// Creates a DrawingLine from JSON data.
  factory DrawingLine.fromJson(Map<String, dynamic> json) {
    final pointsList = (json['points'] as List)
        .map(
          (p) => Offset((p['x'] as num).toDouble(), (p['y'] as num).toDouble()),
        )
        .toList();

    return DrawingLine(
      points: pointsList,
      color: Color(json['color'] as int),
      strokeWidth: (json['strokeWidth'] as num).toDouble(),
    );
  }

  /// Converts this line to a JSON-serializable map.
  Map<String, dynamic> toJson() {
    return {
      'points': points.map((p) => {'x': p.dx, 'y': p.dy}).toList(),
      'color': color.toARGB32(),
      'strokeWidth': strokeWidth,
    };
  }

  /// Creates a copy of this line with an additional point.
  DrawingLine addPoint(Offset normalizedPoint) {
    return DrawingLine(
      points: [...points, normalizedPoint],
      color: color,
      strokeWidth: strokeWidth,
    );
  }

  bool isPointTooClose(Offset nextNormalizedPoint) {
    if (points.isEmpty) {
      return false;
    }
    return (points.last - nextNormalizedPoint).distanceSquared <
        minNormalizedPointDistanceSquared;
  }

  /// Creates a copy with updated points.
  DrawingLine copyWith({
    List<Offset>? points,
    Color? color,
    double? strokeWidth,
  }) {
    return DrawingLine(
      points: points ?? this.points,
      color: color ?? this.color,
      strokeWidth: strokeWidth ?? this.strokeWidth,
    );
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    if (other is! DrawingLine) return false;
    if (points.length != other.points.length) return false;
    if (color != other.color) return false;
    if (strokeWidth != other.strokeWidth) return false;
    for (int i = 0; i < points.length; i++) {
      if (points[i] != other.points[i]) return false;
    }
    return true;
  }

  @override
  int get hashCode => Object.hash(Object.hashAll(points), color, strokeWidth);
}
