import { Mesh } from '../../mesh'
import { mat4 } from 'gl-matrix'
import { subv3, mulv3, addv3, normalv3, crossv3 } from '../../utility'

class CubeFace {
  constructor (parent, level, pos, normal, resolution, radius, generator) {
    this.parent = parent
    this.children = []

    this.position = pos
    this.normal = normal
    this.resolution = resolution
    this.radius = radius

    this.level = 0

    this.generated = false

    // Calculate left and forward vectors from the normal
    this.left = [normal[1], normal[2], normal[0]]
    this.forward = crossv3(normal, this.left)

    this.transform = mat4.create()
    mat4.fromTranslation(this.transform, this.position)

    this.generate()
  }

  generate () {
    if (this.generated) return

    let cpoint = subv3(this.position, mulv3(this.left, this.radius / 2))
    cpoint = subv3(cpoint, mulv3(this.forward, this.radius / 2))

    let VERTICES = this.resolution
    let count = VERTICES * VERTICES
    let vertices = new Array(count * 3)
    // let normals = new Array(count * 3)
    let textureCoords = new Array(count * 2)
    let indices = new Array(6 * (VERTICES - 1) * (VERTICES - 1))

    for (let i = 0, vertexPointer = 0; i < VERTICES; i++) {
      for (let j = 0; j < VERTICES; j++, vertexPointer++) {
        let isize = i / VERTICES * this.radius
        let jsize = j / VERTICES * this.radius

        // From the left and forward vectors, we can calculate an oriented vertex
        let iv = mulv3(this.left, isize)
        let jv = mulv3(this.forward, jsize)

        // Add the scaled left and forward to the centered origin
        let vertex = addv3(cpoint.slice(0), addv3(iv, jv))

        // Normalize and multiply by radius to create a spherical mesh
        let pos = mulv3(normalv3(vertex), this.radius)

        vertices[vertexPointer * 3] = pos[0]
        vertices[vertexPointer * 3 + 1] = pos[1]
        vertices[vertexPointer * 3 + 2] = pos[2]
        // normals[vertexPointer * 3] = normal[0]
        // normals[vertexPointer * 3 + 1] = normal[1]
        // normals[vertexPointer * 3 + 2] = normal[2]
        textureCoords[vertexPointer * 2] = j / (VERTICES - 1)
        textureCoords[vertexPointer * 2 + 1] = i / (VERTICES - 1)
      }
    }

    for (let gz = 0, pointer = 0; gz < VERTICES - 1; gz++) {
      for (let gx = 0; gx < VERTICES - 1; gx++) {
        let topLeft = (gz * VERTICES) + gx
        let topRight = topLeft + 1
        let bottomLeft = ((gz + 1) * VERTICES) + gx
        let bottomRight = bottomLeft + 1
        indices[pointer++] = topLeft
        indices[pointer++] = bottomLeft
        indices[pointer++] = topRight
        indices[pointer++] = topRight
        indices[pointer++] = bottomLeft
        indices[pointer++] = bottomRight
      }
    }

    this.mesh = Mesh.construct(window.gl, vertices, indices, textureCoords)
    this.generated = true
  }

  draw (gl, shader) {
    // Set model transform matrix uniform
    const transformLocation = shader.getUniformLocation(gl, 'uModelMatrix')
    gl.uniformMatrix4fv(transformLocation, false, this.transform)

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

class CubePlanet {
  constructor (origin, resolution, radius, generator) {
    this.origin = origin
    this.resolution = resolution
    this.radius = radius

    let hs = resolution / 2

    this.faces = [
      new CubeFace(this, 0, [0, 0, -hs], [0, 0, -1], resolution, radius, generator), // front
      // new CubeFace(this, 0, [0, 0, hs], [0, 0, 1], resolution, radius, generator), // back

      new CubeFace(this, 0, [-hs, 0, 0], [-1, 0, 0], resolution, radius, generator), // left
      // new CubeFace(this, 0, [hs, 0, 0], [1, 0, 0], resolution, radius, generator), // right

      // new CubeFace(this, 0, [0, hs, 0], [0, 1, 0], resolution, radius, generator), // top
      // new CubeFace(this, 0, [0, -hs, 0], [0, -1, 0], resolution, radius, generator) // bottom
    ]
  }

  draw (gl, shader) {
    for (let i in this.faces) {
      this.faces[i].draw(gl, shader)
    }
  }
}

export { CubePlanet, CubeFace }
