import * as THREE from 'three'

export default class SceneInit {
  public scene: THREE.Scene
  private readonly fov: number
  private container: HTMLElement
  private readonly camera: THREE.PerspectiveCamera
  private clock: THREE.Clock
  private renderer: THREE.WebGLRenderer
  private uniforms: any
  private readonly onWindowResize: () => void
  private readonly onDocumentWheel: (e: WheelEvent) => void

  constructor (container: HTMLElement, fov = 36) {
    this.container = container
    this.fov = fov

    this.camera = new THREE.PerspectiveCamera(
      this.fov,
      this.container.clientWidth / this.container.clientHeight,
      1,
      1000,
    )

    this.camera.position.z = 128

    this.clock = new THREE.Clock()
    this.scene = new THREE.Scene()

    this.uniforms = {
      u_time: { type: 'f', value: 1.0 },
      colorB: { type: 'vec3', value: new THREE.Color(0xFFF000) },
      colorA: { type: 'vec3', value: new THREE.Color(0xFFFFFF) },
    }

    this.renderer = new THREE.WebGLRenderer({
      antialias: true,
    })

    this.renderer.setSize(this.container.clientWidth, this.container.clientHeight)
    container.appendChild(this.renderer.domElement)

    const ambientLight = new THREE.AmbientLight(0xFFFFFF, 0.7)
    ambientLight.castShadow = false
    this.scene.add(ambientLight)

    const spotLight = new THREE.SpotLight(0xFFFFFF, 0.55)
    spotLight.castShadow = true
    spotLight.position.set(0, 80, 10)
    this.scene.add(spotLight)

    this.onWindowResize = () => {
      this.camera.aspect = this.container.clientWidth / this.container.clientHeight
      this.camera.updateProjectionMatrix()
      this.renderer.setSize(this.container.clientWidth, this.container.clientHeight)
    }

    this.onDocumentWheel = (event: WheelEvent) => {
      const val = this.camera.position.z + event.deltaY / 100
      this.camera.position.z = Math.min(Math.max(val, 84), 256)
    }

    window.addEventListener('resize', this.onWindowResize, false)
    document.addEventListener('wheel', this.onDocumentWheel, false)
  }

  animate () {
    requestAnimationFrame(this.animate.bind(this))
    this.render()
  }

  render () {
    this.uniforms.u_time.value += this.clock.getDelta()
    this.renderer.render(this.scene, this.camera)
  }

  destroy () {
    window.removeEventListener('resize', this.onWindowResize, false)
    document.removeEventListener('wheel', this.onDocumentWheel, false)
    this.renderer.domElement.remove()
    this.renderer.dispose()
  }
}
