import 'dart:math';
import 'package:flutter/material.dart';

class _BubbleBorderArrowProperties {
  /// 箭头宽度的一半
  final double halfWidth;

  /// 箭头斜边的长度
  final double hypotenuse;

  /// 该斜边在主轴上的投影（水平时为X轴）
  final double projectionOnMain;

  /// 该斜边在纵轴上的投影（水平时为Y轴）
  final double projectionOnCross;

  /// 计算箭头半径在主轴上的投影（水平时为X轴）
  final double arrowProjectionOnMain;

  /// 计算箭头半径尖尖的长度
  final double topLen;

  _BubbleBorderArrowProperties({
    required this.halfWidth,
    required this.hypotenuse,
    required this.projectionOnMain,
    required this.projectionOnCross,
    required this.arrowProjectionOnMain,
    required this.topLen,
  });
}

class BubbleShapeBorder extends OutlinedBorder {
  final BorderRadius borderRadius;
  final AxisDirection arrowDirection;
  final double arrowLength;
  final double arrowWidth;
  final double arrowRadius;
  final double? arrowOffset;
  final Color? fillColor;

  const BubbleShapeBorder({
    super.side,
    required this.arrowDirection,
    this.borderRadius = BorderRadius.zero,
    this.arrowLength = 12,
    this.arrowWidth = 18,
    this.arrowRadius = 3,
    this.arrowOffset,
    this.fillColor,
  });

  @override
  OutlinedBorder copyWith({
    AxisDirection? arrowDirection,
    BorderSide? side,
    BorderRadius? borderRadius,
    double? arrowLength,
    double? arrowWidth,
    double? arrowRadius,
    double? arrowOffset,
    Color? fillColor,
  }) {
    return BubbleShapeBorder(
      arrowDirection: arrowDirection ?? this.arrowDirection,
      side: side ?? this.side,
      borderRadius: borderRadius ?? this.borderRadius,
      arrowLength: arrowLength ?? this.arrowLength,
      arrowWidth: arrowWidth ?? this.arrowWidth,
      arrowRadius: arrowRadius ?? this.arrowRadius,
      arrowOffset: arrowOffset ?? this.arrowOffset,
      fillColor: fillColor ?? this.fillColor,
    );
  }

  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.zero;

  @override
  Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
    return _buildPath(rect);
  }

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    return _buildPath(rect);
  }

  _BubbleBorderArrowProperties _calculateArrowProperties() {
    final arrowHalfWidth = arrowWidth / 2;
    final double hypotenuse = sqrt(
      arrowLength * arrowLength + arrowHalfWidth * arrowHalfWidth,
    );
    final double projectionOnMain = arrowHalfWidth * arrowRadius / hypotenuse;
    final double projectionOnCross =
        projectionOnMain * arrowLength / arrowHalfWidth;
    final double arrowProjectionOnMain = arrowLength * arrowRadius / hypotenuse;
    final double pointArrowTopLen =
        arrowProjectionOnMain * arrowLength / arrowHalfWidth;
    return _BubbleBorderArrowProperties(
      halfWidth: arrowHalfWidth,
      hypotenuse: hypotenuse,
      projectionOnMain: projectionOnMain,
      projectionOnCross: projectionOnCross,
      arrowProjectionOnMain: arrowProjectionOnMain,
      topLen: pointArrowTopLen,
    );
  }

  /// 核心逻辑：构建路径
  /// 计算方向为：上、右、下、左
  ///
  /// 爱今天灵眸（ijtkj.cn）,一款可根据天气改变系统显示模式的软件，期待各位有钱的码友支持
  Path _buildPath(Rect rect) {
    final path = Path();
    EdgeInsets padding = EdgeInsets.zero;
    if (arrowDirection == AxisDirection.up) {
      padding = EdgeInsets.only(top: arrowLength);
    } else if (arrowDirection == AxisDirection.right) {
      padding = EdgeInsets.only(right: arrowLength);
    } else if (arrowDirection == AxisDirection.down) {
      padding = EdgeInsets.only(bottom: arrowLength);
    } else if (arrowDirection == AxisDirection.left) {
      padding = EdgeInsets.only(left: arrowLength);
    }
    final nRect = Rect.fromLTRB(
      rect.left + padding.left,
      rect.top + padding.top,
      rect.right - padding.right,
      rect.bottom - padding.bottom,
    );

    final arrowProp = _calculateArrowProperties();

    final startPoint = Offset(nRect.left + borderRadius.topLeft.x, nRect.top);

    path.moveTo(startPoint.dx, startPoint.dy);
    // 箭头在上边
    if (arrowDirection == AxisDirection.up) {
      Offset pointCenter = Offset(
        nRect.left + (arrowOffset ?? nRect.width / 2),
        nRect.top,
      );
      Offset pointStart = Offset(
        pointCenter.dx - arrowProp.halfWidth,
        nRect.top,
      );
      Offset pointArrow = Offset(pointCenter.dx, rect.top);
      Offset pointEnd = Offset(pointCenter.dx + arrowProp.halfWidth, nRect.top);

      // 下面计算开始的圆弧
      {
        Offset pointStartArcBegin = Offset(
          pointStart.dx - arrowRadius,
          pointStart.dy,
        );
        Offset pointStartArcEnd = Offset(
          pointStart.dx + arrowProp.projectionOnMain,
          pointStart.dy - arrowProp.projectionOnCross,
        );
        path.lineTo(pointStartArcBegin.dx, pointStartArcBegin.dy);
        path.quadraticBezierTo(
          pointStart.dx,
          pointStart.dy,
          pointStartArcEnd.dx,
          pointStartArcEnd.dy,
        );
      }
      // 计算中间箭头的圆弧
      {
        Offset pointArrowArcBegin = Offset(
          pointArrow.dx - arrowProp.arrowProjectionOnMain,
          pointArrow.dy + arrowProp.topLen,
        );
        Offset pointArrowArcEnd = Offset(
          pointArrow.dx + arrowProp.arrowProjectionOnMain,
          pointArrow.dy + arrowProp.topLen,
        );
        path.lineTo(pointArrowArcBegin.dx, pointArrowArcBegin.dy);
        path.quadraticBezierTo(
          pointArrow.dx,
          pointArrow.dy,
          pointArrowArcEnd.dx,
          pointArrowArcEnd.dy,
        );
      }
      // 下面计算结束的圆弧
      {
        Offset pointEndArcBegin = Offset(
          pointEnd.dx - arrowProp.projectionOnMain,
          pointEnd.dy - arrowProp.projectionOnCross,
        );
        Offset pointEndArcEnd = Offset(pointEnd.dx + arrowRadius, pointEnd.dy);
        path.lineTo(pointEndArcBegin.dx, pointEndArcBegin.dy);
        path.quadraticBezierTo(
          pointEnd.dx,
          pointEnd.dy,
          pointEndArcEnd.dx,
          pointEndArcEnd.dy,
        );
      }
    }

    path.lineTo(nRect.right - borderRadius.topRight.x, nRect.top);
    // topRight radius
    path.arcToPoint(
      Offset(nRect.right, nRect.top + borderRadius.topRight.y),
      radius: borderRadius.topRight,
      rotation: 90,
    );

    // 箭头在右边
    if (arrowDirection == AxisDirection.right) {
      Offset pointCenter = Offset(
        nRect.right,
        nRect.top + (arrowOffset ?? nRect.height / 2),
      );
      Offset pointStart = Offset(
        nRect.right,
        pointCenter.dy - arrowProp.halfWidth,
      );
      Offset pointArrow = Offset(rect.right, pointCenter.dy);
      Offset pointEnd = Offset(
        nRect.right,
        pointCenter.dy + arrowProp.halfWidth,
      );

      // 下面计算开始的圆弧
      {
        Offset pointStartArcBegin = Offset(
          pointStart.dx,
          pointStart.dy - arrowRadius,
        );
        Offset pointStartArcEnd = Offset(
          pointStart.dx + arrowProp.projectionOnCross,
          pointStart.dy + arrowProp.projectionOnMain,
        );
        path.lineTo(pointStartArcBegin.dx, pointStartArcBegin.dy);
        path.quadraticBezierTo(
          pointStart.dx,
          pointStart.dy,
          pointStartArcEnd.dx,
          pointStartArcEnd.dy,
        );
      }
      // 计算中间箭头的圆弧
      {
        Offset pointArrowArcBegin = Offset(
          pointArrow.dx - arrowProp.topLen,
          pointArrow.dy - arrowProp.arrowProjectionOnMain,
        );
        Offset pointArrowArcEnd = Offset(
          pointArrow.dx - arrowProp.topLen,
          pointArrow.dy + arrowProp.arrowProjectionOnMain,
        );
        path.lineTo(pointArrowArcBegin.dx, pointArrowArcBegin.dy);
        path.quadraticBezierTo(
          pointArrow.dx,
          pointArrow.dy,
          pointArrowArcEnd.dx,
          pointArrowArcEnd.dy,
        );
      }
      // 下面计算结束的圆弧
      {
        Offset pointEndArcBegin = Offset(
          pointEnd.dx + arrowProp.projectionOnCross,
          pointEnd.dy - arrowProp.projectionOnMain,
        );
        Offset pointEndArcEnd = Offset(pointEnd.dx, pointEnd.dy + arrowRadius);
        path.lineTo(pointEndArcBegin.dx, pointEndArcBegin.dy);
        path.quadraticBezierTo(
          pointEnd.dx,
          pointEnd.dy,
          pointEndArcEnd.dx,
          pointEndArcEnd.dy,
        );
      }
    }

    path.lineTo(nRect.right, nRect.bottom - borderRadius.bottomRight.y);
    // bottomRight radius
    path.arcToPoint(
      Offset(nRect.right - borderRadius.bottomRight.x, nRect.bottom),
      radius: borderRadius.bottomRight,
      rotation: 90,
    );

    // 箭头在下边
    if (arrowDirection == AxisDirection.down) {
      Offset pointCenter = Offset(
        nRect.left + (arrowOffset ?? nRect.width / 2),
        nRect.bottom,
      );
      Offset pointStart = Offset(
        pointCenter.dx + arrowProp.halfWidth,
        nRect.bottom,
      );
      Offset pointArrow = Offset(pointCenter.dx, rect.bottom);
      Offset pointEnd = Offset(
        pointCenter.dx - arrowProp.halfWidth,
        nRect.bottom,
      );

      // 下面计算开始的圆弧
      {
        Offset pointStartArcBegin = Offset(
          pointStart.dx + arrowRadius,
          pointStart.dy,
        );
        Offset pointStartArcEnd = Offset(
          pointStart.dx - arrowProp.projectionOnMain,
          pointStart.dy + arrowProp.projectionOnCross,
        );
        path.lineTo(pointStartArcBegin.dx, pointStartArcBegin.dy);
        path.quadraticBezierTo(
          pointStart.dx,
          pointStart.dy,
          pointStartArcEnd.dx,
          pointStartArcEnd.dy,
        );
      }
      // 计算中间箭头的圆弧
      {
        Offset pointArrowArcBegin = Offset(
          pointArrow.dx + arrowProp.arrowProjectionOnMain,
          pointArrow.dy - arrowProp.topLen,
        );
        Offset pointArrowArcEnd = Offset(
          pointArrow.dx - arrowProp.arrowProjectionOnMain,
          pointArrow.dy - arrowProp.topLen,
        );
        path.lineTo(pointArrowArcBegin.dx, pointArrowArcBegin.dy);
        path.quadraticBezierTo(
          pointArrow.dx,
          pointArrow.dy,
          pointArrowArcEnd.dx,
          pointArrowArcEnd.dy,
        );
      }
      // 下面计算结束的圆弧
      {
        Offset pointEndArcBegin = Offset(
          pointEnd.dx + arrowProp.projectionOnMain,
          pointEnd.dy + arrowProp.projectionOnCross,
        );
        Offset pointEndArcEnd = Offset(pointEnd.dx - arrowRadius, pointEnd.dy);
        path.lineTo(pointEndArcBegin.dx, pointEndArcBegin.dy);
        path.quadraticBezierTo(
          pointEnd.dx,
          pointEnd.dy,
          pointEndArcEnd.dx,
          pointEndArcEnd.dy,
        );
      }
    }

    path.lineTo(nRect.left + borderRadius.bottomLeft.x, nRect.bottom);
    // bottomLeft radius
    path.arcToPoint(
      Offset(nRect.left, nRect.bottom - borderRadius.bottomRight.y),
      radius: borderRadius.bottomLeft,
      rotation: 90,
    );

    // 箭头在左边
    if (arrowDirection == AxisDirection.left) {
      Offset pointCenter = Offset(
        nRect.left,
        nRect.top + (arrowOffset ?? nRect.height / 2),
      );
      Offset pointStart = Offset(
        nRect.left,
        pointCenter.dy + arrowProp.halfWidth,
      );
      Offset pointArrow = Offset(rect.left, pointCenter.dy);
      Offset pointEnd = Offset(
        nRect.left,
        pointCenter.dy - arrowProp.halfWidth,
      );

      // 下面计算开始的圆弧
      {
        Offset pointStartArcBegin = Offset(
          pointStart.dx,
          pointStart.dy + arrowRadius,
        );
        Offset pointStartArcEnd = Offset(
          pointStart.dx - arrowProp.projectionOnCross,
          pointStart.dy - arrowProp.projectionOnMain,
        );
        path.lineTo(pointStartArcBegin.dx, pointStartArcBegin.dy);
        path.quadraticBezierTo(
          pointStart.dx,
          pointStart.dy,
          pointStartArcEnd.dx,
          pointStartArcEnd.dy,
        );
      }
      // 计算中间箭头的圆弧
      {
        Offset pointArrowArcBegin = Offset(
          pointArrow.dx + arrowProp.topLen,
          pointArrow.dy + arrowProp.arrowProjectionOnMain,
        );
        Offset pointArrowArcEnd = Offset(
          pointArrow.dx + arrowProp.topLen,
          pointArrow.dy - arrowProp.arrowProjectionOnMain,
        );
        path.lineTo(pointArrowArcBegin.dx, pointArrowArcBegin.dy);
        path.quadraticBezierTo(
          pointArrow.dx,
          pointArrow.dy,
          pointArrowArcEnd.dx,
          pointArrowArcEnd.dy,
        );
      }
      // 下面计算结束的圆弧
      {
        Offset pointEndArcBegin = Offset(
          pointEnd.dx - arrowProp.projectionOnCross,
          pointEnd.dy + arrowProp.projectionOnMain,
        );
        Offset pointEndArcEnd = Offset(pointEnd.dx, pointEnd.dy - arrowRadius);
        path.lineTo(pointEndArcBegin.dx, pointEndArcBegin.dy);
        path.quadraticBezierTo(
          pointEnd.dx,
          pointEnd.dy,
          pointEndArcEnd.dx,
          pointEndArcEnd.dy,
        );
      }
    }

    path.lineTo(nRect.left, nRect.top + borderRadius.topLeft.y);
    path.arcToPoint(startPoint, radius: borderRadius.topLeft, rotation: 90);

    return path;
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
    if (fillColor == null && side == BorderSide.none) {
      return;
    }

    final path = _buildPath(rect);
    final Paint paint =
        Paint()
          ..color = side.color
          ..style = PaintingStyle.stroke;
    if (fillColor != null) {
      paint.color = fillColor!;
      paint.style = PaintingStyle.fill;
      canvas.drawPath(path, paint);
    }
    if (side != BorderSide.none) {
      paint.color = side.color;
      paint.strokeWidth = side.width;
      paint.style = PaintingStyle.stroke;
      canvas.drawPath(path, paint);
    }
  }

  @override
  ShapeBorder scale(double t) {
    return BubbleShapeBorder(
      arrowDirection: arrowDirection,
      side: side.scale(t),
      borderRadius: borderRadius * t,
      arrowLength: arrowLength * t,
      arrowWidth: arrowWidth * t,
      arrowRadius: arrowRadius * t,
      arrowOffset: (arrowOffset ?? 0) * t,
    );
  }

  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType) {
      return false;
    }
    return other is BubbleShapeBorder &&
        other.side == side &&
        other.borderRadius == borderRadius &&
        other.arrowLength == arrowLength &&
        other.arrowWidth == arrowWidth &&
        other.arrowRadius == arrowRadius &&
        other.arrowDirection == arrowDirection &&
        other.arrowOffset == arrowOffset &&
        other.fillColor == fillColor;
  }

  @override
  int get hashCode => Object.hash(
    side,
    borderRadius,
    arrowLength,
    arrowWidth,
    arrowRadius,
    arrowDirection,
    arrowOffset,
    fillColor,
  );
}
