// lib/editor/render/static_ink_painter.dart
//
// CustomPainter for the committed-stroke (static) layer.
//
// paint() gets-or-builds a ui.Picture of all committed strokes keyed by
// store.revision, then delegates to canvas.drawPicture — so as long as the
// revision is unchanged the raster thread replays the same display list at
// zero CPU cost.
//
// shouldRepaint() is O(1): it compares the revision int and pageSize only.

import 'dart:ui' as ui;

import 'package:flutter/material.dart';

import '../engine/stroke_geometry.dart';
import '../engine/stroke_store.dart';
import 'ink_picture_cache.dart';

/// Paints the committed ink layer by recording strokes into a [ui.Picture]
/// once per [StrokeStore.revision] and caching it in [InkPictureCache].
///
/// Place this inside a [RepaintBoundary] / [CustomPaint] pair. The sibling
/// [LiveInkPainter] handles the in-progress stroke in a separate layer.
class StaticInkPainter extends CustomPainter {
  StaticInkPainter({
    required this.hostId,
    required this.store,
    required this.pageSize,
    required this.cache,
    this.thinning = kDefaultPenThinning,
  }) : revision = store.revision;

  final String hostId;
  final StrokeStore store;
  final Size pageSize;
  final InkPictureCache cache;

  /// perfect_freehand pressure→width response (from `PenConfig.pressureSensitivity`).
  /// Folded into the cache key + [shouldRepaint] so a sensitivity change can't
  /// replay a stale Picture built at the old thinning.
  final double thinning;

  /// Revision snapshot captured at construction time. Used by [shouldRepaint]
  /// so two painters built at different revisions compare correctly even when
  /// they share the same [StrokeStore] instance.
  final int revision;

  @override
  void paint(Canvas canvas, Size size) {
    // thinning is part of the cache identity (different thinning ⇒ different
    // outline) so it MUST be in the key, not just shouldRepaint.
    final cacheKey = '$hostId#${thinning.toStringAsFixed(4)}';
    final picture = cache.getOrBuild(cacheKey, store.revision, pageSize, () {
      final recorder = ui.PictureRecorder();
      final rec = Canvas(recorder);
      for (final stroke in store.committed) {
        final path = buildStrokeOutline(stroke, pageSize,
            isComplete: true, thinning: thinning);
        if (path.getBounds().isEmpty) continue;
        rec.drawPath(
          path,
          Paint()
            ..color = Color(stroke.color)
            ..style = PaintingStyle.fill
            ..isAntiAlias = true,
        );
      }
      return recorder.endRecording();
    });

    canvas.drawPicture(picture);
  }

  @override
  bool shouldRepaint(StaticInkPainter old) =>
      old.revision != store.revision ||
      old.pageSize != pageSize ||
      old.thinning != thinning;
}
