import { areLinesSameDirection, isHorizontalFromPosition } from "../edge";
import {
  ControlPoint,
  HandlePosition,
  NodeRect,
  getCenterPoints,
  getExpandedRect,
  getOffsetPoint,
  getSidesFromPoints,
  getVerticesFromRectVertex,
  optimizeInputPoints,
  reducePoints,
} from "../point";
import { getAStarPath } from "./a-star";
import { getSimplePath } from "./simple";

/**
 * 边缘控制点计算模块
 * 用于计算图形边缘连接线的控制点,以实现平滑的连接效果
 * 主要应用于流程图、思维导图等需要节点间连线的场景
 */

/**
 * 控制点计算所需的输入参数接口
 */
export interface GetControlPointsParams {
  source: HandlePosition;      // 起始连接点位置
  target: HandlePosition;      // 目标连接点位置 
  sourceRect: NodeRect;        // 起始节点的矩形区域
  targetRect: NodeRect;        // 目标节点的矩形区域
  /**
   * 边缘与节点之间的最小间距
   * @default 20
   */
  offset: number;
}

/**
 * 计算两个节点之间连接线的控制点
 * @param params 控制点计算参数
 * @returns 返回优化后的路径点和输入点集合
 */
export const getControlPoints = ({
  source: oldSource,
  target: oldTarget,
  sourceRect,
  targetRect,
  offset = 20,
}: GetControlPointsParams) => {
  const source: ControlPoint = oldSource;
  const target: ControlPoint = oldTarget;
  let edgePoints: ControlPoint[] = [];
  let optimized: ReturnType<typeof optimizeInputPoints>;

  // 1. 计算考虑偏移量后的起始和结束点
  const sourceOffset = getOffsetPoint(oldSource, offset);
  const targetOffset = getOffsetPoint(oldTarget, offset);
  const expandedSource = getExpandedRect(sourceRect, offset);
  const expandedTarget = getExpandedRect(targetRect, offset);

  // 2. 判断两个矩形是否靠得较近或应该直接连接
  const minOffset = 2 * offset + 10;  // 最小间距阈值
  const isHorizontalLayout = isHorizontalFromPosition(oldSource.position);  // 是否为水平布局
  const isSameDirection = areLinesSameDirection(
    source,
    sourceOffset,
    targetOffset,
    target
  );  // 判断是否同向
  const sides = getSidesFromPoints([
    source,
    target,
    sourceOffset,
    targetOffset,
  ]);  // 获取边界信息

  // 判断节点是否过近
  const isTooClose = isHorizontalLayout
    ? sides.right - sides.left < minOffset
    : sides.bottom - sides.top < minOffset;
  // 判断是否可以直接连接
  const isDirectConnect = isHorizontalLayout
    ? isSameDirection && source.x < target.x
    : isSameDirection && source.y < target.y;

  if (isTooClose || isDirectConnect) {
    // 3. 如果节点较近或可直接连接,返回简单路径
    edgePoints = getSimplePath({
      source,
      target,
      sourceOffset,
      targetOffset,
      isDirectConnect,
    });
    // 优化输入点
    optimized = optimizeInputPoints({
      source: oldSource,
      target: oldTarget,
      sourceOffset,
      targetOffset,
      edgePoints,
    });
    edgePoints = optimized.edgePoints;
  } else {
    // 3. 获取两个扩展矩形的顶点
    edgePoints = [
      ...getVerticesFromRectVertex(expandedSource, targetOffset),
      ...getVerticesFromRectVertex(expandedTarget, sourceOffset),
    ];
    // 4. 计算可能的中点和交点
    edgePoints = edgePoints.concat(
      getCenterPoints({
        source: expandedSource,
        target: expandedTarget,
        sourceOffset,
        targetOffset,
      })
    );
    // 5. 合并临近坐标点并去除重复点
    optimized = optimizeInputPoints({
      source: oldSource,
      target: oldTarget,
      sourceOffset,
      targetOffset,
      edgePoints,
    });
    // 6. 使用A*算法寻找最优路径
    edgePoints = getAStarPath({
      points: optimized.edgePoints,
      source: optimized.source,
      target: optimized.target,
      sourceRect: getExpandedRect(sourceRect, offset / 2),
      targetRect: getExpandedRect(targetRect, offset / 2),
    });
  }

  // 返回简化后的路径点和输入点集合
  return {
    points: reducePoints([optimized.source, ...edgePoints, optimized.target]),
    inputPoints: optimized.edgePoints,
  };
};