import { Vector2, ShapeUtils, Box2 } from "three";
import { Transform } from "konva/lib/Util";
import { round } from "./shared.ts";

export type Pos = { x: number; y: number };

export const vector = (pos: Pos = { x: 0, y: 0 }): Vector2 => {
  return new Vector2(pos.x, pos.y);
  // if (pos instanceof Vector2) {
  //   return pos;
  // } else {
  //   return new Vector2(pos.x, pos.y);
  // }
};
export const lVector = (line: Pos[]) => line.map(vector);

export const zeroEq = (n: number) => Math.abs(n) < 0.0001;
export const numEq = (p1: number, p2: number) => zeroEq(p1 - p2);
export const vEq = (v1: Pos, v2: Pos) => numEq(v1.x, v2.x) && numEq(v1.y, v2.y);

export const vsBound = (positions: Pos[]) => {
  const box = new Box2();
  box.setFromPoints(positions.map(vector));
  return box;
};

/**
 * 获取线段方向
 * @param line 线段
 * @returns 方向
 */
export const lineVector = (line: Pos[]) =>
  vector(line[1]).sub(vector(line[0])).normalize();

/**
 * 点是否相同
 * @param p1 点1
 * @param p2 点2
 * @returns 是否相等
 */
export const eqPoint = vEq;

/**
 * 方向是否相同
 * @param p1 点1
 * @param p2 点2
 * @returns 是否相等
 */
export const eqNGDire = (p1: Pos, p2: Pos) =>
  eqPoint(p1, p2) || eqPoint(p1, vector(p2).multiplyScalar(-1));

/**
 * 获取两点距离
 * @param p1 点1
 * @param p2 点2
 * @returns 距离
 */
export const lineLen = (p1: Pos, p2: Pos) => vector(p1).distanceTo(p2);

export const vectorLen = (dire: Pos) => vector(dire).length();

/**
 * 获取向量的垂直向量
 * @param dire 原方向
 * @returns 垂直向量
 */
export const verticalVector = (dire: Pos) =>
  vector({ x: -dire.y, y: dire.x }).normalize();

/**
 * 获取旋转指定度数后的向量
 * @param pos 远向量
 * @param angleRad 旋转角度
 * @returns 旋转后向量
 */
export const rotateVector = (pos: Pos, angleRad: number) =>
  new Transform().rotate(angleRad).point(pos);

/**
 * 创建线段
 * @param dire 向量
 * @param start 起始点
 * @param dis 长度
 * @returns 线段
 */

export const getVectorLine = (
  dire: Pos,
  start: Pos = { x: 0, y: 0 },
  dis: number = 1
) => [start, vector(dire).multiplyScalar(dis).add(start)];

/**
 * 获取线段的垂直方向向量
 * @param line 原线段
 * @returns 垂直向量
 */
export const lineVerticalVector = (line: Pos[]) =>
  verticalVector(lineVector(line));

/**
 * 获取向量的垂直线段
 * @param dire 向量
 * @param start 线段原点
 * @param len 线段长度
 */
export const verticalVectorLine = (
  dire: Pos,
  start: Pos = { x: 0, y: 0 },
  len: number = 1
) => getVectorLine(verticalVector(dire), start, len);

/**
 * 获取两向量角度(从向量a出发)
 * @param v1 向量a
 * @param v2 向量b
 * @returns 两向量夹角弧度， 逆时针为正，顺时针为负
 */
export const vector2IncludedAngle = (v1: Pos, v2: Pos) => {
  const start = vector(v1);
  const end = vector(v2);
  const angle = start.angleTo(end);
  return start.cross(end) > 0 ? angle : -angle;
};

/**
 * 获取两线段角度(从线段a出发)
 * @param line1 线段a
 * @param line2 线段b
 * @returns 两线段夹角弧度
 */
export const line2IncludedAngle = (line1: Pos[], line2: Pos[]) =>
  vector2IncludedAngle(lineVector(line1), lineVector(line2));

/**
 * 获取向量与X正轴角度
 * @param v 向量
 * @returns 夹角弧度
 */
const nXAxis = vector({ x: 1, y: 0 });
export const vectorAngle = (v: Pos) => {
  const start = vector(v);
  return start.angleTo(nXAxis);
};

/**
 * 获取线段与方向的夹角弧度
 * @param line 线段
 * @param dire 方向
 * @returns 线段与方向夹角弧度
 */
export const lineAndVectorIncludedAngle = (line: Pos[], v: Pos) =>
  vector2IncludedAngle(lineVector(line), v);

/**
 * 获取线段中心点
 * @param line
 * @returns
 */
export const lineCenter = (line: Pos[]) =>
  vector(line[0]).add(line[1]).multiplyScalar(0.5);

export const pointsCenter = (points: Pos[]) => {
  if (points.length === 0) return { x: 0, y: 0 };
  const v = vector(points[0]);
  for (let i = 1; i < points.length; i++) {
    v.add(points[i]);
  }
  return v.multiplyScalar(1 / points.length);
};

export const lineJoin = (l1: Pos[], l2: Pos[]) => {
  const checks = [
    [l1[0], l2[0]],
    [l1[0], l2[1]],
    [l1[1], l2[0]],
    [l1[1], l2[1]],
  ];
  const ndx = checks.findIndex((line) => eqPoint(line[0], line[1]));
  if (~ndx) {
    return checks[ndx];
  } else {
    return false;
  }
};

export const isLineEqual = (l1: Pos[], l2: Pos[]) =>
  eqPoint(l1[0], l2[0]) && eqPoint(l1[1], l2[1]);

export const isLineReverseEqual = (l1: Pos[], l2: Pos[]) =>
  eqPoint(l1[0], l2[1]) && eqPoint(l1[1], l2[0]);

export const isLineIntersect = (l1: Pos[], l2: Pos[]) => {
  const s1 = l2[1].y - l2[0].y;
  const s2 = l2[1].x - l2[0].x;
  const s3 = l1[1].x - l1[0].x;
  const s4 = l1[1].y - l1[0].y;
  const s5 = l1[0].y - l2[0].y;
  const s6 = l1[0].x - l2[0].x;

  const denominator = s1 * s3 - s2 * s4;
  const ua = round((s2 * s5 - s1 * s6) / denominator, 6);
  const ub = round((s3 * s5 - s4 * s6) / denominator, 6);

  if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) {
    return true;
  } else {
    return false;
  }
};

export const vectorParallel = (dire1: Pos, dire2: Pos) =>
  zeroEq(vector(dire1).cross(dire2));

export const lineParallelRelationship = (l1: Pos[], l2: Pos[]) => {
  const dire1 = lineVector(l1);
  const dire2 = lineVector(l2);

  // 计算线段的法向量
  const normal1 = verticalVector(dire1);
  const normal2 = verticalVector(dire2);
  const startDire = lineVector([l1[0], l2[0]]);

  // 计算线段的参数方程
  const t1 = round(normal2.dot(startDire) / normal2.dot(dire1), 6);
  const t2 = round(normal1.dot(startDire) / normal1.dot(dire2), 6);

  if (t1 === 0 && t2 === 0) {
    return RelationshipEnum.Overlap;
  }

  if (eqPoint(normal1, normal2) || eqPoint(normal1, normal2.clone().negate())) {
    return lineJoin(l1, l2)
      ? RelationshipEnum.Overlap
      : RelationshipEnum.Parallel;
  }
};

export enum RelationshipEnum {
  // 重叠
  Overlap = "Overlap",
  // 相交
  Intersect = "Intersect",
  // 延长相交
  ExtendIntersect = "ExtendIntersect",
  // 平行
  Parallel = "Parallel",
  // 首尾连接
  Join = "Join",
  // 一样
  Equal = "Equal",
  // 反向
  ReverseEqual = "ReverseEqual",
}
/**
 * 获取两线段是什么关系，（重叠、相交、平行、首尾相接等）
 * @param l1
 * @param l2
 * @returns RelationshipEnum
 */
export const lineRelationship = (l1: Pos[], l2: Pos[]) => {
  if (isLineEqual(l1, l2)) {
    return RelationshipEnum.Equal;
  } else if (isLineReverseEqual(l1, l2)) {
    return RelationshipEnum.ReverseEqual;
  }

  const parallelRelationship = lineParallelRelationship(l1, l2);
  if (parallelRelationship) {
    return parallelRelationship;
  } else if (lineJoin(l1, l2)) {
    return RelationshipEnum.Join;
  } else if (isLineIntersect(l1, l2)) {
    return RelationshipEnum.Intersect; // 两线段相交
  } else {
    return RelationshipEnum.ExtendIntersect; // 延长可相交
  }
};

export const createLine = (p: Pos, v: Pos, l?: number) => {
  const line = [p];
  if (l) {
    v = vector(v).multiplyScalar(l);
  }
  line[1] = vector(line[0]).add(v);
  return line;
};

/**
 * 获取两线段交点,可延长相交
 * @param l1 线段1
 * @param l2 线段2
 * @returns 交点坐标
 */
export const lineIntersection = (l1: Pos[], l2: Pos[]) => {
  // 定义两条线段的起点和终点坐标
  const [line1Start, line1End] = lVector(l1);
  const [line2Start, line2End] = lVector(l2);

  // 计算线段的方向向量
  const dir1 = line1End.clone().sub(line1Start);
  const dir2 = line2End.clone().sub(line2Start);

  // 计算参数方程中的系数
  const a = dir1.x;
  const b = -dir2.x;
  const c = dir1.y;
  const d = -dir2.y;

  const e = line2Start.x - line1Start.x;
  const f = line2Start.y - line1Start.y;

  // 求解参数t和s
  const t = (d * e - b * f) / (a * d - b * c);
  // 计算交点坐标
  const p = line1Start.clone().add(dir1.clone().multiplyScalar(t));

  if (isNaN(p.x) || !isFinite(p.x) || isNaN(p.y) || !isFinite(p.y)) return null;

  return p;
};

/**
 * 获取点是否在线上
 * @param line 线段
 * @param position 点
 */
export const lineInner = (line: Pos[], position: Pos) => {
  // 定义线段的起点和终点坐标
  const [A, B] = lVector(line);
  // 定义一个点的坐标
  const P = vector(position);

  // 计算向量 AP 和 AB
  const AP = P.clone().sub(A);
  const AB = B.clone().sub(A);

  // 计算叉积
  const crossProduct = AP.x * AB.y - AP.y * AB.x;

  // 如果叉积不为 0，说明点 P 不在直线 AB 上
  if (!zeroEq(crossProduct)) {
    return false;
  }
  // 检查点 P 的坐标是否在 A 和 B 的坐标范围内
  return (
    Math.min(A.x, B.x) <= P.x &&
    P.x <= Math.max(A.x, B.x) &&
    Math.min(A.y, B.y) <= P.y &&
    P.y <= Math.max(A.y, B.y)
  );
};

/**
 * 获取点在线段上的投影
 * @param line 线段
 * @param position 点
 * @returns 投影信息
 */
export const linePointProjection = (line: Pos[], position: Pos) => {
  // 定义线段的起点和终点坐标
  const [lineStart, lineEnd] = lVector(line);
  // 定义一个点的坐标
  const point = vector(position);

  // 计算线段的方向向量
  const lineDir = lineEnd.clone().sub(lineStart);
  // 计算点到线段起点的向量
  const pointToLineStart = point.clone().sub(lineStart);
  // 计算点在线段方向上的投影长度
  const t = pointToLineStart.dot(lineDir.normalize());
  // 计算投影点的坐标
  return lineStart.add(lineDir.multiplyScalar(t));
};

/**
 * 获取点距离线段最近距离
 * @param line 直线
 * @param position 参考点
 * @returns 距离
 */
export const linePointLen = (line: Pos[], position: Pos) =>
  lineLen(position, linePointProjection(line, position));

/**
 * 计算多边形是否为逆时针
 * @param points 多边形顶点
 * @returns true | false
 */
export const isPolygonCounterclockwise = (points: Pos[]) =>
  ShapeUtils.isClockWise(points.map(vector));

/**
 * 切割线段，返回连段切割点
 * @param line 线段
 * @param amount 切割份量
 * @param unit 一份单位大小
 * @returns 点数组
 */
export const lineSlice = (
  line: Pos[],
  amount: number,
  unit = lineLen(line[0], line[1]) / amount
) =>
  new Array(unit)
    .fill(0)
    .map((_, i) => linePointProjection(line, { x: i * unit, y: i * unit }));

/**
 * 线段是否相交多边形
 * @param polygon 多边形
 * @param line 检测线段
 * @returns
 */
export const isPolygonLineIntersect = (polygon: Pos[], line: Pos[]) => {
  for (let i = 0; i < polygon.length; i++) {
    if (isLineIntersect([polygon[i], polygon[i + 1]], line)) {
      return true;
    }
  }
  return false;
};

/**
 * 通过角度和两个点获取两者的连接点，
 * @param p1
 * @param p2
 * @param rad
 */
export const joinPoint = (p1: Pos, p2: Pos, rad: number) => {
  const lvector = new Vector2()
    .subVectors(p1, p2)
    .rotateAround({ x: 0, y: 0 }, rad);

  return vector(p2).add(lvector);
};

/**
 * 要缩放多少才能到达目标
 * @param origin 缩放原点
 * @param scaleDirection 缩放方向
 * @param p1 当前点位
 * @param p2 目标点位
 * @returns
 */
export function calculateScaleFactor(
  origin: Pos,
  scaleDirection: Pos,
  p1: Pos,
  p2: Pos
) {
  const op1 = vector(p1).sub(origin);
  const op2 = vector(p2).sub(origin);
  const xZero = zeroEq(op1.x);
  const yZero = zeroEq(op1.y);

  if (zeroEq(op1.x) || zeroEq(op2.y)) return;
  if (zeroEq(scaleDirection.x)) {
    if (zeroEq(p2.x - p1.x)) {
      return zeroEq(op1.y - op2.y) ? 1 : yZero ? null : op2.y / op1.y;
    } else {
      return;
    }
  }
  if (zeroEq(scaleDirection.y)) {
    if (zeroEq(p2.y - p1.y)) {
      return zeroEq(op1.x - op2.x) ? 1 : xZero ? null : op2.x / op1.x;
    } else {
      return;
    }
  }
  if (xZero && yZero) {
    return null;
  }
  const xScaleFactor = op2.x / (op1.x * scaleDirection.x);
  const yScaleFactor = op2.y / (op1.y * scaleDirection.y);

  if (xZero) {
    return yScaleFactor;
  } else if (yZero) {
    return xScaleFactor;
  }
  if (zeroEq(xScaleFactor - yScaleFactor)) {
    return xScaleFactor;
  }
}

// 获取两线段的矩阵关系
export const getLineRelationMat = (l1: [Pos, Pos], l2: [Pos, Pos]) => {
  // 提取点
  const P1 = l1[0]; // l1 的起点
  const P1End = l1[1]; // l1 的终点
  const P2 = l2[0]; // l2 的起点
  const P2End = l2[1]; // l2 的终点

  // 计算方向向量
  const d1 = { x: P1End.x - P1.x, y: P1End.y - P1.y };
  const d2 = { x: P2End.x - P2.x, y: P2End.y - P2.y };

  // 计算方向向量的长度
  const lengthD1 = Math.sqrt(d1.x ** 2 + d1.y ** 2);
  const lengthD2 = Math.sqrt(d2.x ** 2 + d2.y ** 2);

  if (lengthD1 === 0 || lengthD2 === 0) return new Transform();

  // 归一化方向向量
  const unitD1 = { x: d1.x / lengthD1, y: d1.y / lengthD1 };
  const unitD2 = { x: d2.x / lengthD2, y: d2.y / lengthD2 };

  // 计算旋转角度
  const angle = Math.atan2(unitD2.y, unitD2.x) - Math.atan2(unitD1.y, unitD1.x);
  // 计算旋转矩阵
  // 计算缩放因子
  const scale = lengthD2 / lengthD1;
  // 计算平移向量
  const translation = [P2.x - P1.x, P2.y - P1.y];

  const mat = new Transform()
    .translate(translation[0], translation[1])
    .translate(P1.x, P1.y)
    .scale(scale, scale)
    .rotate(angle)
    .translate(-P1.x, -P1.y);

  
  if (!eqPoint(mat.point(P1), P2)) {
    console.error('对准不正确 旋转后P1', mat.point(P1), P2)
  }
  if (!eqPoint(mat.point(P1End), P2End)) {
    console.error('对准不正确 旋转后P2', mat.point(P1End), P1End)
  }
  return mat
};
