// Copyright 2013 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:math' as math;
import 'dart:typed_data';

import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/ui.dart' as ui;
import 'package:web_engine_tester/golden_tester.dart';

import '../common/test_initialization.dart';
import 'utils.dart';

void main() {
  internalBootstrapBrowserTest(() => testMain);
}

const ui.Rect kBlueSquareRegion = ui.Rect.fromLTRB(0, 0, 25, 25);
const ui.Rect kRedCircleRegion = ui.Rect.fromLTRB(25, 0, 50, 25);
const ui.Rect kMagentaStarRegion = ui.Rect.fromLTRB(0, 25, 30, 55);
const ui.Rect kGreenSquiggleRegion = ui.Rect.fromLTRB(30, 25, 50, 55);
const ui.Rect kTotalAtlasRegion = ui.Rect.fromLTRB(0, 0, 55, 55);

ui.Image generateAtlas() {
  final ui.PictureRecorder recorder = ui.PictureRecorder();
  final ui.Canvas canvas = ui.Canvas(recorder);

  canvas.drawColor(const ui.Color(0), ui.BlendMode.src);

  // Draw the square
  canvas.save();
  canvas.clipRect(kBlueSquareRegion);
  canvas.drawRect(
    // Inset the square by one pixel so it doesn't bleed into the other sprites.
    kBlueSquareRegion.deflate(1.0),
    ui.Paint()..color = const ui.Color(0xFF0000FF)
  );
  canvas.restore();

  // Draw the circle
  canvas.save();
  canvas.clipRect(kRedCircleRegion);
  canvas.drawCircle(
    kRedCircleRegion.center,
    // Make the circle one pixel smaller than the bounds to it doesn't bleed
    // into the other shapes.
    (kRedCircleRegion.width / 2.0) - 1.0,
    ui.Paint()..color = const ui.Color(0xFFFF0000));
  canvas.restore();

  // Draw the star
  canvas.save();
  canvas.clipRect(kMagentaStarRegion);
  final ui.Offset starCenter = kMagentaStarRegion.center;

  // Make the star one pixel smaller than the bounds so that it doesn't bleed
  // into the other shapes.
  final double radius = (kMagentaStarRegion.height / 2.0) - 1.0;

  // Start at the top (rotated 90 degrees)
  double theta = -math.pi / 2.0;

  // Rotate two fifths of the circle each time
  const double rotation = 4.0 * math.pi / 5.0;
  final ui.Path starPath = ui.Path();
  starPath.moveTo(
    starCenter.dx + radius * math.cos(theta),
    starCenter.dy + radius * math.sin(theta)
  );
  for (int i = 0; i < 5; i++) {
    theta += rotation;
    starPath.lineTo(
      starCenter.dx + radius * math.cos(theta),
      starCenter.dy + radius * math.sin(theta)
    );
  }
  canvas.drawPath(
    starPath,
    ui.Paint()
      ..color = const ui.Color(0xFFFF00FF)
      ..style = ui.PaintingStyle.fill
  );
  canvas.restore();

  // Draw the Squiggle
  canvas.save();
  canvas.clipRect(kGreenSquiggleRegion);
  final ui.Path squigglePath = ui.Path();
  squigglePath.moveTo(kGreenSquiggleRegion.topCenter.dx, kGreenSquiggleRegion.topCenter.dy + 2.0);
  squigglePath.cubicTo(
    kGreenSquiggleRegion.left - 10.0, kGreenSquiggleRegion.top + kGreenSquiggleRegion.height * 0.33,
    kGreenSquiggleRegion.right + 10.0, kGreenSquiggleRegion.top + kGreenSquiggleRegion.height * 0.66,
    kGreenSquiggleRegion.bottomCenter.dx, kGreenSquiggleRegion.bottomCenter.dy - 2.0
  );
  canvas.drawPath(
    squigglePath,
    ui.Paint()
      ..color = const ui.Color(0xFF00FF00)
      ..style = ui.PaintingStyle.stroke
      ..strokeWidth = 5.0
  );
  canvas.restore();

  final ui.Picture picture = recorder.endRecording();
  return picture.toImageSync(
    kTotalAtlasRegion.width.toInt(),
    kTotalAtlasRegion.height.toInt()
  );
}

Future<void> testMain() async {
  setUpUnitTests(
    setUpTestViewDimensions: false,
  );

  const ui.Rect region = ui.Rect.fromLTWH(0, 0, 300, 300);
  test('drawAtlas', () async {
    final ui.PictureRecorder recorder = ui.PictureRecorder();
    final ui.Canvas canvas = ui.Canvas(recorder, region);
    final ui.Image atlas = generateAtlas();
    final List<ui.RSTransform> transforms = List<ui.RSTransform>.generate(
      12, (int index) {
        const double radius = 100;
        const double rotation = math.pi / 6.0;
        final double angle = rotation * index;
        final double scos = math.sin(angle);
        final double ssin = math.cos(angle);
        return ui.RSTransform(
          scos,
          ssin,
          region.center.dx + radius * scos,
          region.center.dy + radius * ssin,
        );
      }
    );
    final List<ui.Rect> rects = <ui.Rect>[
      kBlueSquareRegion,
      kRedCircleRegion,
      kMagentaStarRegion,
      kGreenSquiggleRegion,
      kBlueSquareRegion,
      kRedCircleRegion,
      kMagentaStarRegion,
      kGreenSquiggleRegion,
      kBlueSquareRegion,
      kRedCircleRegion,
      kMagentaStarRegion,
      kGreenSquiggleRegion,
    ];
    canvas.drawAtlas(
      atlas, transforms, rects, null, null, null, ui.Paint());

    await drawPictureUsingCurrentRenderer(recorder.endRecording());
    await matchGoldenFile('ui_draw_atlas.png', region: region);
  }, skip: isHtml); // HTML renderer doesn't support drawAtlas

  test('drawAtlasRaw', () async {
    final ui.PictureRecorder recorder = ui.PictureRecorder();
    final ui.Canvas canvas = ui.Canvas(recorder, region);
    final ui.Image atlas = generateAtlas();
    final Float32List transforms = Float32List(12 * 4);
    for (int i = 0; i < 12; i++) {
      const double radius = 100;
      const double rotation = math.pi / 6.0;
      final double angle = rotation * i;
      final double scos = math.sin(angle);
      final double ssin = math.cos(angle);
      transforms[i * 4] = scos;
      transforms[i * 4 + 1] = ssin;
      transforms[i * 4 + 2] = region.center.dx + radius * scos;
      transforms[i * 4 + 3] = region.center.dy + radius * ssin;
    }
    final List<ui.Rect> rects = <ui.Rect>[
      kBlueSquareRegion,
      kRedCircleRegion,
      kMagentaStarRegion,
      kGreenSquiggleRegion,
      kBlueSquareRegion,
      kRedCircleRegion,
      kMagentaStarRegion,
      kGreenSquiggleRegion,
      kBlueSquareRegion,
      kRedCircleRegion,
      kMagentaStarRegion,
      kGreenSquiggleRegion,
    ];
    final Float32List rawRects = Float32List(rects.length * 4);
    for (int i = 0; i < rects.length; i++) {
      rawRects[i * 4] = rects[i].left;
      rawRects[i * 4 + 1] = rects[i].top;
      rawRects[i * 4 + 2] = rects[i].right;
      rawRects[i * 4 + 3] = rects[i].bottom;
    }

    final Int32List colors = Int32List(12);
    for (int i = 0; i < 12; i++) {
      final int rgb = 0xFF << (8 * (i % 3));
      colors[i] = 0xFF000000 | rgb;
    }
    canvas.drawRawAtlas(
      atlas,
      transforms,
      rawRects,
      colors,
      ui.BlendMode.dstIn,
      null,
      ui.Paint()
    );

    await drawPictureUsingCurrentRenderer(recorder.endRecording());
    await matchGoldenFile('ui_draw_atlas_raw.png', region: region);
  }, skip: isHtml); // HTML renderer doesn't support drawAtlas
}
