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

/**
 * 创建一个2维度向量类
 */
export class Vector {
  components: any;
  length: number;
  constructor(...components: any) {
    this.components = components;
    const pointO = components[0];
    const pointA = components[1];

    const diffX = pointA.x - pointO.x;
    const diffY = pointA.y - pointO.y;
    this.length = Math.sqrt(diffX * diffX + diffY * diffY);
  }

  // 加法
  add(components: any) {
    const pointO = this.components[0];
    const pointA = this.components[1];
    const pointB = components;
    return {
      x: pointA.x + pointB.x - pointO.x,
      y: pointA.y + pointB.y - pointO.y,
    };
  }

  // 减
  subtract(components: any) {
    const pointO = this.components[0];
    const pointA = this.components[1];
    const pointC = components;
    return {
      x: pointA.x - pointC.x + pointO.x,
      y: pointA.y - pointC.y + pointO.y,
    };
  }

  // 向量夹角
  // 向量点乘：（内积）
  //
  // 数学公式
  // a向量(x1,y1) b向量(x2,y2)
  // a向量 点乘 b向量 = x1*x2 + y1*y2
  //                 = a向量的模 * b向量的模 * cos(夹角)
  // https://zhuanlan.zhihu.com/p/359975221
  dotProduct(components: any) {
    const pointO = this.components[0];
    const pointA = this.components[1];
    const pointB = components;

    const _pointA = { x: pointA.x - pointO.x, y: pointA.y - pointO.y };
    const _pointB = { x: pointB.x - pointO.x, y: pointB.y - pointO.y };

    // 求向量OA与OB的夹角

    // AB向量 = OB向量 - OA向量
    // const ab_x = _pointB.x - _pointA.x;
    // const ab_y = _pointB.y - _pointA.y;

    // OA向量 * OB向量 > 0，夹角在0~90度之间
    // OA向量 * OB向量 = 0，正交，相互垂直
    // OA向量 * OB向量 < 0，夹角在90度~180度之间
    //
    // OA向量 * OB向量 = x1*x2 + y1*y2;
    const abMultiplication = _pointA.x * _pointB.x + _pointA.y * _pointB.y;

    // OA向量的模
    const abs_OA = new Vector({ x: 0, y: 0 }, _pointA).length;

    // OB向量的模
    const abs_OB = new Vector({ x: 0, y: 0 }, _pointB).length;

    // 得到弧度值
    let result = Math.acos(abMultiplication / (abs_OA * abs_OB));
    // 转为角度值
    result = toAngle(result);

    const arr = [];
    arr.push(_pointA);
    arr.push({ x: 0, y: 0 });
    arr.push(_pointB);
    const is_clockwise = isClockwise(arr);
    if (is_clockwise) {
      // 顺时针方向 大于180度
      result = 360 - result;
    } else {
    }

    return result;
  }

  // 向量叉乘
  // a向量(x1,y1) b向量(x2,y2)
  // a向量 叉乘 b向量 = c向量
  // c向量的模 = x1*y2 - x2*y1
  //          = a向量的模 * b向量的模 * sin(夹角)
  // 如果以向量a和向量b边构成一个平行四边形，那么这两个向量外积的模长与这个平行四边形的面积相等。

  // 向量单位化
  // 单位化后的向量以(0,0)点为原点
  unitization() {
    const pointO = this.components[0];
    const pointA = this.components[1];
    const norm = (x: number, y: number) => Math.sqrt(x * x + y * y);
    const vectorX1 = pointA.x - pointO.x; // 向量OA 横坐标
    const vectorY1 = pointA.y - pointO.y; // 向量OA 纵坐标
    const n1 = norm(vectorX1, vectorY1); // 向量的平方根 为了对向量OA做单位化
    const vectorUnitX1 = vectorX1 / n1; // 向量单位化 横坐标
    const vectorUnitY1 = vectorY1 / n1; // 向量单位化 纵坐标
    return {
      point: { x: vectorUnitX1, y: vectorUnitY1 },
      n: n1,
    };
  }
}

/**
 * 判断坐标数组是否顺时针（默认为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;
}

/**
 * 转为弧度值
 */
export function toRadian(val: number) {
  return (val * Math.PI) / 180;
}
/**
 * 转角度值
 */
export function toAngle(val: number) {
  return (val * 180) / Math.PI;
}
