import mapboxgl from "@cgcs2000/mapbox-gl";

import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'

/**
 * 定义GLTF模型提供类
 *
 * @alias ThreeGltfProvider
 * @constructor
 *
 * @param options -GLTF选项
 */
function ThreeGltfProvider(options) {
  this._map = options.map;
  this._url = options.url;
  this._lonLat = options.origin || [];
  this._alt = options.alt || 0;

  this._draco = options.draco || false;

  this._layerId = undefined;

  // 初始化转换参数
  this._initTransform();
}

/**
 * 设置模型原点
 * @param {Number} lon -经度
 * @param {Number} lat -纬度
 * @param {Number} alt -高程
 */
ThreeGltfProvider.prototype.setOrigin = function (lon, lat, alt = 0) {
  // parameters to ensure the model is georeferenced correctly on the map
  this._lonLat = [lon, lat];
  this._alt = alt;

  this._initTransform();
}

/**
 * 创建自定义图层
 *
 * @returns {String} 图层Id
 */
ThreeGltfProvider.prototype.loadLayer = function (id) {
  let modelTransform = this._transform;
  let url = this._url;
  let draco = this._draco;

  let layerId = id || 'model_gltf-default';
  this._layerId = layerId;

  // 移除图层
  this.removeLayer();

  const mouse = new THREE.Vector4(-1000, -1000, 1, 1);
  // configuration of the custom layer for a 3D model per the CustomLayerInterface
  let customLayer = {
    id: layerId,
    type: "custom",
    renderingMode: "3d",
    onAdd: function (map, gl) {
      this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
      this.scene = new THREE.Scene();

      // create two three.js lights to illuminate the model
      var directionalLight = new THREE.DirectionalLight(0xffffff);
      directionalLight.position.set(0, -50, 100).normalize();
      this.scene.add(directionalLight);

      var directionalLight2 = new THREE.DirectionalLight(0xffffff);
      directionalLight2.position.set(0, 50, 100).normalize();
      this.scene.add(directionalLight2);

      // use the three.js GLTF loader to add the 3D model to the three.js scene
      var loader = new GLTFLoader();
      if (draco) {
        var dracoLoader = new DRACOLoader();
        dracoLoader.setDecoderPath('../static/draco/gltf/');//设置解压库文件路径
        dracoLoader.setDecoderConfig({ type: 'js' });

        loader.setDRACOLoader(dracoLoader);
      }
      loader.load(url, function (gltf) {
        gltf.scene.traverse(function (child) {
          if (child.isMesh) {
            child.material.emissive = child.material.color;
            child.material.emissiveMap = child.material.map;
          }
        });
        this.scene.add(gltf.scene);
      }.bind(this)
      );

      this.map = map;

      // use the Mapbox GL JS map canvas for three.js
      this.renderer = new THREE.WebGLRenderer({
        canvas: map.getCanvas(),
        context: gl,
        antialias: true, //设置抗锯齿
        alpha: true,     //背景透明
        logarithmicDepthBuffer: true
      });

      this.renderer.autoClear = false;

      this.raycaster = new THREE.Raycaster();
      // 添加click事件
      this.renderer.domElement.addEventListener('click', (e) => {
        e.preventDefault();
        mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;

        // 计算物体和射线的焦点
        const intersects = this.raycaster.intersectObjects(this.scene.children, true);
        if (intersects.length > 0) {
          //console.log(intersects);
        }
      });
    },

    render: function (gl, matrix) {
      var rotationX = new THREE.Matrix4().makeRotationAxis(
        new THREE.Vector3(1, 0, 0),
        modelTransform.rotateX
      );
      var rotationY = new THREE.Matrix4().makeRotationAxis(
        new THREE.Vector3(0, 1, 0),
        modelTransform.rotateY
      );
      var rotationZ = new THREE.Matrix4().makeRotationAxis(
        new THREE.Vector3(0, 0, 1),
        modelTransform.rotateZ
      );

      var m = new THREE.Matrix4().fromArray(matrix);
      var l = new THREE.Matrix4()
        .makeTranslation(
          modelTransform.translateX,
          modelTransform.translateY,
          modelTransform.translateZ
        )
        .scale(
          new THREE.Vector3(
            modelTransform.scale,
            -modelTransform.scale,
            modelTransform.scale
          )
        )
        .multiply(rotationX)
        .multiply(rotationY)
        .multiply(rotationZ);

      this.camera.projectionMatrix.elements = matrix;
      this.camera.projectionMatrix = m.multiply(l);
      this.renderer.state.reset();

      const freeCamera = this.map.getFreeCameraOptions();
      let cameraPosition = new THREE.Vector4(freeCamera.position.x, freeCamera.position.y, freeCamera.position.z, 1);
      cameraPosition.applyMatrix4(l.invert());

      let direction = mouse.clone().applyMatrix4(this.camera.projectionMatrix.clone().invert());
      direction.divideScalar(direction.w);
      this.raycaster.set(cameraPosition, direction.sub(cameraPosition).normalize());

      this.renderer.render(this.scene, this.camera);
      // 渲染
      this.map.triggerRepaint();
    }
  };

  this._map.addLayer(customLayer, "");

  return layerId;
}

/**
 * 图层是否已加载
 * @returns {Boolean}
 */
ThreeGltfProvider.prototype.hasLayer = function () {
  if (this._layerId && this._map.getLayer(this._layerId)) {
    return true;
  }

  return false;
}

/**
 * 移除图层
 */
ThreeGltfProvider.prototype.removeLayer = function () {
  if (this.hasLayer()) {
    this._map.removeLayer(this._layerId);
  }
}

/**
 * 显示/隐藏图层
 * @param {Boolean} isShow -是否显示
 */
ThreeGltfProvider.prototype.showLayer = function (isShow = true) {
  let visible = isShow ? 'visible' : 'none';

  if (this.hasLayer()) {
    this._map.setLayoutProperty(this._layerId, 'visibility', visible);
  }
}

/**
 * 初始化转换参数
 */
ThreeGltfProvider.prototype._initTransform = function () {
  let modelOrigin = this._lonLat;
  let modelAltitude = this._alt;
  let modelRotate = [Math.PI / 2, 0, 0];
  //let modelRotate = [0, 0, 0];
  //let modelScale = 2.41843220338983e-8; // 模型比例
  var modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat(
    modelOrigin,
    modelAltitude
  );

  // transformation parameters to position, rotate and scale the 3D model onto the map
  let modelTransform = {
    translateX: modelAsMercatorCoordinate.x,
    translateY: modelAsMercatorCoordinate.y,
    translateZ: modelAsMercatorCoordinate.z,
    rotateX: modelRotate[0],
    rotateY: modelRotate[1],
    rotateZ: modelRotate[2],
    /* Since our 3D model is in real world meters, a scale transform needs to be
     * applied since the CustomLayerInterface expects units in MercatorCoordinates.
     */
    scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits(),
  };

  this._transform = modelTransform;
}

export default ThreeGltfProvider;
