// lib/editor/notebook/ink_stroke_adapter.dart
//
// Bridge between the legacy note/ppt storage model (`InkStroke`, ABSOLUTE pixel
// coordinates, `PenTool`) and the pen-first canvas model (`PenStroke`,
// NORMALIZED [0,1] coordinates, `PenStrokeKind`). The pen-first canvas is the
// single performant inking engine, so notes and slides are rebuilt on top of it
// and persisted back as `InkStroke` via this adapter.
//
// Coordinates are normalized against a logical page rectangle: ink absolute
// (x,y) -> pen (x/pageW, y/pageH) and back. Stroke width is likewise expressed
// as a fraction of the page width on the pen side and as absolute pixels on the
// ink side. Only freehand pen/highlighter strokes round-trip; shape/text
// `PenTool`s have no pen-canvas representation and are dropped (the pen-first
// note is handwriting-first — see the rebuild roadmap).

import 'dart:ui' show Size;

import '../../models/ink_point.dart';
import '../../models/ink_stroke.dart';
import '../../models/pen_tool.dart';
import '../canvas/pen_stroke.dart';
import '../engine/brush.dart';

/// Logical page rectangle a blank note is inked on (portrait, ~A4 √2 ratio).
/// Strokes are normalized against this so they stay pinned under zoom/pan.
const Size kNoteLogicalPage = Size(1000, 1414);

/// True when [tool] is a freehand mark the pen canvas can render
/// (pen/marker/highlighter). Shapes and text are not representable.
bool isFreehandTool(PenTool tool) =>
    tool == PenTool.pen ||
    tool == PenTool.marker ||
    tool == PenTool.highlighter;

/// Maps an ink [PenTool] to the pen-canvas stroke kind.
PenStrokeKind penKindFromTool(PenTool tool) =>
    tool == PenTool.highlighter ? PenStrokeKind.highlighter : PenStrokeKind.pen;

/// Maps a pen-canvas stroke kind back to a [PenTool].
PenTool toolFromPenKind(PenStrokeKind kind) =>
    kind == PenStrokeKind.highlighter ? PenTool.highlighter : PenTool.pen;

/// Convert a stored [InkStroke] (absolute px on [page]) to a [PenStroke]
/// (normalized). Returns null for non-freehand strokes (shapes/text), which the
/// pen canvas cannot draw.
PenStroke? penStrokeFromInk(InkStroke s, Size page) {
  if (!isFreehandTool(s.tool)) return null;
  if (s.points.isEmpty) return null;
  final w = page.width <= 0 ? 1.0 : page.width;
  final h = page.height <= 0 ? 1.0 : page.height;
  return PenStroke(
    points: [
      for (final p in s.points)
        PenPoint(p.x / w, p.y / h, p.pressure, tilt: p.tilt),
    ],
    color: s.color,
    width: s.strokeWidth / w,
    kind: penKindFromTool(s.tool),
    // Brush isn't persisted yet (TODO(brush-persist)); derive from the tool so
    // a loaded highlighter renders with the flat highlighter brush and pens
    // fall back to the fountainPen default.
    brush: s.tool == PenTool.highlighter
        ? BrushKind.highlighter
        : BrushKind.fountainPen,
  );
}

/// Convert a freshly drawn [PenStroke] (normalized) back to an [InkStroke]
/// (absolute px on [page]) for persistence. [id] and [createdAt] come from the
/// caller (uuid + clock) so this stays pure/deterministic.
InkStroke inkStrokeFromPen(
  PenStroke s,
  Size page, {
  required String id,
  required DateTime createdAt,
}) {
  final w = page.width <= 0 ? 1.0 : page.width;
  final h = page.height <= 0 ? 1.0 : page.height;
  return InkStroke(
    id: id,
    points: [
      for (final p in s.points)
        InkPoint(
          x: p.x * w,
          y: p.y * h,
          pressure: p.pressure ?? 0.5,
          tilt: p.tilt ?? 0.0,
          timestamp: 0,
        ),
    ],
    tool: toolFromPenKind(s.kind),
    color: s.color,
    strokeWidth: s.width * w,
    createdAt: createdAt,
  );
}

/// Convert a list of stored ink strokes to pen strokes, dropping the ones the
/// canvas cannot represent (shapes/text). Order is preserved.
List<PenStroke> penStrokesFromInk(Iterable<InkStroke> strokes, Size page) =>
    [for (final s in strokes) penStrokeFromInk(s, page)]
        .whereType<PenStroke>()
        .toList();
