import 'dart:math';

import 'package:flutter/material.dart';
import './custom_gesture_detector.dart' as gd;

class ScaleChangedModel {
  double scale;
  Offset offset;

  ScaleChangedModel({this.scale, this.offset});

  @override
  String toString() {
    return 'ScaleChangedModel(scale: $scale, offset:$offset)';
  }
}

class TouchableContainer extends StatefulWidget {
  final Widget child;
  final bool doubleTapStillScale;

  ///用来约束图和坐标轴的
  ///因为坐标轴和图是堆叠起来的，图在坐标轴的内部，需要制定margin，否则放大后图会超出坐标轴
  final EdgeInsets margin;
  ValueChanged<ScaleChangedModel> scaleChangedCallback; // 图片缩放倍数改变回调函数
  Function() pointerDownCallback; // 图片指针按下回调函数
  Function() pointerUpCallback; // 图片指针弹起回调函数

  TouchableContainer({
    Key key,
    this.child,
    this.margin = const EdgeInsets.all(0),
    this.pointerDownCallback,
    this.pointerUpCallback,
    this.scaleChangedCallback,
    this.doubleTapStillScale,
  });

  _TouchableContainerState createState() => _TouchableContainerState();
}

class _TouchableContainerState extends State<TouchableContainer>
    with SingleTickerProviderStateMixin {
  double _kMinFlingVelocity = 800.0;
  AnimationController _controller;
  Animation<Offset> _flingAnimation;
  Offset _offset = Offset.zero;
  double _scale = 1.0;
  Offset _normalizedOffset;
  double _previousScale;
  Offset doubleDownPositon;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this)..addListener(_handleFlingAnimation);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  // The maximum offset value is 0,0. If the size of this renderer's box is w,h
  // then the minimum offset value is w - _scale * w, h - _scale * h.
  //也就是最小值是原点0，0，点从最大值到0的区间，也就是这个图可以从最大值移动到原点
  Offset _clampOffset(Offset offset) {
    final Size size = context.size; //容器的大小
    // print("context.size = ${context.size}"); // context.size = Size(298.0, 298.0)
    final Offset minOffset = Offset(size.width, size.height) * (1.0 - _scale);
    return Offset(offset.dx.clamp(minOffset.dx, 0.0), offset.dy.clamp(minOffset.dy, 0.0));
    // return Offset(minOffset.dx / 2, minOffset.dy / 2);
  }

  // 实现双击图片放大后，点击位置位于容器中央
  Offset _clampOffset2(Offset position) {
    final Size size = context.size; //容器的大小
    // print("context.size = ${context.size}"); // context.size = Size(298.0, 298.0)
    RenderBox renderBox = context.findRenderObject();
    Offset offsetTopLeft = renderBox.localToGlobal(Offset.zero);
    print("offsetTopLeft = $offsetTopLeft"); // offsetTopLeft = Offset(31.0, 111.0)

    double x = position.dx - offsetTopLeft.dx;
    x = max(x * _scale - size.width * _scale / 4, 0);
    x = min(x, size.width * _scale / 2);

    double y = position.dy - offsetTopLeft.dy;
    y = max(y * _scale - size.height * _scale / 4, 0);
    y = min(y, size.height * _scale / 2);
    print("Offset(x, y) = ${Offset(-x, -y)}");

    return Offset(-x, -y);
  }

  void _handleFlingAnimation() {
    setState(() {
      _offset = _flingAnimation.value;
    });
  }

  void _handleOnScaleStart(gd.ScaleStartDetails details) {
    setState(() {
      _previousScale = _scale;
      _normalizedOffset = (details.focalPoint - _offset) / _scale;
      // The fling animation stops if an input gesture starts.
      _controller.stop();
    });
  }

  void _handleOnScaleUpdate(gd.ScaleUpdateDetails details) {
    setState(() {
      if (details.pointCount > 1) {
        _scale = (_previousScale * details.scale).clamp(1.0, double.infinity);
      }
      // Ensure that image location under the focal point stays in the same place despite scaling.
      _offset = _clampOffset(details.focalPoint - _normalizedOffset * _scale);
    });
    ScaleChangedModel model = ScaleChangedModel(scale: _scale, offset: _offset);
    if (widget.scaleChangedCallback != null) widget.scaleChangedCallback(model);
  }

  void _handleOnScaleEnd(gd.ScaleEndDetails details) {
    final double magnitude = details.velocity.pixelsPerSecond.distance;
    if (magnitude < _kMinFlingVelocity) return;
    final Offset direction = details.velocity.pixelsPerSecond / magnitude;
    final double distance = (Offset.zero & context.size).shortestSide;
    _flingAnimation =
        Tween<Offset>(begin: _offset, end: _clampOffset(_offset + direction * distance))
            .animate(_controller);
    _controller
      ..value = 0.0
      ..fling(velocity: magnitude / 1000.0);
  }

  void _onDoubleTap(gd.DoubleDetails details) {
    _normalizedOffset = (details.pointerEvent.position - _offset) / _scale;
    print("_scale = ${_scale}, _offset = ${_offset}");
    print("position = ${details.pointerEvent.position}, _normalizedOffset = ${_normalizedOffset}");
    // _scale = 1.0, _offset = Offset(0.0, 0.0)
    // position = Offset(178.0, 260.7), _normalizedOffset = Offset(178.0, 260.7)
    if (!widget.doubleTapStillScale && _scale != 1.0) {
      setState(() {
        _scale = 1.0;
        _offset = Offset.zero;
      });
      ScaleChangedModel model = ScaleChangedModel(scale: _scale, offset: _offset);
      if (widget.scaleChangedCallback != null) widget.scaleChangedCallback(model);
      return;
    }
    setState(() {
      if (widget.doubleTapStillScale) {
        _scale *= (1 + 0.5);
        // Ensure that image location under the focal point stays in the same place despite scaling.
        // _offset = doubleDownPositon;
        _offset = _clampOffset(details.pointerEvent.position - _normalizedOffset * _scale);
      } else {
        _scale *= (2);
        _offset = _clampOffset2(details.pointerEvent.position);
        // _offset = Offset.zero; // 对齐左上角
        print("_scale = ${_scale}, _offset = ${_offset}");
        // _scale = 2.0, _offset = Offset(-175.3, -270.0)
      }
    });

    ScaleChangedModel model = ScaleChangedModel(scale: _scale, offset: _offset);
    if (widget.scaleChangedCallback != null) widget.scaleChangedCallback(model);
  }

  @override
  Widget build(BuildContext context) {
    return gd.GestureDetector(
      // onPanDown: _onPanDown,
      onDoubleTap: _onDoubleTap,
      onScaleStart: _handleOnScaleStart,
      onScaleUpdate: _handleOnScaleUpdate,
      pointerDownCallback: widget.pointerDownCallback,
      pointerUpCallback: widget.pointerUpCallback,
      // onScaleEnd: _handleOnScaleEnd,
      child: Container(
        margin: widget.margin,
        constraints: const BoxConstraints(
          minWidth: double.maxFinite,
          minHeight: double.infinity,
        ),
        child: Transform(
            transform: Matrix4.identity()
              ..translate(_offset.dx, _offset.dy)
              ..scale(_scale, _scale, 1.0),
            child: widget.child),
      ),
    );
  }
}
