import { vec2 } from '@antv/matrix-util';
import { each } from '@antv/util';
import { Coordinate, PathCommand } from '../../../dependents';
import { Point, Position } from '../../../interface';
import { getDistanceToCenter } from '../../../util/coordinate';

function _points2path(points: Point[], isInCircle: boolean): PathCommand[] {
  const path = [];
  if (points.length) {
    path.push(['M', points[0].x, points[0].y]);
    for (let i = 1, length = points.length; i < length; i += 1) {
      const item = points[i];
      path.push(['L', item.x, item.y]);
    }

    if (isInCircle) {
      path.push(['Z']);
    }
  }

  return path;
}

function _convertArr(arr: number[], coord: Coordinate): any[] {
  const tmp = [arr[0]];
  for (let i = 1, len = arr.length; i < len; i = i + 2) {
    const point = coord.convert({
      x: arr[i],
      y: arr[i + 1],
    });
    tmp.push(point.x, point.y);
  }
  return tmp;
}
function _convertArcPath(path: PathCommand, coord: Coordinate): any[] {
  const { isTransposed } = coord;
  const r = path[1];
  const x = path[6];
  const y = path[7];
  const point = coord.convert({ x, y });
  const direction = isTransposed ? 0 : 1;
  return ['A', r, r, 0, 0, direction, point.x, point.y];
}

function _convertPolarPath(pre: PathCommand, cur: PathCommand, coord: Coordinate): PathCommand[] {
  const { isTransposed, startAngle, endAngle } = coord;
  const prePoint =
    pre[0].toLowerCase() === 'a'
      ? {
          x: pre[6],
          y: pre[7],
        }
      : {
          x: pre[1],
          y: pre[2],
        };
  const curPoint = {
    x: cur[1],
    y: cur[2],
  };
  const rst = [];
  const xDim = isTransposed ? 'y' : 'x';
  const angleRange = Math.abs(curPoint[xDim] - prePoint[xDim]) * (endAngle - startAngle);
  const direction = curPoint[xDim] >= prePoint[xDim] ? 1 : 0; // 圆弧的方向
  const flag = angleRange > Math.PI ? 1 : 0; // 大弧还是小弧标志位
  const convertPoint = coord.convert(curPoint);
  const r = getDistanceToCenter(coord, convertPoint);
  if (r >= 0.5) {
    // 小于1像素的圆在图像上无法识别
    if (angleRange === Math.PI * 2) {
      const middlePoint = {
        x: (curPoint.x + prePoint.x) / 2,
        y: (curPoint.y + prePoint.y) / 2,
      };
      const middleConvertPoint = coord.convert(middlePoint);
      rst.push(['A', r, r, 0, flag, direction, middleConvertPoint.x, middleConvertPoint.y]);
      rst.push(['A', r, r, 0, flag, direction, convertPoint.x, convertPoint.y]);
    } else {
      rst.push(['A', r, r, 0, flag, direction, convertPoint.x, convertPoint.y]);
    }
  }
  return rst;
}

// 当存在整体的圆时，去除圆前面和后面的线，防止出现直线穿过整个圆的情形
function _filterFullCirleLine(path: PathCommand[]) {
  each(path, (subPath, index) => {
    const cur = subPath;
    if (cur[0].toLowerCase() === 'a') {
      const pre = path[index - 1];
      const next = path[index + 1];
      if (next && next[0].toLowerCase() === 'a') {
        if (pre && pre[0].toLowerCase() === 'l') {
          pre[0] = 'M';
        }
      } else if (pre && pre[0].toLowerCase() === 'a') {
        if (next && next[0].toLowerCase() === 'l') {
          next[0] = 'M';
        }
      }
    }
  });
}

/**
 * @ignore
 * 计算光滑的贝塞尔曲线
 */
export const smoothBezier = (
  points: Position[],
  smooth: number,
  isLoop: boolean,
  constraint: Position[]
): Position[] => {
  const cps = [];
  const hasConstraint = !!constraint;

  let prevPoint: Position;
  let nextPoint: Position;
  let min: Position;
  let max: Position;
  let nextCp0: Position;
  let cp1: Position;
  let cp0: Position;

  if (hasConstraint) {
    [min, max] = constraint;
    for (let i = 0, l = points.length; i < l; i++) {
      const point = points[i];
      min = vec2.min([0, 0], min, point) as [number, number];
      max = vec2.max([0, 0], max, point) as [number, number];
    }
  }

  for (let i = 0, len = points.length; i < len; i++) {
    const point = points[i];
    if (i === 0 && !isLoop) {
      cp0 = point;
    } else if (i === len - 1 && !isLoop) {
      cp1 = point;
      cps.push(cp0);
      cps.push(cp1);
    } else {
      prevPoint = points[isLoop ? (i ? i - 1 : len - 1) : i - 1];
      nextPoint = points[isLoop ? (i + 1) % len : i + 1];

      let v: [number, number] = [0, 0];
      v = vec2.sub(v, nextPoint, prevPoint) as [number, number];
      v = vec2.scale(v, v, smooth) as [number, number];

      let d0 = vec2.distance(point, prevPoint);
      let d1 = vec2.distance(point, nextPoint);

      const sum = d0 + d1;
      if (sum !== 0) {
        d0 /= sum;
        d1 /= sum;
      }

      let v1 = vec2.scale([0, 0], v, -d0);
      let v2 = vec2.scale([0, 0], v, d1);

      cp1 = vec2.add([0, 0], point, v1) as Position;
      nextCp0 = vec2.add([0, 0], point, v2) as Position;

      // 下一个控制点必须在这个点和下一个点之间
      nextCp0 = vec2.min([0, 0], nextCp0, vec2.max([0, 0], nextPoint, point)) as Position;
      nextCp0 = vec2.max([0, 0], nextCp0, vec2.min([0, 0], nextPoint, point)) as Position;

      // 重新计算 cp1 的值
      v1 = vec2.sub([0, 0], nextCp0, point);
      v1 = vec2.scale([0, 0], v1, -d0 / d1);
      cp1 = vec2.add([0, 0], point, v1) as Position;

      // 上一个控制点必须要在上一个点和这一个点之间
      cp1 = vec2.min([0, 0], cp1, vec2.max([0, 0], prevPoint, point)) as Position;
      cp1 = vec2.max([0, 0], cp1, vec2.min([0, 0], prevPoint, point)) as Position;

      // 重新计算 nextCp0 的值
      v2 = vec2.sub([0, 0], point, cp1);
      v2 = vec2.scale([0, 0], v2, d1 / d0);
      nextCp0 = vec2.add([0, 0], point, v2) as Position;

      if (hasConstraint) {
        cp1 = vec2.max([0, 0], cp1, min) as Position;
        cp1 = vec2.min([0, 0], cp1, max) as Position;
        nextCp0 = vec2.max([0, 0], nextCp0, min) as Position;
        nextCp0 = vec2.min([0, 0], nextCp0, max) as Position;
      }

      cps.push(cp0);
      cps.push(cp1);
      cp0 = nextCp0;
    }
  }

  if (isLoop) {
    cps.push(cps.shift());
  }

  return cps;
};

/**
 * @ignore
 * 贝塞尔曲线
 */
export function catmullRom2bezier(crp: number[], z: boolean, constraint: Position[]): PathCommand[] {
  const isLoop = !!z;
  const pointList = [];
  for (let i = 0, l = crp.length; i < l; i += 2) {
    pointList.push([crp[i], crp[i + 1]]);
  }

  const controlPointList = smoothBezier(pointList, 0.4, isLoop, constraint);
  const len = pointList.length;
  const d1 = [];

  let cp1: Position;
  let cp2: Position;
  let p: Position;

  for (let i = 0; i < len - 1; i++) {
    cp1 = controlPointList[i * 2];
    cp2 = controlPointList[i * 2 + 1];
    p = pointList[i + 1];

    d1.push(['C', cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]]);
  }

  if (isLoop) {
    cp1 = controlPointList[len];
    cp2 = controlPointList[len + 1];
    p = pointList[0];

    d1.push(['C', cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]]);
  }
  return d1;
}

/**
 * @ignore
 * 将点连接成路径 path
 */
export function getLinePath(points: Point[], isInCircle?: boolean): PathCommand[] {
  return _points2path(points, isInCircle);
}

/**
 * @ignore
 * 根据关键点获取限定了范围的平滑线
 */
export function getSplinePath(points: Point[], isInCircle?: boolean, constaint?: Position[]): PathCommand[] {
  const data = [];
  const first = points[0];
  let prePoint = null;
  if (points.length <= 2) {
    // 两点以内直接绘制成路径
    return getLinePath(points, isInCircle);
  }
  for (let i = 0, len = points.length; i < len; i++) {
    const point = points[i];
    if (!prePoint || !(prePoint.x === point.x && prePoint.y === point.y)) {
      data.push(point.x);
      data.push(point.y);
      prePoint = point;
    }
  }
  const constraint = constaint || [
    // 范围
    [0, 0],
    [1, 1],
  ];
  const splinePath = catmullRom2bezier(data, isInCircle, constraint);
  splinePath.unshift(['M', first.x, first.y]);
  return splinePath;
}

/**
 * @ignore
 * 将归一化后的路径数据转换成坐标
 */
export function convertNormalPath(coord, path: PathCommand[]): PathCommand[] {
  const tmp = [];
  each(path, (subPath) => {
    const action = subPath[0];
    switch (action.toLowerCase()) {
      case 'm':
      case 'l':
      case 'c':
        tmp.push(_convertArr(subPath, coord));
        break;
      case 'a':
        tmp.push(_convertArcPath(subPath, coord));
        break;
      case 'z':
      default:
        tmp.push(subPath);
        break;
    }
  });
  return tmp;
}

/**
 * @ignore
 * 将路径转换为极坐标下的真实路径
 */
export function convertPolarPath(coord, path: PathCommand[]): PathCommand[] {
  let tmp = [];
  let pre: PathCommand;
  let cur: PathCommand;
  let transposed: boolean;
  let equals: boolean;
  each(path, (subPath, index) => {
    const action = subPath[0];

    switch (action.toLowerCase()) {
      case 'm':
      case 'c':
      case 'q':
        tmp.push(_convertArr(subPath, coord));
        break;
      case 'l':
        pre = path[index - 1];
        cur = subPath;
        transposed = coord.isTransposed;
        // 是否半径相同，转换成圆弧
        equals = transposed ? pre[pre.length - 2] === cur[1] : pre[pre.length - 1] === cur[2];
        if (equals) {
          tmp = tmp.concat(_convertPolarPath(pre, cur, coord));
        } else {
          // y 不相等，所以直接转换
          tmp.push(_convertArr(subPath, coord));
        }
        break;
      case 'a':
        tmp.push(_convertArcPath(subPath, coord));
        break;
      case 'z':
      default:
        tmp.push(subPath);
        break;
    }
  });
  _filterFullCirleLine(tmp); // 过滤多余的直线
  return tmp;
}
