import { Object3D, PerspectiveCamera, Vector3 } from 'three';
import { Base } from '../base';
import PoiMarker from './poiMarker';

interface IOptions {
  name?: string;
  collision?: boolean;
}

export default class PoisLayer extends Base {
  MY_NAME = 'PoisLayer';

  private _scale = 4;

  private options: IOptions;
  _group: Object3D;

  constructor({ name = '', collision = false }: IOptions) {
    super();
    this.options = { name, collision };
    this._group = new Object3D();
    this._group.name = name;
  }

  _init() {
    this.map.scene.add(this._group);
  }

  async add(...poiMarker: PoiMarker[]) {
    for (const item of poiMarker) {
      item._inject(this.map, this._scale);
      const marker = await item._create();
      this._group.add(marker);
    }
    this.map.reDraw();
    return this;
  }

  remove(...poiMarker: PoiMarker[]) {
    for (const item of poiMarker) {
      item?.sprite?.geometry?.dispose?.();
      item?.sprite?.material?.dispose?.();
      item?.sprite && this._group.remove(item?.sprite);
    }
    this.map.reDraw();
    return this;
  }

  /**
   * @desc 暴露出更新size方法，使其可以在 cameraControls 控制时候更新
   */
  _updateSize() {
    const sprites = this._group ? this._group.children : [];
    if (sprites.length === 0) return;

    //  是否需要碰撞检测
    if (this.options.collision) {
      for (let i = 0; i < sprites.length; i++) {
        const sprite = sprites[i];
        sprite.visible = true;
      }
    }

    for (let i = 0; i < sprites.length; i++) {
      const compareSprite = sprites[i];
      // @ts-ignore
      const canvas = compareSprite.material.map.image;
      if (canvas) {
        const position = compareSprite.position;
        const scale = this.getPoiScale(position, {
          w: canvas.width,
          h: canvas.height
        });

        // 缩小 4 倍在 retina 屏幕会比较高清
        compareSprite.scale.set(
          scale[0] / this._scale,
          scale[1] / this._scale,
          1.0
        );

        // 是否需要碰撞检测
        if (this.options.collision && compareSprite.visible) {
          for (let j = i + 1; j < sprites.length; j++) {
            const sprite = sprites[j];
            if (this.isMarkerCollision(compareSprite, sprite)) {
              sprite.visible = false;
            }
          }
        }
      }
    }
  }

  private getPoiScale(position: any, poiRect: any) {
    const distance = this.map.camera!.position.distanceTo(position); // 相机和标注点的距离
    const top =
      Math.tan(((this.map.camera!.fov / 2) * Math.PI) / 180) * distance; // camera.fov 相机的拍摄距离
    // var meterPerPixel = (2 * top) / window.innerHeight;
    const meterPerPixel = (2 * top) / this.map.$map!.clientHeight;
    const scaleX = poiRect.w * meterPerPixel;
    const scaleY = poiRect.h * meterPerPixel;
    return [scaleX, scaleY, 1.0];
  }

  /**
   * @desc 检测两个标注sprite是否碰撞
   */
  private isMarkerCollision(compareSprite: any, sprite: any) {
    const comparePosition = this.toScreenPos(
      [
        compareSprite.position.x,
        compareSprite.position.y,
        compareSprite.position.z
      ],
      this.map.camera!
    );
    const spritePosition = this.toScreenPos(
      [sprite.position.x, sprite.position.y, sprite.position.z],
      this.map.camera!
    );

    const image1 = compareSprite.material.map.image;
    const image2 = sprite.material.map.image;
    const w1 = image1.width / this._scale;
    const h1 = image1.height / this._scale;
    const x1 = comparePosition.x - w1 / 2;
    const y1 = comparePosition.y - h1 / 2;

    const w2 = image2 ? image2.width / this._scale : 0;
    const h2 = image2 ? image2.height / this._scale : 0;

    const x2 = spritePosition.x - w2 / 2; // 点2左下角的xy点
    const y2 = spritePosition.y - h2 / 2;
    if (x1 >= x2 && x1 >= x2 + w2) {
      return false;
    } else if (x1 <= x2 && x1 + w1 <= x2) {
      return false;
    } else if (y1 >= y2 && y1 >= y2 + h2) {
      return false;
    } else if (y1 <= y2 && y1 + h1 <= y2) {
      return false;
    } else {
      return true;
    }
  }

  /**
   * @desc three世界坐标转为屏幕坐标
   */
  private toScreenPos(position: number[], camera: PerspectiveCamera) {
    const worldVector = new Vector3(position[0], position[1], position[2]);
    const standardVector = worldVector.project(camera); // 世界坐标转标准设备坐标
    const a = this.map.$map!.clientWidth / 2;
    const b = this.map.$map!.clientHeight / 2;
    const x = Math.round(standardVector.x * a + a); // 标准设备坐标转屏幕坐标
    const y = Math.round(-standardVector.y * b + b); // 标准设备坐标转屏幕坐标
    return {
      x: x,
      y: y
    };
  }
}
