import { getArcParams } from '@antv/g-canvas';
import { isNumberEqual, isEqual } from '@antv/util';

import { IShape, PathCommand } from '../../dependents';
import { GAnimateCfg } from '../../interface';
import { AnimateExtraCfg } from '../interface';

import { getArcPath, getSectorPath } from '../../util/graphics';

function getAngle(startPoint: number[], arcPath: PathCommand) {
  let { startAngle, endAngle } = getArcParams(startPoint, arcPath);

  if (!isNumberEqual(startAngle, -Math.PI * 0.5) && startAngle < -Math.PI * 0.5) {
    startAngle += Math.PI * 2;
  }
  if (!isNumberEqual(endAngle, -Math.PI * 0.5) && endAngle < -Math.PI * 0.5) {
    endAngle += Math.PI * 2;
  }

  if (arcPath[5] === 0) {
    // 逆时针，需要将 startAngle 和 endAngle 转置，因为 G2 极坐标系为顺时针方向
    [startAngle, endAngle] = [endAngle, startAngle];
  }

  if (isNumberEqual(startAngle, Math.PI * 1.5)) {
    startAngle = Math.PI * -0.5;
  }

  if (isNumberEqual(endAngle, Math.PI * -0.5)) {
    endAngle = Math.PI * 1.5;
  }

  return {
    startAngle,
    endAngle,
  };
}

function getArcStartPoint(path: PathCommand) {
  let startPoint;
  if (path[0] === 'M' || path[0] === 'L') {
    startPoint = [path[1], path[2]];
  } else if (path[0] === 'a' || path[0] === 'A' || path[0] === 'C') {
    startPoint = [path[path.length - 2], path[path.length - 1]];
  }

  return startPoint;
}

/**
 * path 存在以下情况
 * 1. 饼图不为整圆的 path，命令为 M, L, A, L, Z
 * 2. 饼图为整圆的 path，命令为 M, M, A, A, M, Z
 * 3. 环图不为整圆的 path，命令为 M, A, L, A, L, Z
 * 4. 环图为整圆的 path，命令为 M, A, A, M, A, A, M, Z
 * 5. radial-line, 不为整圆时的 path, 命令为 M, A, A, Z
 * 6. radial-line, 为整圆时的 path，命令为 M, A, A, A, A, Z
 * @param path theta 坐标系下圆弧的 path 命令
 */
function getArcInfo(path: PathCommand[]) {
  let startAngle;
  let endAngle;

  const arcPaths = path.filter((command) => {
    return command[0] === 'A' || command[0] === 'a';
  });

  if (arcPaths.length === 0) {
    return {
      startAngle: 0,
      endAngle: 0,
      radius: 0,
      innerRadius: 0,
    };
  }

  const firstArcPathCommand = arcPaths[0];
  const lastArcPathCommand = arcPaths.length > 1 ? arcPaths[1] : arcPaths[0];
  const firstIndex = path.indexOf(firstArcPathCommand);
  const lastIndex = path.indexOf(lastArcPathCommand);
  const firstStartPoint = getArcStartPoint(path[firstIndex - 1]);
  const lastStartPoint = getArcStartPoint(path[lastIndex - 1]);

  const { startAngle: firstStartAngle, endAngle: firstEndAngle } = getAngle(firstStartPoint, firstArcPathCommand);
  const { startAngle: lastStartAngle, endAngle: lastEndAngle } = getAngle(lastStartPoint, lastArcPathCommand);

  if (isNumberEqual(firstStartAngle, lastStartAngle) && isNumberEqual(firstEndAngle, lastEndAngle)) {
    startAngle = firstStartAngle;
    endAngle = firstEndAngle;
  } else {
    startAngle = Math.min(firstStartAngle, lastStartAngle);
    endAngle = Math.max(firstEndAngle, lastEndAngle);
  }

  let radius = firstArcPathCommand[1];
  let innerRadius = arcPaths[arcPaths.length - 1][1];
  if (radius < innerRadius) {
    [radius, innerRadius] = [innerRadius, radius];
  } else if (radius === innerRadius) {
    innerRadius = 0;
  }

  return {
    startAngle,
    endAngle,
    radius,
    innerRadius,
  };
}

/**
 * @ignore
 * 饼图更新动画
 * @param shape 文本图形
 * @param animateCfg
 * @param cfg
 */
export function sectorPathUpdate(shape: IShape, animateCfg: GAnimateCfg, cfg: AnimateExtraCfg) {
  const { toAttrs, coordinate } = cfg;
  const path = (toAttrs as { path: PathCommand[] }).path || [];
  const pathCommands = path.map((command) => command[0]);

  if (path.length < 1) return;

  const { startAngle: curStartAngle, endAngle: curEndAngle, radius, innerRadius } = getArcInfo(path);
  const { startAngle: preStartAngle, endAngle: preEndAngle } = getArcInfo(shape.attr('path'));

  const center = coordinate.getCenter();
  const diffStartAngle = curStartAngle - preStartAngle;
  const diffEndAngle = curEndAngle - preEndAngle;
  // 没有 diff 时直接返回最终 attrs，不需要额外动画
  if (diffStartAngle === 0 && diffEndAngle === 0) {
    shape.attr('path', path);
    return;
  }

  shape.animate(
    (ratio) => {
      const onFrameStartAngle = preStartAngle + ratio * diffStartAngle;
      const onFrameEndAngle = preEndAngle + ratio * diffEndAngle;
      return {
        ...toAttrs,
        path:
          // hack, 兼容 /examples/bar/basic/demo/radial-line.ts 动画
          isEqual(pathCommands, ['M', 'A', 'A', 'Z'])
            ? getArcPath(center.x, center.y, radius, onFrameStartAngle, onFrameEndAngle)
            : getSectorPath(center.x, center.y, radius, onFrameStartAngle, onFrameEndAngle, innerRadius),
      };
    },
    {
      ...animateCfg,
      callback: () => {
        // 将 path 保持原始态，否则会影响 setState() 的动画
        shape.attr('path', path);
      },
    }
  );
}
