import * as THREE from 'three';
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
import * as PIXI from 'pixi.js';

interface IXY {
  x: number;
  y: number;
}

interface ITransform {
  offset?: IXY;
  size?: number;
  rotation?: number;
}

function drawLineByShape(graphics: PIXI.Graphics, curve: THREE.Curve<THREE.Vector2>, matrix: THREE.Matrix3) {
  const c = curve.toJSON() as any;
  switch (curve.type) {
  case 'QuadraticBezierCurve': {
    const v0 = new THREE.Vector2().fromArray(c.v0).applyMatrix3(matrix);
    const v1 = new THREE.Vector2().fromArray(c.v1).applyMatrix3(matrix);
    const v2 = new THREE.Vector2().fromArray(c.v2).applyMatrix3(matrix);
    graphics.bezierCurveTo(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y);
    break;
  }
  case 'LineCurve': {
    const v = new THREE.Vector2().fromArray(c.v2).applyMatrix3(matrix);
    graphics.lineTo(v.x, v.y);
    break;
  }
  default:
    break;
  }
}

/**
 * 绘制字体
 * @param {PIXI} graphics graphics
 * @param {string} string 文本
 * @param {ITransform} transform 绘制的 model matrix decompose
 */
export async function drawText(
  graphics: PIXI.Graphics,
  string: string | number,
  transform?: ITransform,
  options?: {
    color?: number;
    fontUrl?: string;
  }
) {
  // generate shapes
  const defaultOps = {
    color: 0x131313,
    fontUrl: ''
  };
  const ops = options || defaultOps;
  const size = transform?.size || 5;
  const font = await new FontLoader().loadAsync(ops.fontUrl || defaultOps.fontUrl);
  const str = string.toString();
  const textShapes = font.generateShapes(str, size);

  // matrix parameter
  const halfH = size / 2;
  const halfW = str.length * 0.7 * halfH;
  const offset = { x: transform?.offset?.x || 0, y: transform?.offset?.y || 0 };
  const rotation = transform?.rotation || 0;

  // compose matrix
  const matrix = new THREE.Matrix3();
  const cos = Math.cos(rotation);
  const sin = Math.sin(rotation);
  matrix.set(
    cos,
    -sin,
    -cos * halfW + sin * halfH + offset.x,
    sin,
    cos,
    -sin * halfW - cos * halfH + offset.y,
    1,
    1,
    1
  );

  // draw
  for (let i = 0; i < textShapes.length; i++) {
    graphics.beginFill(ops.color || defaultOps.color);
    const shape: THREE.Shape = textShapes[i];
    const shapeCurveLength = shape.curves.length;
    const curve = shape.curves[shapeCurveLength - 1];
    const v = new THREE.Vector2().fromArray((curve.toJSON() as any).v2).clone().applyMatrix3(matrix);
    graphics.moveTo(v.x, v.y);
    for (let j = 0; j < shapeCurveLength; j++) {
      drawLineByShape(graphics, shape.curves[j], matrix);
    }

    const holes = shape.holes;
    for (let j = 0; j < holes.length; j++) {
      const hole: THREE.Path = holes[j];
      graphics.beginHole();
      const v2 = hole.currentPoint.applyMatrix3(matrix);
      graphics.moveTo(v2.x, v2.y);
      for (let k = 0; k < hole.curves.length; k++) {
        drawLineByShape(graphics, hole.curves[k], matrix);
      }
      graphics.endHole();
    }
  }
}

// export function computeFlowLayoutPosition(central: boolean, margin?: string) {
//   const [posX, posY] = central ? [0 - this.view.width / 2, 0 - this.view.height / 2] : [0, 0];
//   const [top, right, bottom, left] = margin?.split(',').map(n => parseInt(n, 10)) || [0, 0, 0, 0];
//   const parentNode = this._vNode.parent;
//   if (parentNode && parentNode.instance instanceof Container2d) {
//     this.view.position.x = posX + left;
//     if (parentNode.child === this._vNode) {
//       this.view.position.y = posY + top;
//     }
//     if (this._vNode.sibling) {
//       const siblingMargin = this._vNode.sibling?.props.margin?.split(',').map(n => parseInt(n, 10)) || [0, 0, 0, 0];
//       if (this._vNode.sibling?.instance instanceof Mesh2D) {
//         ((this._vNode.sibling?.instance as any).view as PIXI.Container).position.y = this.view.position.y + this.view.height + bottom + siblingMargin[0];
//       }
//     }
//   }
// }
