import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flyinsono/lab/color/lab_colors.dart';

class SelectionRect extends StatefulWidget {
  final Widget child;
  final ScrollController scrollController;
  final Function(Set<int>) onSelectionUpdating;
  final Function(Set<int>) onSelectionEnd;
  final DataLayoutType dataLayoutType;
  final double itemHeight;
  final int? columnCount;
  final double? xSpacing;
  final double? ySpacing;
  final double? rightPadding;

  SelectionRect({
    required this.child,
    required this.scrollController,
    required this.onSelectionUpdating,
    required this.onSelectionEnd,
    required this.dataLayoutType,
    required this.itemHeight,
    this.columnCount,
    this.xSpacing,
    this.ySpacing,
    this.rightPadding,
  })  : assert(dataLayoutType == DataLayoutType.Grid
            ? (columnCount != null &&
                xSpacing != null &&
                ySpacing != null &&
                rightPadding != null)
            : true),
        assert(itemHeight > 0);

  @override
  _SelectionRectState createState() => _SelectionRectState();
}

class _SelectionRectState extends State<SelectionRect> {
  Offset? startDragOffset;
  Offset? currentDragOffset;
  Rect selectionRect = Rect.zero;
  double scrollOffsetOnStart = 0;
  double itemWidth = 0;
  GlobalKey dataViewKey = GlobalKey();

  @override
  void initState() {
    super.initState();
    widget.scrollController.addListener(onDataViewScroll);
  }

  @override
  void dispose() {
    widget.scrollController.removeListener(onDataViewScroll);
    super.dispose();
  }

  void onDataViewScroll() {
    // 滚动时的逻辑
    setState(() {
      // 更新选择框的位置，考虑滚动偏移
      if (startDragOffset != null && currentDragOffset != null) {
        _updateSelectionRect(startDragOffset!, currentDragOffset!);
        _onSelectionUpdating();
      }
    });
  }

  void _updateSelectionRect(Offset start, Offset end) {
    double top =
        start.dy - (widget.scrollController.offset - scrollOffsetOnStart);
    double left = start.dx;
    double bottom = end.dy;
    double right = end.dx;

    setState(() {
      selectionRect = Rect.fromLTRB(
        left,
        top,
        right,
        bottom,
      );
    });
  }

  // 计算listView中的选中条目
  Set<int> _countListViewSelection() {
    final startY = selectionRect.top + widget.scrollController.offset;
    final endY = selectionRect.bottom + widget.scrollController.offset;
    final selectedIndexes = <int>{};
    int firstIndex = (startY / widget.itemHeight).floor();
    int lastIndex = (endY / widget.itemHeight).floor();
    if (firstIndex > lastIndex) {
      int temp = firstIndex;
      firstIndex = lastIndex;
      lastIndex = temp;
    }
    for (int i = firstIndex; i <= lastIndex; i++) {
      selectedIndexes.add(i);
    }
    return selectedIndexes;
  }

  // 计算gridView中的选中条目
  Set<int> _countGridViewSelection() {
    final startX = selectionRect.left;
    final startY = selectionRect.top + widget.scrollController.offset;
    final endX = selectionRect.right;
    final endY = selectionRect.bottom + widget.scrollController.offset;
    final selectedIndexes = <int>{};
    final Point leftTopPoint = Point(min(startX, endX), min(startY, endY));
    final Point rightBottomPoint = Point(max(startX, endX), max(startY, endY));
    Point<int> leftTopIndex =
        _getFixedPoint(leftTopPoint, widget.xSpacing!, widget.ySpacing!);
    Point<int> rightBottomIndex =
        _getFixedPoint(rightBottomPoint, widget.xSpacing!, widget.ySpacing!);
    if (leftTopIndex.x < 0) {
      leftTopIndex = Point(0, leftTopIndex.y);
    }
    if (rightBottomIndex.x >= widget.columnCount! * 2) {
      rightBottomIndex =
          Point((widget.columnCount! - 1) * 2, rightBottomIndex.y);
    }
    for (int i = leftTopIndex.x; i <= rightBottomIndex.x; i += 1) {
      for (int j = leftTopIndex.y; j <= rightBottomIndex.y; j += 1) {
        if (i % 2 == 0 && j % 2 == 0) {
          selectedIndexes.add(((j * widget.columnCount! + i) / 2).floor());
        }
      }
    }
    return selectedIndexes;
  }

  Point<int> _getFixedPoint(Point point, double xSpacing, double ySpacing) {
    final px = (point.x / (itemWidth + xSpacing)).floor();
    int pxFixed = px * 2;
    if (point.x < px * (itemWidth + xSpacing)) {
      pxFixed -= 1;
    }
    if (point.x > (px + 1) * (itemWidth + xSpacing) - xSpacing) {
      pxFixed += 1;
    }
    final py = (point.y / (widget.itemHeight + ySpacing)).floor();
    int pyFixed = py * 2;
    if (point.y < py * (widget.itemHeight + ySpacing)) {
      pyFixed -= 1;
    }
    if (point.y > (py + 1) * (widget.itemHeight + ySpacing) - ySpacing) {
      pyFixed += 1;
    }

    return Point(pxFixed, pyFixed);
  }

  void _onSelectionEnd() {
    if (widget.dataLayoutType == DataLayoutType.List) {
      final selectedIndexes = _countListViewSelection();
      widget.onSelectionEnd(selectedIndexes);
    } else if (widget.dataLayoutType == DataLayoutType.Grid) {
      final selectedIndexes = _countGridViewSelection();
      widget.onSelectionEnd(selectedIndexes);
    }
  }

  void _onSelectionUpdating() {
    if (widget.dataLayoutType == DataLayoutType.List) {
      final selectedIndexes = _countListViewSelection();
      widget.onSelectionUpdating(selectedIndexes);
    } else if (widget.dataLayoutType == DataLayoutType.Grid) {
      final selectedIndexes = _countGridViewSelection();
      widget.onSelectionUpdating(selectedIndexes);
    }
  }

  void _resetState() {
    startDragOffset = null;
    currentDragOffset = null;
    setState(() {
      selectionRect = Rect.zero;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ClipRect(
      key: dataViewKey,
      child: GestureDetector(
        onPanStart: (details) {
          if (widget.dataLayoutType == DataLayoutType.Grid &&
              widget.columnCount != null) {
            final dataViewWidth = dataViewKey.currentContext!.size!.width -
                ((widget.columnCount! - 1) * widget.xSpacing!) -
                widget.rightPadding!;
            itemWidth = dataViewWidth / widget.columnCount!;
          }
          scrollOffsetOnStart = widget.scrollController.offset;
          startDragOffset = details.localPosition;
          currentDragOffset = details.localPosition;
          _updateSelectionRect(startDragOffset!, currentDragOffset!);
        },
        onPanUpdate: (details) {
          currentDragOffset = details.localPosition;
          _updateSelectionRect(startDragOffset!, currentDragOffset!);
          _onSelectionUpdating();
        },
        onPanEnd: (details) {
          _onSelectionEnd();
          _resetState();
        },
        child: CustomPaint(
          foregroundPainter: SelectionRectPainter(selectionRect: selectionRect),
          child: Container(
            child: widget.child,
          ),
        ),
      ),
    );
  }
}

class SelectionRectPainter extends CustomPainter {
  final Rect selectionRect;

  SelectionRectPainter({required this.selectionRect});

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = LabColors.base500.withOpacity(0.3)
      ..style = PaintingStyle.fill
      ..isAntiAlias = false;

    canvas.drawRect(selectionRect, paint);

    final borderPaint = Paint()
      ..color = LabColors.base500
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1
      ..isAntiAlias = false;

    canvas.drawRect(selectionRect, borderPaint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

enum DataLayoutType {
  List,
  Grid,
}
