import * as THREE from 'three';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
// 导入轨道控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

import gsap from 'gsap';

import ResourceTracker from '/@/utils/threejs/ResourceTracker';
const resourceTracker = new ResourceTracker()
const track = resourceTracker.track.bind(resourceTracker)


class UseThree {

  container: HTMLCanvasElement;
  camera: THREE.PerspectiveCamera | null = null;
  scene: THREE.Scene | null = null;
  renderer: THREE.WebGLRenderer | null = null;
  orbitControls: OrbitControls | null = null;
  giftLoader: THREE.Object3D | null = null;
  animationMixer: THREE.AnimationMixer | null = null;
  animationAction: THREE.AnimationAction | null = null;
  clock: THREE.Clock = new THREE.Clock(); // 计时器
  timeoutId: NodeJS.Timeout | null = null;
  animationId: number = 0

  constructor(selector) {
    this.animationId = 0
    this.container = document.querySelector(selector);
    //初始化
    this.init();
    this.animate();
    window.addEventListener('resize', this.resizeRenderer.bind(this));
    // 添加滚动事件，鼠标滚动模型执行动画
    // window.addEventListener('wheel', this.wheelRenderer.bind(this));
  }
  init() {

    // 初始化场景
    this.initScene();
    // 初始化环境光
    this.initLight();
    // 初始化相机
    this.initCamera();
    //初始化渲染器
    this.initRenderer();
    // 初始化控制器
    this.initControles();
  }

  initScene() {
    this.scene = new THREE.Scene();
    const axesHelper = new THREE.AxesHelper(5);
    // this.scene.add(axesHelper);
  }
  initLight() {
    const light = new THREE.AmbientLight(0xfafafa, 1);
    light.position.set(0, 200, 200);
    (this.scene as THREE.Scene).add(light);
  }
  initCamera() {
    this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
    this.camera.position.set(0, 0.2, 0.3);
  }
  initRenderer() {

    this.renderer = new THREE.WebGLRenderer({ antialias: true });
    // 设置屏幕像素比
    this.renderer.setPixelRatio(window.devicePixelRatio);
    // 设置渲染的尺寸
    this.renderer.setSize(window.innerWidth, window.innerHeight);
    // 色调映射
    this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
    // 曝光程度
    this.renderer.toneMappingExposure = 3;
    this.container.appendChild(this.renderer.domElement);
  }

  initControles() {
    this.orbitControls = new OrbitControls(this.camera as THREE.Camera, this.renderer?.domElement);
  }

  test() {
    //
    const planeGeometry = new THREE.PlaneGeometry(1, 1);
    const meshStandardMaterial = new THREE.MeshStandardMaterial();
    const mesh = new THREE.Mesh(planeGeometry, meshStandardMaterial);
    mesh.position.set(0, -0.2, 0);
    mesh.rotation.z = Math.PI / 4;
    this.scene?.add(mesh);
  }

  setModel(modalName) {
    const a = new Date().getTime() / 1000
    return new Promise(async (resolve, reject) => {
      try {
        const db =  window['CustomDB']
        const modalArr =  await db.modal.where('modalName').equals(modalName).toArray()
        if(modalArr.length > 0) {
          const modalValue = modalArr[0].modalVal
          new THREE.ObjectLoader().parse(modalValue, (e) => {
            const group = e.children[0]

            const cityMaterial = new THREE.MeshBasicMaterial({
              color: new THREE.Color(0x0c016f),
            });
            group?.traverse((item: THREE.Mesh) => {
              if (item.type === 'Mesh') {
                item.material = cityMaterial;
                this.setMaterial(item);
              }
            });
            this.scene?.add(e.children[0]);
            console.log((new Date().getTime() / 1000) - a + " s")
            resolve(this.scene);
          })
        } 
      } catch (error) {
        reject('加载模型出错')
      }
      
    })
  }

  setMaterial(obj) {

    obj.geometry.computeBoundingBox();
    const { max, min } = obj.geometry.boundingBox;
    const distance = max.y - min.y + 2;

    obj.material.onBeforeCompile = (shader) => {
      shader.uniforms.uDistance = {
        value: distance,
      };
      shader.uniforms.uTopColor = {
        value: new THREE.Color(0xaaaeff),
      };
      this.addGrad(shader);
      this.addRadiusSpread(shader);
      this.addLineSpread(shader);
      this.addToSpread(shader);
    };
  }
  addGrad(shader) {
    shader.vertexShader = shader.vertexShader.replace(
      '#include <common>',
      `

      #include <common>
      varying vec3 vPosition;
    `
    );
    shader.vertexShader = shader.vertexShader.replace(
      '#include <fog_vertex>',
      `

      #include <fog_vertex>
      vPosition = position;
     `
    );
    shader.fragmentShader = shader.fragmentShader.replace(
      '#include <common>',
      `
      #include <common>
      uniform vec3 uTopColor;
      uniform float uDistance;
      varying vec3 vPosition;
  
      `
    );

    shader.fragmentShader = shader.fragmentShader.replace(
      '#include <dithering_fragment>',
      `
      #include <dithering_fragment>

      vec4 distGradColor = gl_FragColor;
      // 设置混合的百分比
      float gradMix = (vPosition.y + (uDistance / 2.0)) / uDistance / 5.0;
      // 设置混合颜色
      vec3 colorMix = mix(distGradColor.xyz, uTopColor, gradMix);
      gl_FragColor = vec4(colorMix, 1.0);
      //#end#
      `
    );
  }

  addRadiusSpread(shader) {
    // 设置扩散中心店
    shader.uniforms.uRadiusSpreadCenter = {
      value: new THREE.Vector2(0, 0),
    };
    // 扩散时间
    shader.uniforms.uRadiusSpreadTime = {
      value: 0,
    };

    shader.uniforms.uRadiusSpreadWidth = {
      value: 20,
    };
    shader.fragmentShader = shader.fragmentShader.replace(
      '#include <common>',
      `
      #include <common>
      uniform vec2 uRadiusSpreadCenter;
      uniform float uRadiusSpreadTime;
      uniform float uRadiusSpreadWidth;

      `
    );

    shader.fragmentShader = shader.fragmentShader.replace(
      '//#end#',
      `
      float spreadRadius = distance(vPosition.xz + vec2(-720, -550), uRadiusSpreadCenter);
      // 扩散范围
      float spreadIndex = -(spreadRadius - uRadiusSpreadTime) * (spreadRadius - uRadiusSpreadTime ) + uRadiusSpreadWidth;
      if(spreadIndex > 0.0){
        gl_FragColor = mix(gl_FragColor, vec4(1.0, 1.0, 1.0, 1.0), spreadIndex / uRadiusSpreadWidth );
      }
      //#end#
      `
    );
    gsap.to(shader.uniforms.uRadiusSpreadTime, {
      value: 250,
      duration: 1,
      ease: 'none',
      repeat: -1,
    });
  }

  addLineSpread(shader) {
    // 设置扩散中心店
    shader.uniforms.uLineSpreadCenter = {
      value: new THREE.Vector2(0, 0),
    };
    // 扩散时间
    shader.uniforms.uLineSpreadTime = {
      value: 0,
    };

    shader.uniforms.uLineSpreadWidth = {
      value: 20,
    };
    shader.fragmentShader = shader.fragmentShader.replace(
      '#include <common>',
      `
      #include <common>
      uniform vec2 uLineSpreadCenter;
      uniform float uLineSpreadTime;
      uniform float uLineSpreadWidth;

      `
    );

    shader.fragmentShader = shader.fragmentShader.replace(
      '//#end#',
      `
      float spreadLine = vPosition.x - 400.0;
      // 扩散范围
      float spreadLineIndex = -(spreadLine - uLineSpreadTime * 5.0) * (spreadLine - uLineSpreadTime * 5.0) + uLineSpreadWidth;
      if(spreadLineIndex > 0.0){
        gl_FragColor = mix(gl_FragColor, vec4(1.0, 1.0, 1.0, 1.0), spreadLineIndex / uLineSpreadWidth );
      }
      //#end#
      `
    );
    gsap.to(shader.uniforms.uLineSpreadTime, {
      value: 150,
      duration: 2,
      ease: 'none',
      repeat: -1,
    });
  }

  addToSpread(shader) {
    // 设置扩散中心店
    shader.uniforms.uTopSpreadCenter = {
      value: new THREE.Vector2(0, 0),
    };
    // 扩散时间
    shader.uniforms.uTopSpreadTime = {
      value: 0,
    };

    shader.uniforms.uTopSpreadWidth = {
      value: 20,
    };
    shader.fragmentShader = shader.fragmentShader.replace(
      '#include <common>',
      `
      #include <common>
      uniform vec2 uTopSpreadCenter;
      uniform float uTopSpreadTime;
      uniform float uTopSpreadWidth;

      `
    );

    shader.fragmentShader = shader.fragmentShader.replace(
      '//#end#',
      `
      float spreadTopLine = vPosition.y * 2.0;
      // 扩散范围uTopSpreadWidth
      float spreadTopIndex = -(spreadTopLine - uTopSpreadTime) * (spreadTopLine - uTopSpreadTime) + uTopSpreadWidth;
      if(spreadTopIndex > 0.0){
        gl_FragColor = mix(gl_FragColor, vec4(0.0, 1.0, 1.0, 1.0), spreadTopIndex / uTopSpreadWidth / uTopSpreadTime * 2.0);
      }
      //#end#
      `
    );
    gsap.to(shader.uniforms.uTopSpreadTime, {
      value: 200,
      duration: 5,
      ease: 'none',
      repeat: -1,
    });
  }

  addLine(geometry) {
    const edges = new THREE.EdgesGeometry(geometry);
    const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xff0000 }));
    (this.scene as THREE.Scene).add(line);
    // console.log(line);
  }

  /* 场景环境背景 */
  setEnvMap(hdr) {
    new RGBELoader().setPath('/public/3D/hdr/').load(hdr + '.hdr', (texture) => {
      texture.mapping = THREE.EquirectangularReflectionMapping;
      (this.scene as THREE.Scene).background = texture;
      (this.scene as THREE.Scene).environment = texture;
    });
  }

  render() {
    const delta = this.clock.getDelta();
    this.renderer?.render(this.scene as THREE.Object3D, this.camera as THREE.Camera);
    this.animationMixer?.update(delta);
  }

  animate() {
    // this.renderer?.setAnimationLoop(this.render.bind(this));
    if(this.animationId != -1) {
      this.render()
      this.animationId = requestAnimationFrame(this.animate.bind(this))
    }
  }
  resizeRenderer() {
    // 更新相机比例
    (this.camera as THREE.PerspectiveCamera).aspect = window.innerWidth / window.innerHeight;
    // 刷新相机矩阵
    this.camera?.updateProjectionMatrix();
    // 设置场景尺寸
    this.renderer?.setSize(window.innerWidth, window.innerHeight);
  }
  wheelRenderer(e: WheelEvent) {
    const timeScale = e.deltaY > 0 ? 1 : -1;
    this.animationAction?.setEffectiveTimeScale(timeScale);
    (this.animationAction as THREE.AnimationAction).paused = false;
    this.animationAction?.play();
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
    }
    this.timeoutId = setTimeout(() => {
      this.animationAction?.halt(0.5);
    }, 500);
  }
  deleteModal() {
    this.scene?.children.forEach((obj:any) => {
      if(obj.type === 'Group'){
        obj.traverse(function(item:any) {
          if (item.type === 'Mesh') {
            item.geometry.dispose();
            item.material.dispose();
            !!item.clear&&item.clear();
          }
        })
      }else if (obj.material) {
        obj.material.dispose();
      }else if (obj.geometry) {
        obj.geometry.dispose();
      }
      this.scene?.remove(obj)
      !!obj.clear??obj.clear()
      obj = null;
    })
    let gl = this.renderer?.domElement.getContext("webgl");
    gl && gl?.getExtension("WEBGL_lose_context")?.loseContext();

    this.renderer?.forceContextLoss();
    this.renderer?.dispose();
    this.camera = null;
    this.orbitControls = null;
    this.renderer.domElement = null;
    this.renderer = null;

    // css3dRender.domElement = null;
    // css3dRender = null;
    // model3D.innerHTML = '';
    // css3D.innerHTML = '';
    // model3D = null
    // css3D = null

    !!this.scene?.clear??this.scene?.clear();
    cancelAnimationFrame(this.animationId);
    this.animationId = -1
    // this.stats = null
    // scene = null

    THREE.Cache.clear();  
    console.log('3D环境已清理干净');
  }


}

export default UseThree;
