import * as THREE from "three";
import {
  AxesHelper,
  BoxGeometry,
  Mesh,
  MeshLambertMaterial,
  PerspectiveCamera,
  Vector2,
} from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass";
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";

export default class ThreeHelper {
  width: number = 0;
  height: number = 0;
  group: THREE.Group;
  geometry: THREE.BoxGeometry;
  material: THREE.MeshLambertMaterial;
  mesh: THREE.Mesh;
  camera: THREE.PerspectiveCamera;
  axesHelper: THREE.AxesHelper;
  scene: THREE.Scene;
  light: THREE.PointLight;
  renderer: THREE.WebGLRenderer | null = null;

  constructor() {
    this.group = new THREE.Group();

    this.geometry = new BoxGeometry(60, 90, 80);
    this.material = new MeshLambertMaterial({
      color: 0x009900,
      transparent: true,
      opacity: 0.5,
    });
    this.mesh = new Mesh(this.geometry, this.material);
    this.group.add(this.mesh);

    this.camera = this.creatCamera();
    this.axesHelper = new AxesHelper(400);
    this.light = this.creatLight();

    this.scene = new THREE.Scene();
    this.scene.add(this.group);
    this.scene.add(this.axesHelper);
    this.scene.add(this.light);
  }

  creatLight = () => {
    const light = new THREE.PointLight(0xffffff, 1.0);
    light.intensity = 300.0; //光照强度
    light.decay = 0.0; //设置光源不随距离衰减
    light.position.set(1000, 1000, 1000);
    return light;
  };

  creatRenderer = (dom: HTMLElement) => {
    this.width = dom.clientWidth;
    this.height = dom.clientHeight;

    const renderer: THREE.WebGLRenderer = new THREE.WebGLRenderer({
      antialias: true,
    });
    renderer.setSize(this.width, this.height);
    renderer.setClearColor(0x000000, 0.4);
    this.renderer = renderer;

    // 改变相机观察目标
    this.camera.aspect = this.width / this.height;
    this.camera.updateProjectionMatrix();

    // 注意相机控件OrbitControls会影响lookAt设置，注意手动设置OrbitControls的目标参数

    const controls = new OrbitControls(this.camera, this.renderer.domElement);
    // controls.target = mesh.position;
    // controls.update();
    controls.addEventListener("change", () => {
      this.renderer!.render(this.scene, this.camera);
    });

    // console.log(this.renderer, dom);

    dom.appendChild(renderer.domElement);

    this.renderer.render(this.scene, this.camera);
  };

  loadFBX = (src: string, finish: () => void) => {
    const loader = new FBXLoader();
    loader.load(
      src,
      (fbx) => {
        console.log("loadFBX", fbx);
        this.group.add(fbx);
        this.renderer!.render(this.scene, this.camera);
        finish();
      },
      (event) => {
        console.log(
          "loadFBX" + (event.loaded / event.total) * 100 + "% loaded"
        );
      },
      (error) => {
        console.log("loadFBX", error);
        finish();
      }
    );
  };

  addOutline = (
    width: number = this.width,
    height: number = this.height,
    model?: THREE.Object3D
  ) => {
    const composer = new EffectComposer(this.renderer!);
    const renderPass = new RenderPass(this.scene, this.camera);
    const outlinePass = new OutlinePass(
      new Vector2(width, height),
      this.scene,
      this.camera
    );
    //模型描边颜色，默认白色
    outlinePass.visibleEdgeColor.set(0xffff00);
    //高亮发光描边厚度
    outlinePass.edgeThickness = 4;
    //高亮描边发光强度
    outlinePass.edgeStrength = 6;
    //模型闪烁频率控制，默认0不闪烁
    outlinePass.pulsePeriod = 2;
    composer.addPass(renderPass);
    composer.addPass(outlinePass);
    // composer.render();

    const that = this;

    this.renderer!.domElement.addEventListener("click", function (event) {
      // .offsetY、.offsetX以canvas画布左上角为坐标原点,单位px
      const px = event.offsetX;
      const py = event.offsetY;
      //屏幕坐标px、py转WebGL标准设备坐标x、y
      //width、height表示canvas画布宽高度
      const x = (px / width) * 2 - 1;
      const y = -(py / height) * 2 + 1;
      //创建一个射线投射器`Raycaster`
      const raycaster = new THREE.Raycaster();
      //.setFromCamera()计算射线投射器`Raycaster`的射线属性.ray
      // 形象点说就是在点击位置创建一条射线，射线穿过的模型代表选中
      raycaster.setFromCamera(new THREE.Vector2(x, y), that.camera);
      //.intersectObjects([mesh1, mesh2, mesh3])对参数中的网格模型对象进行射线交叉计算
      // 未选中对象返回空数组[],选中一个对象，数组1个元素，选中两个对象，数组两个元素
      const toAddModelArray = [];
      if (model) {
        toAddModelArray.push(model);
      }
      const intersects = raycaster.intersectObjects([
        that.mesh,
        ...toAddModelArray,
      ]);
      console.log("射线器返回的对象", intersects);
      // intersects.length大于0说明，说明选中了模型
      if (intersects.length > 0) {
        outlinePass.selectedObjects = [intersects[0].object];
        composer.render();
      }
    });
  };

  resizeRender = (dom: HTMLElement) => {
    this.width = dom.clientWidth;
    this.height = dom.clientHeight;
    this.renderer!.setSize(this.width, this.height);
    this.camera.aspect = this.width / this.height;
    this.camera.updateProjectionMatrix();
    this.renderer!.render(this.scene, this.camera);
  };

  private creatCamera = () => {
    const camera = new PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      3000
    );
    camera.position.set(1000, 1000, 1000);
    camera.lookAt(0, 0, 0);
    return camera;
  };
}
