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

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';

class TestCanvas implements Canvas {
  final List<Invocation> invocations = <Invocation>[];

  @override
  void noSuchMethod(Invocation invocation) {
    invocations.add(invocation);
  }
}

void main() {
  // the textDirection values below are intentionally sometimes different and
  // sometimes the same as the layoutDirection, to make sure that they don't
  // affect the layout.

  test('A Banner with a location of topStart paints in the top left (LTR)', () {
    final BannerPainter bannerPainter = BannerPainter(
      message: 'foo',
      textDirection: TextDirection.rtl,
      location: BannerLocation.topStart,
      layoutDirection: TextDirection.ltr,
    );

    final TestCanvas canvas = TestCanvas();

    bannerPainter.paint(canvas, const Size(1000.0, 1000.0));

    final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #translate;
    });

    expect(translateCommand, isNotNull);
    expect(translateCommand.positionalArguments[0], lessThan(100.0));
    expect(translateCommand.positionalArguments[1], lessThan(100.0));

    final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #rotate;
    });

    expect(rotateCommand, isNotNull);
    expect(rotateCommand.positionalArguments[0], equals(-math.pi / 4.0));
  });

  test('A Banner with a location of topStart paints in the top right (RTL)', () {
    final BannerPainter bannerPainter = BannerPainter(
      message: 'foo',
      textDirection: TextDirection.ltr,
      location: BannerLocation.topStart,
      layoutDirection: TextDirection.rtl,
    );

    final TestCanvas canvas = TestCanvas();

    bannerPainter.paint(canvas, const Size(1000.0, 1000.0));

    final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #translate;
    });

    expect(translateCommand, isNotNull);
    expect(translateCommand.positionalArguments[0], greaterThan(900.0));
    expect(translateCommand.positionalArguments[1], lessThan(100.0));

    final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #rotate;
    });

    expect(rotateCommand, isNotNull);
    expect(rotateCommand.positionalArguments[0], equals(math.pi / 4.0));
  });

  test('A Banner with a location of topEnd paints in the top right (LTR)', () {
    final BannerPainter bannerPainter = BannerPainter(
      message: 'foo',
      textDirection: TextDirection.ltr,
      location: BannerLocation.topEnd,
      layoutDirection: TextDirection.ltr,
    );

    final TestCanvas canvas = TestCanvas();

    bannerPainter.paint(canvas, const Size(1000.0, 1000.0));

    final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #translate;
    });

    expect(translateCommand, isNotNull);
    expect(translateCommand.positionalArguments[0], greaterThan(900.0));
    expect(translateCommand.positionalArguments[1], lessThan(100.0));

    final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #rotate;
    });

    expect(rotateCommand, isNotNull);
    expect(rotateCommand.positionalArguments[0], equals(math.pi / 4.0));
  });

  test('A Banner with a location of topEnd paints in the top left (RTL)', () {
    final BannerPainter bannerPainter = BannerPainter(
      message: 'foo',
      textDirection: TextDirection.rtl,
      location: BannerLocation.topEnd,
      layoutDirection: TextDirection.rtl,
    );

    final TestCanvas canvas = TestCanvas();

    bannerPainter.paint(canvas, const Size(1000.0, 1000.0));

    final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #translate;
    });

    expect(translateCommand, isNotNull);
    expect(translateCommand.positionalArguments[0], lessThan(100.0));
    expect(translateCommand.positionalArguments[1], lessThan(100.0));

    final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #rotate;
    });

    expect(rotateCommand, isNotNull);
    expect(rotateCommand.positionalArguments[0], equals(-math.pi / 4.0));
  });

  test('A Banner with a location of bottomStart paints in the bottom left (LTR)', () {
    final BannerPainter bannerPainter = BannerPainter(
      message: 'foo',
      textDirection: TextDirection.ltr,
      location: BannerLocation.bottomStart,
      layoutDirection: TextDirection.ltr,
    );

    final TestCanvas canvas = TestCanvas();

    bannerPainter.paint(canvas, const Size(1000.0, 1000.0));

    final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #translate;
    });

    expect(translateCommand, isNotNull);
    expect(translateCommand.positionalArguments[0], lessThan(100.0));
    expect(translateCommand.positionalArguments[1], greaterThan(900.0));

    final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #rotate;
    });

    expect(rotateCommand, isNotNull);
    expect(rotateCommand.positionalArguments[0], equals(math.pi / 4.0));
  });

  test('A Banner with a location of bottomStart paints in the bottom right (RTL)', () {
    final BannerPainter bannerPainter = BannerPainter(
      message: 'foo',
      textDirection: TextDirection.rtl,
      location: BannerLocation.bottomStart,
      layoutDirection: TextDirection.rtl,
    );

    final TestCanvas canvas = TestCanvas();

    bannerPainter.paint(canvas, const Size(1000.0, 1000.0));

    final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #translate;
    });

    expect(translateCommand, isNotNull);
    expect(translateCommand.positionalArguments[0], greaterThan(900.0));
    expect(translateCommand.positionalArguments[1], greaterThan(900.0));

    final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #rotate;
    });

    expect(rotateCommand, isNotNull);
    expect(rotateCommand.positionalArguments[0], equals(-math.pi / 4.0));
  });

  test('A Banner with a location of bottomEnd paints in the bottom right (LTR)', () {
    final BannerPainter bannerPainter = BannerPainter(
      message: 'foo',
      textDirection: TextDirection.rtl,
      location: BannerLocation.bottomEnd,
      layoutDirection: TextDirection.ltr,
    );

    final TestCanvas canvas = TestCanvas();

    bannerPainter.paint(canvas, const Size(1000.0, 1000.0));

    final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #translate;
    });

    expect(translateCommand, isNotNull);
    expect(translateCommand.positionalArguments[0], greaterThan(900.0));
    expect(translateCommand.positionalArguments[1], greaterThan(900.0));

    final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #rotate;
    });

    expect(rotateCommand, isNotNull);
    expect(rotateCommand.positionalArguments[0], equals(-math.pi / 4.0));
  });

  test('A Banner with a location of bottomEnd paints in the bottom left (RTL)', () {
    final BannerPainter bannerPainter = BannerPainter(
      message: 'foo',
      textDirection: TextDirection.ltr,
      location: BannerLocation.bottomEnd,
      layoutDirection: TextDirection.rtl,
    );

    final TestCanvas canvas = TestCanvas();

    bannerPainter.paint(canvas, const Size(1000.0, 1000.0));

    final Invocation translateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #translate;
    });

    expect(translateCommand, isNotNull);
    expect(translateCommand.positionalArguments[0], lessThan(100.0));
    expect(translateCommand.positionalArguments[1], greaterThan(900.0));

    final Invocation rotateCommand = canvas.invocations.firstWhere((Invocation invocation) {
      return invocation.memberName == #rotate;
    });

    expect(rotateCommand, isNotNull);
    expect(rotateCommand.positionalArguments[0], equals(math.pi / 4.0));
  });

  testWidgetsWithLeakTracking('Banner widget', (WidgetTester tester) async {
    debugDisableShadows = false;
    await tester.pumpWidget(
      const Directionality(
        textDirection: TextDirection.ltr,
        child: Banner(message: 'Hello', location: BannerLocation.topEnd),
      ),
    );
    expect(find.byType(CustomPaint), paints
      ..save()
      ..translate(x: 800.0, y: 0.0)
      ..rotate(angle: math.pi / 4.0)
      ..rect(rect: const Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0x7f000000), hasMaskFilter: true)
      ..rect(rect: const Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0xa0b71c1c), hasMaskFilter: false)
      ..paragraph(offset: const Offset(-40.0, 29.0))
      ..restore(),
    );
    debugDisableShadows = true;
  });

  testWidgetsWithLeakTracking('Banner widget in MaterialApp', (WidgetTester tester) async {
    debugDisableShadows = false;
    await tester.pumpWidget(const MaterialApp(home: Placeholder()));
    expect(find.byType(CheckedModeBanner), paints
      ..save()
      ..translate(x: 800.0, y: 0.0)
      ..rotate(angle: math.pi / 4.0)
      ..rect(rect: const Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0x7f000000), hasMaskFilter: true)
      ..rect(rect: const Rect.fromLTRB(-40.0, 28.0, 40.0, 40.0), color: const Color(0xa0b71c1c), hasMaskFilter: false)
      ..paragraph(offset: const Offset(-40.0, 29.0))
      ..restore(),
    );
    debugDisableShadows = true;
  });

  test('BannerPainter dispatches memory events', () async {
    await expectLater(
      await memoryEvents(
        () => BannerPainter(
          message: 'foo',
          textDirection: TextDirection.rtl,
          location: BannerLocation.topStart,
          layoutDirection: TextDirection.ltr,
        ).dispose(),
        BannerPainter,
      ),
      areCreateAndDispose,
    );
  });
}
