// 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 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import '../foundation/leak_tracking.dart';

void main() {
  testWidgetsWithLeakTracking('gets local coordinates', (WidgetTester tester) async {
    int tapCount = 0;
    int tapCancelCount = 0;
    final List<TapDownDetails> downDetails = <TapDownDetails>[];
    final List<TapUpDetails> upDetails = <TapUpDetails>[];

    final Key redContainer = UniqueKey();
    await tester.pumpWidget(
      Center(
        child: GestureDetector(
          onTap: () {
            tapCount++;
          },
          onTapCancel: () {
            tapCancelCount++;
          },
          onTapDown: (TapDownDetails details) {
            downDetails.add(details);
          },
          onTapUp: (TapUpDetails details) {
            upDetails.add(details);
          },
          child: Container(
            key: redContainer,
            width: 100,
            height: 150,
            color: Colors.red,
          ),
        ),
      ),
    );

    await tester.tapAt(tester.getCenter(find.byKey(redContainer)));
    expect(tapCount, 1);
    expect(tapCancelCount, 0);
    expect(downDetails.single.localPosition, const Offset(50, 75));
    expect(downDetails.single.globalPosition, const Offset(400, 300));
    expect(upDetails.single.localPosition, const Offset(50, 75));
    expect(upDetails.single.globalPosition, const Offset(400, 300));
  });

  testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled up', (WidgetTester tester) async {
    int tapCount = 0;
    int tapCancelCount = 0;
    final List<TapDownDetails> downDetails = <TapDownDetails>[];
    final List<TapUpDetails> upDetails = <TapUpDetails>[];

    final Key redContainer = UniqueKey();
    await tester.pumpWidget(
        Center(
          child: Transform.scale(
            scale: 2.0,
            child: GestureDetector(
                onTap: () {
                  tapCount++;
                },
                onTapCancel: () {
                  tapCancelCount++;
                },
                onTapDown: (TapDownDetails details) {
                  downDetails.add(details);
                },
                onTapUp: (TapUpDetails details) {
                  upDetails.add(details);
                },
                child: Container(
                  key: redContainer,
                  width: 100,
                  height: 150,
                  color: Colors.red,
                ),
            ),
          ),
        ),
    );

    // Move just below kTouchSlop should recognize tap.
    TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(redContainer)));
    await gesture.moveBy(const Offset(0, kTouchSlop - 1));
    await gesture.up();

    expect(tapCount, 1);
    expect(tapCancelCount, 0);
    expect(downDetails.single.localPosition, const Offset(50, 75));
    expect(downDetails.single.globalPosition, const Offset(400, 300));
    expect(upDetails.single.localPosition, const Offset(50, 75 + (kTouchSlop - 1) / 2.0));
    expect(upDetails.single.globalPosition, const Offset(400, 300 + (kTouchSlop - 1)));

    downDetails.clear();
    upDetails.clear();
    tapCount = 0;
    tapCancelCount = 0;

    // Move more then kTouchSlop should cancel.
    gesture = await tester.startGesture(tester.getCenter(find.byKey(redContainer)));
    await gesture.moveBy(const Offset(0, kTouchSlop + 1));
    await gesture.up();
    expect(tapCount, 0);
    expect(tapCancelCount, 1);
    expect(downDetails.single.localPosition, const Offset(50, 75));
    expect(downDetails.single.globalPosition, const Offset(400, 300));
    expect(upDetails, isEmpty);
  });

  testWidgetsWithLeakTracking('kTouchSlop is evaluated in the global coordinate space when scaled down', (WidgetTester tester) async {
    int tapCount = 0;
    int tapCancelCount = 0;
    final List<TapDownDetails> downDetails = <TapDownDetails>[];
    final List<TapUpDetails> upDetails = <TapUpDetails>[];

    final Key redContainer = UniqueKey();
    await tester.pumpWidget(
        Center(
          child: Transform.scale(
            scale: 0.5,
            child: GestureDetector(
                onTap: () {
                  tapCount++;
                },
                onTapCancel: () {
                  tapCancelCount++;
                },
                onTapDown: (TapDownDetails details) {
                  downDetails.add(details);
                },
                onTapUp: (TapUpDetails details) {
                  upDetails.add(details);
                },
                child: Container(
                  key: redContainer,
                  width: 100,
                  height: 150,
                  color: Colors.red,
                ),
            ),
          ),
        ),
    );

    // Move just below kTouchSlop should recognize tap.
    TestGesture gesture = await tester.startGesture(tester.getCenter(find.byKey(redContainer)));
    await gesture.moveBy(const Offset(0, kTouchSlop - 1));
    await gesture.up();

    expect(tapCount, 1);
    expect(tapCancelCount, 0);
    expect(downDetails.single.localPosition, const Offset(50, 75));
    expect(downDetails.single.globalPosition, const Offset(400, 300));
    expect(upDetails.single.localPosition, const Offset(50, 75 + (kTouchSlop - 1) * 2.0));
    expect(upDetails.single.globalPosition, const Offset(400, 300 + (kTouchSlop - 1)));

    downDetails.clear();
    upDetails.clear();
    tapCount = 0;
    tapCancelCount = 0;

    // Move more then kTouchSlop should cancel.
    gesture = await tester.startGesture(tester.getCenter(find.byKey(redContainer)));
    await gesture.moveBy(const Offset(0, kTouchSlop + 1));
    await gesture.up();
    expect(tapCount, 0);
    expect(tapCancelCount, 1);
    expect(downDetails.single.localPosition, const Offset(50, 75));
    expect(downDetails.single.globalPosition, const Offset(400, 300));
    expect(upDetails, isEmpty);
  });
}
