import {
  Camera,
  DoubleSide,
  LinearFilter,
  Material,
  Mesh,
  MeshBasicMaterial,
  PlaneGeometry,
  TextureLoader,
  Vector3,
  sRGBEncoding,
} from "three-platformize";
import { TextureAnimator, TextureAnimatorParams } from "./TextureAnimator";

export interface ReticleModelParams
  extends Omit<TextureAnimatorParams, "texture" | "numberOfTiles"> {
  name?: string;
  url: string;
  planeWidth: number;
  scale: number;
  position: Vector3;
  camera: Camera;
}

/**
 * 创建光照焦点
 */
export class ReticleModel {
  name = "";

  textureAnimator: TextureAnimator;
  reticleAnim: Mesh;
  camera: Camera;

  constructor(params: ReticleModelParams) {
    const {
      name,
      url,
      planeWidth,
      scale,
      camera,
      position,
      ...textureAnimatorParams
    } = params;
    const texture = new TextureLoader().load(url);

    texture.magFilter = LinearFilter;
    texture.minFilter = LinearFilter;
    texture.encoding = sRGBEncoding;

    this.textureAnimator = new TextureAnimator({
      ...textureAnimatorParams,
      texture,
      numberOfTiles: Math.max(
        textureAnimatorParams.tilesHorizontal,
        textureAnimatorParams.tilesVertical
      ),
    });

    const planeGeometry = new PlaneGeometry(planeWidth, planeWidth, 1, 1);
    const meshBasicMaterial = new MeshBasicMaterial({
      map: texture,
      side: DoubleSide,
      transparent: true,
      opacity: 1,
      depthTest: false,
    });

    this.reticleAnim = new Mesh(planeGeometry, meshBasicMaterial);
    this.reticleAnim.lookAt(camera.position);
    this.reticleAnim.name = name ?? "";
    this.reticleAnim.userData.depthlevel = 1;
    this.reticleAnim.scale.copy(new Vector3(scale, scale, scale));
    this.reticleAnim.visible = true;
    this.reticleAnim.position.copy(position);
    this.camera = camera;
  }

  private hideTime: NodeJS.Timeout;
  private showTime: NodeJS.Timeout;

  show() {
    this.reticleAnim.visible = true;

    this.clearTime();

    this.showTime = setInterval(() => {
      const material = this.reticleAnim.material as Material;
      material.opacity += 0.02;

      if (material.opacity >= 1) {
        material.opacity = 1;
        clearInterval(this.showTime);
      }
    }, 20);
  }

  hide() {
    this.clearTime();

    this.hideTime = setInterval(() => {
      const material = this.reticleAnim.material as Material;
      material.opacity -= 0.05;

      if (material.opacity <= 0) {
        material.opacity = 0;
        this.reticleAnim.visible = false;
        clearInterval(this.hideTime);
      }
    }, 20);
  }

  /**
   * 更新
   * @param time 秒
   */
  update(time: number) {
    this.reticleAnim.lookAt(this.camera.position);
    this.textureAnimator.update(time * 2000);
  }

  clearTime() {
    this.hideTime && clearInterval(this.hideTime);
    this.showTime && clearInterval(this.showTime);
  }
}
