// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:ui';

import 'recorder.dart';
import 'test_data.dart';

/// The height of each row.
const double kRowHeight = 20.0;

/// Number of rows.
const int kRows = 100;

/// Number of columns.
const int kColumns = 10;

/// The amount the picture is scrolled on every iteration of the benchmark.
const double kScrollDelta = 2.0;

/// Draws one complex picture, then moves a clip around it simulating scrolling
/// large static content.
///
/// This benchmark measures how efficient we are at taking advantage of the
/// static picture when all that changes is the clip.
///
/// See also:
///
/// * `bench_text_out_of_picture_bounds.dart`, which measures a volatile
///   picture with a static clip.
/// * https://github.com/flutter/flutter/issues/42987, which this benchmark is
///   based on.
class BenchDynamicClipOnStaticPicture extends SceneBuilderRecorder {
  BenchDynamicClipOnStaticPicture() : super(name: benchmarkName) {
    // If the scrollable extent is too small, the benchmark may end up
    // scrolling the picture out of the clip area entirely, resulting in
    // bogus metric vaules.
    const double maxScrollExtent = kTotalSampleCount * kScrollDelta;
    const double pictureHeight = kRows * kRowHeight;
    if (maxScrollExtent > pictureHeight) {
      throw Exception(
        'Bad combination of constant values kRowHeight, kRows, and '
        'kScrollData. With these numbers there is risk that the picture '
        'will scroll out of the clip entirely. To fix the issue reduce '
        'kScrollDelta, or increase either kRows or kRowHeight.'
      );
    }

    // Create one static picture, then never change it again.
    const Color black = Color.fromARGB(255, 0, 0, 0);
    final PictureRecorder pictureRecorder = PictureRecorder();
    final Canvas canvas = Canvas(pictureRecorder);
    screenSize = window.physicalSize / window.devicePixelRatio;
    clipSize = Size(
      screenSize.width / 2,
      screenSize.height / 5,
    );
    final double cellWidth = screenSize.width / kColumns;

    final List<Paragraph> paragraphs = generateLaidOutParagraphs(
      paragraphCount: 500,
      minWordCountPerParagraph: 3,
      maxWordCountPerParagraph: 3,
      widthConstraint: cellWidth,
      color: black,
    );

    int paragraphCounter = 0;
    double yOffset = 0.0;
    for (int row = 0; row < kRows; row += 1) {
      for (int column = 0; column < kColumns; column += 1) {
        final double left = cellWidth * column;
        canvas.save();
        canvas.clipRect(Rect.fromLTWH(
          left,
          yOffset,
          cellWidth,
          20.0,
        ));
        canvas.drawParagraph(
          paragraphs[paragraphCounter % paragraphs.length],
          Offset(left, yOffset),
        );
        canvas.restore();
        paragraphCounter += 1;
      }
      yOffset += kRowHeight;
    }

    picture = pictureRecorder.endRecording();
  }

  static const String benchmarkName = 'dynamic_clip_on_static_picture';

  Size screenSize;
  Size clipSize;
  Picture picture;
  double pictureVerticalOffset = 0.0;

  @override
  void onDrawFrame(SceneBuilder sceneBuilder) {
    // Render the exact same picture, but offset it as if it's being scrolled.
    // This will move the clip along the Y axis in picture's local coordinates
    // causing a repaint. If we're not efficient at managing clips and/or
    // repaints this will jank (see https://github.com/flutter/flutter/issues/42987).
    final Rect clip = Rect.fromLTWH(0.0, 0.0, clipSize.width, clipSize.height);
    sceneBuilder.pushClipRect(clip);
    sceneBuilder.pushOffset(0.0, pictureVerticalOffset);
    sceneBuilder.addPicture(Offset.zero, picture);
    sceneBuilder.pop();
    sceneBuilder.pop();
    pictureVerticalOffset -= kScrollDelta;
  }
}
