/*
 * @Description: 多边形核心算法
 * 源码Gitee：https://gitee.com/dhzx/js-polygon-algorithm
 */

// import { toRadian, Vector } from 'src/modules/vector/utils';

interface Point {
  x: number;
  y: number;
  gap?: number;
  [proppName: string]: any;
}

/**
 * 获取多边形中心点
 * @param {Point[]} points 点坐标数组 [{x:0,y:0}...]
 */
export function getPolygonCenter(points: Point[]) {
  if (!Array.isArray(points) || points.length < 3) {
    console.error('多边形坐标集合不能少于3个');
    return;
  }
  const result = { x: 0, y: 0 };
  points.forEach((p) => {
    result.x += p.x;
    result.y += p.y;
  });
  result.x /= points.length;
  result.y /= points.length;
  return result;
}

/**
 * 获取多边形重心（质心）
 * @param {Point[]} points 点坐标数组 [{x:0,y:0}...]
 */
export function getPolygonBaryCenter(points: Point[]) {
  if (!Array.isArray(points) || points.length < 3) {
    console.error('多边形坐标集合不能少于3个');
    return;
  }
  const result = { x: 0, y: 0 };
  let area = 0;
  for (let i = 1; i <= points.length; i++) {
    const curX = points[i % points.length].x;
    const curY = points[i % points.length].y;
    const nextX = points[i - 1].x;
    const nextY = points[i - 1].y;
    const temp = (curX * nextY - curY * nextX) / 2;
    area += temp;
    result.x += (temp * (curX + nextX)) / 3;
    result.y += (temp * (curY + nextY)) / 3;
  }
  result.x /= area;
  result.y /= area;
  return result;
}

/**
 * 判断点是否在多边形内部
 * @param {Point} point 点坐标
 * @param {Point[]} points 点坐标数组 [{x:0,y:0}...]
 * @returns
 */
export function isInPolygon(point: Point, points: Point[]) {
  if (!Array.isArray(points) || points.length < 3) {
    console.error('多边形坐标集合不能少于3个');
    return;
  }
  const n = points.length;
  let nCross = 0;
  for (let i = 0; i < n; i++) {
    const p1 = points[i];
    const p2 = points[(i + 1) % n];
    // 求解 y=p.y 与 p1 p2 的交点
    // p1p2 与 y=p0.y平行
    if (p1.y === p2.y) continue;
    // 交点在p1p2延长线上
    if (point.y < Math.min(p1.y, p2.y)) continue;
    // 交点在p1p2延长线上
    if (point.y >= Math.max(p1.y, p2.y)) continue;
    // 求交点的 X 坐标
    const x = ((point.y - p1.y) * (p2.x - p1.x)) / (p2.y - p1.y) + p1.x;
    // 只统计单边交点
    if (x > point.x) nCross++;
  }
  return nCross % 2 === 1;
}

// /**
//  * 缩放多边形坐标
//  * https://blog.csdn.net/sun_and_breeze/article/details/107517088
//  * @decoration 需配合顺时针判断方法一起使用
//  * @param {Point[]} points 点坐标数组 [{x:0,y:0}...]
//  * @param {number} extra 外延大小。为正: 向外扩; 为负: 向内缩
//  * @return {Point[]} 扩展或缩小后的多边形点坐标数组
//  */
// export function scalePolygon2(points: Point[], extra: number) {
//   if (!Array.isArray(points) || points.length < 3) {
//     console.error('多边形坐标集合不能少于3个');
//     return;
//   }
//   const ps = points;
//   // 通过顺时针判断取正值还是负值
//   const extra0 = isClockwise(ps) ? -extra : extra;

//   const norm = (x: number, y: number) => Math.sqrt(x * x + y * y);

//   const len = ps.length;
//   const polygon = [];
//   for (let i = 0; i < len; i++) {
//     const point = ps[i];
//     const point1 = ps[i === 0 ? len - 1 : i - 1];
//     const point2 = ps[i === len - 1 ? 0 : i + 1];

//     // 向量PP1
//     const vectorX1 = point1.x - point.x; // 向量PP1 横坐标
//     const vectorY1 = point1.y - point.y; // 向量PP1 纵坐标
//     const n1 = norm(vectorX1, vectorY1); // 向量的平方根 为了对向量PP1做单位化
//     let vectorUnitX1 = vectorX1 / n1; // 向量单位化 横坐标
//     let vectorUnitY1 = vectorY1 / n1; // 向量单位化 纵坐标

//     // 向量PP2
//     const vectorX2 = point2.x - point.x; // 向量PP2 横坐标
//     const vectorY2 = point2.y - point.y; // 向量PP2 纵坐标
//     const n2 = norm(vectorX2, vectorY2); // 向量的平方根 为了对向量PP1做单位化
//     let vectorUnitX2 = vectorX2 / n2; // 向量单位化 横坐标
//     let vectorUnitY2 = vectorY2 / n2; // 向量单位化 纵坐标

//     // PQ距离
//     const vectorLen =
//       -extra0 /
//       Math.sqrt(
//         (1 - (vectorUnitX1 * vectorUnitX2 + vectorUnitY1 * vectorUnitY2)) / 2
//       );

//     // 根据向量的叉乘积来判断角是凹角还是凸角
//     if (vectorX1 * vectorY2 + -1 * vectorY1 * vectorX2 < 0) {
//       vectorUnitX2 *= -1;
//       vectorUnitY2 *= -1;
//       vectorUnitX1 *= -1;
//       vectorUnitY1 *= -1;
//     }

//     // PQ的方向
//     const vectorX = vectorUnitX1 + vectorUnitX2;
//     const vectorY = vectorUnitY1 + vectorUnitY2;
//     const n = vectorLen / norm(vectorX, vectorY);
//     const vectorUnitX = vectorX * n;
//     const vectorUnitY = vectorY * n;

//     const polygonX = vectorUnitX + point.x;
//     const polygonY = vectorUnitY + point.y;

//     polygon[i] = { x: polygonX, y: polygonY };
//   }

//   return polygon;
// }

/**
 * 缩放多边形坐标
 * https://blog.csdn.net/sun_and_breeze/article/details/107517088
 * @decoration 需配合顺时针判断方法一起使用
 * @param {Point[]} points 点坐标数组 [{x:0,y:0}...]
 * @return {Point[]} 扩展或缩小后的多边形点坐标数组
 */
export function scalePolygon(points: Point[]) {
  if (!Array.isArray(points) || points.length < 3) {
    console.error('多边形坐标集合不能少于3个');
    return;
  }

  const norm = (x: number, y: number) => Math.sqrt(x * x + y * y);

  const ps = points;
  const len = ps.length;
  const polygon: any[] = [];
  const is_clockwise = isClockwise(ps);

  for (let i = 0; i < len; i++) {
    const pointP = ps[i]; // 当前点
    const pointP1 = ps[i === len - 1 ? 0 : i + 1]; // 后一个点
    const pointP2 = ps[i === 0 ? len - 1 : i - 1]; // 前一个点

    // 边距
    const extraNum = 20;
    // 通过顺时针判断取正值还是负值
    const extra0 = is_clockwise ? -extraNum : extraNum;

    // 向量PP1
    const vectorX1 = pointP1.x - pointP.x; // 向量PP1 横坐标
    const vectorY1 = pointP1.y - pointP.y; // 向量PP1 纵坐标
    const n1 = norm(vectorX1, vectorY1); // 向量的平方根 为了对向量PP1做单位化
    let vectorUnitX1 = vectorX1 / n1; // 向量单位化 横坐标
    let vectorUnitY1 = vectorY1 / n1; // 向量单位化 纵坐标

    // 向量PP2
    const vectorX2 = pointP2.x - pointP.x; // 向量PP2 横坐标
    const vectorY2 = pointP2.y - pointP.y; // 向量PP2 纵坐标
    const n2 = norm(vectorX2, vectorY2); // 向量的平方根 为了对向量PP2做单位化
    let vectorUnitX2 = vectorX2 / n2; // 向量单位化 横坐标
    let vectorUnitY2 = vectorY2 / n2; // 向量单位化 纵坐标

    const vectorUnit1 = { x: vectorUnitX1, y: vectorUnitY1 };
    const vectorUnit2 = { x: vectorUnitX2, y: vectorUnitY2 };
    console.log('PP1的单位向量', vectorUnit1, 'PP2的单位向量', vectorUnit2);

    // PQ距离
    const vectorLen =
      extra0 /
      Math.sqrt(
        (1 - (vectorUnitX1 * vectorUnitX2 + vectorUnitY1 * vectorUnitY2)) / 2
      );

    // 根据向量的叉乘积来判断角是凹角还是凸角
    if (vectorX1 * vectorY2 + -1 * vectorY1 * vectorX2 < 0) {
      vectorUnitX2 *= -1;
      vectorUnitY2 *= -1;
      vectorUnitX1 *= -1;
      vectorUnitY1 *= -1;
    }

    // PQ的方向
    const vectorX = vectorUnitX1 + vectorUnitX2;
    const vectorY = vectorUnitY1 + vectorUnitY2;
    const n = vectorLen / norm(vectorX, vectorY);
    const vectorUnitX = vectorX * n;
    const vectorUnitY = vectorY * n;

    const polygonX = vectorUnitX + pointP.x;
    const polygonY = vectorUnitY + pointP.y;
    polygon[i] = { x: polygonX, y: polygonY };
  }

  console.log('polygon', polygon);
  return polygon;
}

/**
 * 判断坐标数组是否顺时针（默认为false）
 * @param {Point[]} points 点坐标数组 [{x:0,y:0}...]
 * @returns {boolean} 是否顺时针
 */
export function isClockwise(points: Point[]) {
  // 三个点可以判断矢量是顺时针旋转还是逆时针旋转的，但由于可能存在凹边，所以并不是任意三点都可以正确反映多边形的走向
  // 因此需要取多边形中绝对是凸边的点来判断，
  // 多边形中的极值点（x最大或x最小或y最大或y最小）它与相邻两点构成的边必然是凸边，因此我们先取出多边形中的极值点，再由极值点和其前后两点去判断矢量的走向，从而判断出多边形的走向。
  if (!Array.isArray(points) || points.length < 3) {
    console.error('多边形坐标集合不能少于3个');
    return false;
  }
  let coords = JSON.parse(JSON.stringify(points));

  if (coords[0] === coords[coords.length - 1]) {
    coords = coords.slice(0, coords.length - 1);
  }
  coords = coords.reverse();
  let maxXIndex = 0;
  let maxX = parseFloat(coords[maxXIndex].x);
  let c1;
  let c2;
  let c3;
  for (let i = 0; i < coords.length; i++) {
    if (parseFloat(coords[i].x) > maxX) {
      maxX = parseFloat(coords[i].x);
      maxXIndex = i;
    }
  }
  if (maxXIndex === 0) {
    c1 = coords[coords.length - 1];
    c2 = coords[maxXIndex];
    c3 = coords[maxXIndex + 1];
  } else if (maxXIndex === coords.length - 1) {
    c1 = coords[maxXIndex - 1];
    c2 = coords[maxXIndex];
    c3 = coords[0];
  } else {
    c1 = coords[maxXIndex - 1];
    c2 = coords[maxXIndex];
    c3 = coords[maxXIndex + 1];
  }
  const x1 = parseFloat(c1.x);
  const y1 = parseFloat(c1.y);
  const x2 = parseFloat(c2.x);
  const y2 = parseFloat(c2.y);
  const x3 = parseFloat(c3.x);
  const y3 = parseFloat(c3.y);
  const s = (x1 - x3) * (y2 - y3) - (x2 - x3) * (y1 - y3);
  return s < 0;
}
