import Sphere from '../../mesh/geometry/sphere'
import { vec3 } from 'gl-matrix'
import { subv3, normalv3 } from '../../utility'

class Atmosphere {
  constructor (planet, outerRadius, wavelength = [0.650, 0.570, 0.475]) {
    this.mesh = Sphere.new(outerRadius, 200, 200)
    this.planet = planet
    this.outerRadius = outerRadius
    this.innerRadius = planet.generator.radius
    this.wavelength = wavelength

    this.Kr = 0.0025
    this.Km = 0.0015
    this.ESun = 15.0
    this.g = -0.950
    this.scaleDepth = 0.25
    this.mieScaleDepth = 0.1
  }

  setUniforms (gl, shader, camera, sun) {
    const camHeight = vec3.length(subv3(camera.pos, this.planet.origin))
    const invWavelength = [1 / Math.pow(this.wavelength[0], 4), 1 / Math.pow(this.wavelength[1], 4), 1 / Math.pow(this.wavelength[2], 4)]
    gl.uniform3fv(shader.getUniformLocation(gl, 'v3CameraPosition'), camera.pos)
    gl.uniform3fv(shader.getUniformLocation(gl, 'v3LightPosition'), normalv3(sun.pos))
    gl.uniform3fv(shader.getUniformLocation(gl, 'v3InvWavelength'), invWavelength)
    gl.uniform1f(shader.getUniformLocation(gl, 'fCameraHeight'), camHeight)
    gl.uniform1f(shader.getUniformLocation(gl, 'fCameraHeight2'), camHeight * camHeight)
    gl.uniform1f(shader.getUniformLocation(gl, 'fInnerRadius'), this.innerRadius)
    gl.uniform1f(shader.getUniformLocation(gl, 'fInnerRadius2'), this.innerRadius * this.innerRadius)
    gl.uniform1f(shader.getUniformLocation(gl, 'fOuterRadius'), this.outerRadius)
    gl.uniform1f(shader.getUniformLocation(gl, 'fOuterRadius2'), this.outerRadius * this.outerRadius)
    gl.uniform1f(shader.getUniformLocation(gl, 'fKrESun'), this.Kr * this.ESun)
    gl.uniform1f(shader.getUniformLocation(gl, 'fKmESun'), this.Km * this.ESun)
    gl.uniform1f(shader.getUniformLocation(gl, 'fKr4PI'), this.Kr * 4 * Math.PI)
    gl.uniform1f(shader.getUniformLocation(gl, 'fKm4PI'), this.Km * 4 * Math.PI)
    gl.uniform1f(shader.getUniformLocation(gl, 'fScale'), 1 / (this.outerRadius - this.innerRadius))
    gl.uniform1f(shader.getUniformLocation(gl, 'fScaleDepth'), this.scaleDepth)
    gl.uniform1f(shader.getUniformLocation(gl, 'fScaleOverScaleDepth'), 1 / (this.outerRadius - this.innerRadius) / this.scaleDepth)
    gl.uniform1f(shader.getUniformLocation(gl, 'g'), this.g)
    gl.uniform1f(shader.getUniformLocation(gl, 'g2'), this.g * this.g)
  }

  draw (gl, shader, camera, sun) {
    // Set model transform matrix uniform
    gl.uniformMatrix4fv(shader.getUniformLocation(gl, 'uModelMatrix'), false, this.planet.transform)
    this.setUniforms(gl, shader, camera, sun)

    // Draw the mesh
    gl.enable(gl.BLEND)
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
    gl.cullFace(gl.FRONT)

    this.mesh.prepare(gl, shader)
    this.mesh.draw(gl, shader)
    this.mesh.postdraw(gl, shader)

    gl.disable(gl.BLEND)
    gl.cullFace(gl.BACK)
  }

  setupPlanetShader (gl, shader, camera, sun) {
    this.setUniforms(gl, shader, camera, sun)
  }
}

export default Atmosphere
