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

import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.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);
}

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

  const ui.Rect region = ui.Rect.fromLTWH(0, 0, 128, 128);

  Future<void> drawTestImageWithPaint(ui.Paint paint) async {
    final ui.Codec codec = await renderer.instantiateImageCodecFromUrl(
      Uri(path: '/test_images/mandrill_128.png')
    );
    expect(codec.frameCount, 1);

    final ui.FrameInfo info = await codec.getNextFrame();
    final ui.Image image = info.image;
    expect(image.width, 128);
    expect(image.height, 128);
    final ui.PictureRecorder recorder = ui.PictureRecorder();
    final ui.Canvas canvas = ui.Canvas(recorder, region);
    canvas.drawImage(
      image,
      ui.Offset.zero,
      paint,
    );

    await drawPictureUsingCurrentRenderer(recorder.endRecording());
  }

  test('blur filter', () async {
    await drawTestImageWithPaint(ui.Paint()..imageFilter = ui.ImageFilter.blur(
        sigmaX: 5.0,
        sigmaY: 5.0,
    ));
    await matchGoldenFile('ui_filter_blur_imagefilter.png', region: region);
  });

  test('dilate filter', () async {
    await drawTestImageWithPaint(ui.Paint()..imageFilter = ui.ImageFilter.dilate(
        radiusX: 5.0,
        radiusY: 5.0,
    ));
    await matchGoldenFile('ui_filter_dilate_imagefilter.png', region: region);
  }, skip: !isSkwasm); // Only skwasm supports dilate filter right now

  test('erode filter', () async {
    await drawTestImageWithPaint(ui.Paint()..imageFilter = ui.ImageFilter.erode(
        radiusX: 5.0,
        radiusY: 5.0,
    ));
    await matchGoldenFile('ui_filter_erode_imagefilter.png', region: region);
  }, skip: !isSkwasm); // Only skwasm supports erode filter

  test('matrix filter', () async {
    await drawTestImageWithPaint(ui.Paint()..imageFilter = ui.ImageFilter.matrix(
      Matrix4.rotationZ(math.pi / 6).toFloat64(),
      filterQuality: ui.FilterQuality.high,
    ));
    await matchGoldenFile('ui_filter_matrix_imagefilter.png', region: region);
  });

  test('composed filters', () async {
    final ui.ImageFilter filter = ui.ImageFilter.compose(
      outer: ui.ImageFilter.matrix(
        Matrix4.rotationZ(math.pi / 6).toFloat64(),
        filterQuality: ui.FilterQuality.high,
      ),
      inner: ui.ImageFilter.blur(
        sigmaX: 5.0,
        sigmaY: 5.0,
      )
    );
    await drawTestImageWithPaint(ui.Paint()..imageFilter = filter);
    await matchGoldenFile('ui_filter_composed_imagefilters.png', region: region);
  }, skip: !isSkwasm); // Only Skwasm implements composable filters right now.

  test('compose with colorfilter', () async {
    final ui.ImageFilter filter = ui.ImageFilter.compose(
      outer: const ui.ColorFilter.mode(
        ui.Color.fromRGBO(0, 0, 255, 128),
        ui.BlendMode.srcOver,
      ),
      inner: ui.ImageFilter.blur(
        sigmaX: 5.0,
        sigmaY: 5.0,
      )
    );
    await drawTestImageWithPaint(ui.Paint()..imageFilter = filter);
    await matchGoldenFile('ui_filter_composed_colorfilter.png', region: region);
  }, skip: !isSkwasm); // Only Skwasm implements composable filters right now.

  test('color filter as image filter', () async {
    const ui.ColorFilter colorFilter = ui.ColorFilter.mode(
      ui.Color.fromRGBO(0, 0, 255, 128),
      ui.BlendMode.srcOver,
    );
    await drawTestImageWithPaint(ui.Paint()..imageFilter = colorFilter);
    await matchGoldenFile('ui_filter_colorfilter_as_imagefilter.png', region: region);
    expect(colorFilter.toString(), 'ColorFilter.mode(Color(0x800000ff), BlendMode.srcOver)');
  });

  test('mode color filter', () async {
    const ui.ColorFilter colorFilter = ui.ColorFilter.mode(
      ui.Color.fromRGBO(0, 0, 255, 128),
      ui.BlendMode.srcOver,
    );
    await drawTestImageWithPaint(ui.Paint()..colorFilter = colorFilter);
    await matchGoldenFile('ui_filter_mode_colorfilter.png', region: region);
    expect(colorFilter.toString(), 'ColorFilter.mode(Color(0x800000ff), BlendMode.srcOver)');
  });

  test('linearToSRGBGamma color filter', () async {
    const ui.ColorFilter colorFilter = ui.ColorFilter.linearToSrgbGamma();
    await drawTestImageWithPaint(ui.Paint()..colorFilter = colorFilter);
    await matchGoldenFile('ui_filter_linear_to_srgb_colorfilter.png', region: region);
    expect(colorFilter.toString(), 'ColorFilter.linearToSrgbGamma()');
  }, skip: isHtml); // HTML renderer hasn't implemented this.

  test('srgbToLinearGamma color filter', () async {
    const ui.ColorFilter colorFilter = ui.ColorFilter.srgbToLinearGamma();
    await drawTestImageWithPaint(ui.Paint()..colorFilter = colorFilter);
    await matchGoldenFile('ui_filter_srgb_to_linear_colorfilter.png', region: region);
    expect(colorFilter.toString(), 'ColorFilter.srgbToLinearGamma()');
  }, skip: isHtml); // HTML renderer hasn't implemented this.

  test('matrix color filter', () async {
    const ui.ColorFilter sepia = ui.ColorFilter.matrix(<double>[
      0.393, 0.769, 0.189, 0, 0,
      0.349, 0.686, 0.168, 0, 0,
      0.272, 0.534, 0.131, 0, 0,
      0,     0,     0,     1, 0,
    ]);
    await drawTestImageWithPaint(ui.Paint()..colorFilter = sepia);
    await matchGoldenFile('ui_filter_matrix_colorfilter.png', region: region);
    expect(sepia.toString(), startsWith('ColorFilter.matrix([0.393, 0.769, 0.189, '));
  });

  test('invert colors', () async {
    await drawTestImageWithPaint(ui.Paint()..invertColors = true);
    await matchGoldenFile('ui_filter_invert_colors.png', region: region);
  });

  test('invert colors with color filter', () async {
    const ui.ColorFilter sepia = ui.ColorFilter.matrix(<double>[
      0.393, 0.769, 0.189, 0, 0,
      0.349, 0.686, 0.168, 0, 0,
      0.272, 0.534, 0.131, 0, 0,
      0,     0,     0,     1, 0,
    ]);

    await drawTestImageWithPaint(ui.Paint()
      ..invertColors = true
      ..colorFilter = sepia);
    await matchGoldenFile('ui_filter_invert_colors_with_colorfilter.png', region: region);
  });

  test('mask filter', () async {
    const ui.MaskFilter maskFilter = ui.MaskFilter.blur(ui.BlurStyle.normal, 25.0);
    await drawTestImageWithPaint(ui.Paint()..maskFilter = maskFilter);
    await matchGoldenFile('ui_filter_blur_maskfilter.png', region: region);
  });
}
