import { Mesh2D } from '@turbox3d/renderer-pixi';
import { SceneEvent, ViewEntity } from '@turbox3d/event-manager';
import * as PIXI from 'pixi.js';
import { Vec2 } from '@turbox3d/shared';
import { drawText } from '../_utils/utils';

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

interface IDimensionData {
  bbox: IXY[]; // 包围盒
  innerVX: number[]; // 竖直构件端点
  innerHY: number[]; // 水平构件端点
}

interface IDimensionProps {
  data: IDimensionData[];
  editableTextPs?: IXY[]; // 需要隐藏文字的位置
  clickCallback?: Function;
  rotation?: number;
  scale?: Vec2;
}

interface ILineData {
  p0: IXY;
  p1: IXY;
}

interface IText {
  roundLength: number;
  params: {
    offset: IXY;
    size: number;
    rotation: number;
  };
  endPoints: ILineData;
}

const DIM_OFFSET = 80;
const DIM_INTERNAL = 80;
const textH = 40;
const textOffsetK = 1;// 标注垂直于文字方向偏移系数,分母为文字尺寸

/**
 * 根据两个端点生成尺寸线数据
 */
export function generateDimData(x0: number, y0: number, x1: number, y1: number) {
  // for (let i = 0; i < 100; i++) {
  const endLineL = 25;
  const endLine2L = 35;

  // compute line data
  const v = { x: x1 - x0, y: y1 - y0 };
  const vL = Math.sqrt(v.x * v.x + v.y * v.y);
  v.x /= vL;
  v.y /= vL;

  // anticlockwise 90 vector
  const v1 = { x: -v.y, y: v.x };
  // anticlockwise 45 vector
  const v2 = { x: 0.707 * v.x - 0.707 * v.y, y: 0.707 * v.x + 0.707 * v.y };

  const data = [
    // main body
    { x: x0, y: y0 }, { x: x1, y: y1 },

    // end line
    { x: x0 + endLineL * v1.x, y: y0 + endLineL * v1.y }, { x: x0 - endLineL * v1.x, y: y0 - endLineL * v1.y },
    { x: x1 + endLineL * v1.x, y: y1 + endLineL * v1.y }, { x: x1 - endLineL * v1.x, y: y1 - endLineL * v1.y },

    // 45° end line
    // { x: x0 + endLine2L * v2.x, y: y0 + endLine2L * v2.y },
    // { x: x0 - endLine2L * v2.x, y: y0 - endLine2L * v2.y },
    // { x: x1 + endLine2L * v2.x, y: y1 + endLine2L * v2.y },
    // { x: x1 - endLine2L * v2.x, y: y1 - endLine2L * v2.y },
  ];

  const length = vL;

  const angle = Math.atan2(v.y, v.x);

  return { data, length, angle };
}

export default class Dimension extends Mesh2D<IDimensionProps> {
  protected view = new PIXI.Container();
  private _interactContainer = new PIXI.Container();
  // public componentWillReceiveProps(para1,para2){
  //   console.warn(para1,para2)
  // }
  private graphic2endPsMap: Map<PIXI.Graphics, IText> = new Map();
  public draw() {
    this.view.removeChildren();
    const graphics = new PIXI.Graphics();
    this.view.addChild(graphics);
    this._interactContainer.removeChildren();
    this.view.addChild(this._interactContainer);

    // processedData 数据格式 { p0: { x: 1300, y: 2250 }, p1: { x: 2700, y: 2250 } },
    const textInfo: IText[] = [];
    const processedData = this.processData();

    graphics.lineStyle(1, 0x131313);
    graphics.line.native = true;
    for (let i = 0; i < processedData.length; i++) {
      const { data, angle, length } =
        generateDimData(processedData[i].p0.x, processedData[i].p0.y, processedData[i].p1.x, processedData[i].p1.y);

      const roundLength = Math.round(length);
      if (roundLength === 0) {
        // eslint-disable-next-line no-continue
        continue;
      }

      for (let j = 0; j < data.length; j += 2) {
        graphics.moveTo(data[j].x, data[j].y);
        graphics.lineTo(data[j + 1].x, data[j + 1].y);
      }

      const center = {
        x: (processedData[i].p0.x + processedData[i].p1.x) / 2,
        y: (processedData[i].p0.y + processedData[i].p1.y) / 2,
      };
      const textOffsetDir = { x: -Math.sin(angle), y: Math.cos(angle) };
      center.x += textOffsetK * textH * textOffsetDir.x;
      center.y += textOffsetK * textH * textOffsetDir.y;
      textInfo.push({ roundLength, params: { offset: { x: center.x, y: center.y }, size: textH, rotation: angle }, endPoints: { p0: processedData[i].p0, p1: processedData[i].p1 } });
    }

    // delete text data
    if (this.props.editableTextPs) {
      this.props.editableTextPs.forEach((p) => {
        let nearestD2 = Infinity;
        let nearestIndex = -1;
        textInfo.forEach((info, i) => {
          const tempD2 = (p.x - info.params.offset.x) ** 2 + (p.y - info.params.offset.y) ** 2;
          if (tempD2 < nearestD2) {
            nearestD2 = tempD2;
            nearestIndex = i;
          }
        });
        textInfo.splice(nearestIndex, 1); // delete nearest text
      });
    }

    // draw text
    graphics.lineStyle(0);
    textInfo.forEach(info => {
      drawText(graphics, info.roundLength, info.params);

      const interactGraphics = new PIXI.Graphics();
      interactGraphics.lineStyle(0);
      interactGraphics.beginFill(0x00ff00, 0.0001);
      const halfH = info.params.size / 2; const halfW = info.roundLength.toString().length * 0.8 * halfH;
      interactGraphics.drawRect(-halfW, -halfH, 2 * halfW, 2 * halfH);
      interactGraphics.position.set(info.params.offset.x, info.params.offset.y);
      interactGraphics.endFill();
      interactGraphics.rotation = info.params.rotation;
      this._interactContainer.addChild(interactGraphics);
      this.graphic2endPsMap.set(interactGraphics, info);
    });

    this.view.rotation = this.props.rotation ?? 0;
    this.view.scale.set(this.props.scale?.x ?? 1, this.props.scale?.y ?? 1);
  }
  protected onClickable() {
    return true;
  }
  protected onClick = (v: Partial<ViewEntity>, e: SceneEvent<any>) => {
    let targetG = this._interactContainer.children[0];
    this._interactContainer.children.forEach(c => {
      if (this._distance2(c.position, e.getScenePosition()) < this._distance2(targetG.position, e.getScenePosition())) targetG = c;
    });
    this.props.clickCallback?.(this.graphic2endPsMap.get(targetG as PIXI.Graphics));
  };

  private _distance2(p0: PIXI.IPointData, p1: PIXI.IPointData) {
    return (p0.x - p1.x) ** 2 + (p0.y - p1.y) ** 2;
  }
  /**
   * @description: 矩形交错网格构件上获取标注端点坐标数组
   *
   *       3-------------A------------2
   *       |             |            |
   *       |             |            |
   *       |             |            |
   *       C-------------B------------D
   *       |                          |
   *       |                          |
   *       |                          |
   *       0--------------------------1
   *
   *      bbox是整个构件的包围盒四个点
   *      AB是内插的竖直构件，上方的标注需要体现其水平X位置，
   *      CD是内插的水平构件，右方标注需要体现其竖直Y位置
   */
  private processData() {
    let lineData: ILineData[] = [
      // { p0: { x: 1300, y: 2250 }, p1: { x: 2700, y: 2250 } },
    ];

    // 遍历窗
    this.props.data.forEach((data) => {
      const oneBlockLineData: ILineData[] = [];

      const { bbox, innerHY, innerVX } = data;

      // 计算最外围标注
      // 有内插标注则则最外围标注有额外偏移
      const interHK = innerVX.length ? 1 : 0;
      const interVK = innerHY.length ? 1 : 0;
      // 上方横
      oneBlockLineData.push({
        p0: { x: bbox[3].x, y: bbox[3].y + DIM_OFFSET + interHK * DIM_INTERNAL },
        p1: { x: bbox[2].x, y: bbox[3].y + DIM_OFFSET + interHK * DIM_INTERNAL },
      });
      // 右侧竖
      oneBlockLineData.push({
        p0: { x: bbox[1].x + DIM_OFFSET + interVK * DIM_INTERNAL, y: bbox[2].y },
        p1: { x: bbox[1].x + DIM_OFFSET + interVK * DIM_INTERNAL, y: bbox[1].y },
      });

      // 竖直构件x排序
      if (innerVX.length) {
        let PX: number[] = [bbox[3].x, bbox[2].x];
        PX = PX.concat(innerVX);
        PX.sort((a, b) => a - b);
        for (let i = 0; i < PX.length - 1; i++) {
          oneBlockLineData.push({
            p0: { x: PX[i], y: bbox[2].y + DIM_OFFSET },
            p1: { x: PX[i + 1], y: bbox[2].y + DIM_OFFSET },
          });
        }
      }

      // 水平构件y排序
      if (innerHY.length) {
        let PY: number[] = [bbox[2].y, bbox[1].y];
        PY = PY.concat(innerHY);
        PY.sort((a, b) => b - a);
        for (let i = 0; i < PY.length - 1; i++) {
          oneBlockLineData.push({
            p0: { x: bbox[1].x + DIM_OFFSET, y: PY[i] },
            p1: { x: bbox[1].x + DIM_OFFSET, y: PY[i + 1] },
          });
        }
      }
      lineData = lineData.concat(oneBlockLineData);
    });

    return lineData;
  }
}
