"use strict"

module.exports = createTextSprites

var createBuffer  = require('gl-buffer')
var createVAO     = require('gl-vao')
var vectorizeText = require('vectorize-text')
var createShader  = require('./shaders').text

var globals = window || process.global || {}
var __TEXT_CACHE  = globals.__TEXT_CACHE || {}
globals.__TEXT_CACHE = {}

//Vertex buffer format for text is:
//
/// [x,y,z] = Spatial coordinate
//

var VERTEX_SIZE = 3
var VERTEX_STRIDE = VERTEX_SIZE * 4

function TextSprites(
  gl,
  shader,
  buffer,
  vao) {
  this.gl           = gl
  this.shader       = shader
  this.buffer       = buffer
  this.vao          = vao
  this.tickOffset   =
  this.tickCount    =
  this.labelOffset  =
  this.labelCount   = null
}

var proto = TextSprites.prototype

//Bind textures for rendering
var SHAPE = [0,0]
proto.bind = function(model, view, projection, pixelScale) {
  this.vao.bind()
  this.shader.bind()
  var uniforms = this.shader.uniforms
  uniforms.model = model
  uniforms.view = view
  uniforms.projection = projection
  uniforms.pixelScale = pixelScale
  SHAPE[0] = this.gl.drawingBufferWidth
  SHAPE[1] = this.gl.drawingBufferHeight
  this.shader.uniforms.resolution = SHAPE
}

proto.unbind = function() {
  this.vao.unbind()
}

proto.update = function(bounds, labels, labelFont, ticks, tickFont) {
  var gl = this.gl
  var data = []

  function addItem(t, text, font, size) {
    var fontcache = __TEXT_CACHE[font]
    if(!fontcache) {
      fontcache = __TEXT_CACHE[font] = {}
    }
    var mesh = fontcache[text]
    if(!mesh) {
      mesh = fontcache[text] = tryVectorizeText(text, {
        triangles: true,
        font: font,
        textAlign: 'center',
        textBaseline: 'middle'
      })
    }
    var scale = (size || 12) / 12
    var positions = mesh.positions
    var cells = mesh.cells
    var lo = [ Infinity, Infinity]
    var hi = [-Infinity,-Infinity]
    for(var i=0, nc=cells.length; i<nc; ++i) {
      var c = cells[i]
      for(var j=2; j>=0; --j) {
        var p = positions[c[j]]
        data.push(scale*p[0], -scale*p[1], t)
      }
    }
  }

  //Generate sprites for all 3 axes, store data in texture atlases
  var tickOffset  = [0,0,0]
  var tickCount   = [0,0,0]
  var labelOffset = [0,0,0]
  var labelCount  = [0,0,0]
  for(var d=0; d<3; ++d) {

    //Generate label
    labelOffset[d] = (data.length/VERTEX_SIZE)|0
    addItem(0.5*(bounds[0][d]+bounds[1][d]), labels[d], labelFont)
    labelCount[d] = ((data.length/VERTEX_SIZE)|0) - labelOffset[d]

    //Generate sprites for tick marks
    tickOffset[d] = (data.length/VERTEX_SIZE)|0
    for(var i=0; i<ticks[d].length; ++i) {
      if(!ticks[d][i].text) {
        continue
      }
      addItem(
        ticks[d][i].x,
        ticks[d][i].text,
        ticks[d][i].font || tickFont,
        ticks[d][i].fontSize || 12)
    }
    tickCount[d] = ((data.length/VERTEX_SIZE)|0) - tickOffset[d]
  }

  this.buffer.update(data)
  this.tickOffset = tickOffset
  this.tickCount = tickCount
  this.labelOffset = labelOffset
  this.labelCount = labelCount
}

//Draws the tick marks for an axis
var AXIS = [0,0,0]
proto.drawTicks = function(d, scale, angle, offset, color) {
  if(!this.tickCount[d]) {
    return
  }

  var v = AXIS
  v[0] = v[1] = v[2] = 0
  v[d] = 1
  this.shader.uniforms.axis = v
  this.shader.uniforms.color = color
  this.shader.uniforms.angle = angle
  this.shader.uniforms.scale = scale
  this.shader.uniforms.offset = offset
  this.vao.draw(this.gl.TRIANGLES, this.tickCount[d], this.tickOffset[d])
}

//Draws the text label for an axis
var ZERO = [0,0,0]
proto.drawLabel = function(d, scale, angle, offset, color) {
  if(!this.labelCount[d]) {
    return
  }
  this.shader.uniforms.axis = ZERO
  this.shader.uniforms.color = color
  this.shader.uniforms.angle = angle
  this.shader.uniforms.scale = scale
  this.shader.uniforms.offset = offset
  this.vao.draw(this.gl.TRIANGLES, this.labelCount[d], this.labelOffset[d])
}

//Releases all resources attached to this object
proto.dispose = function() {
  this.shader.dispose()
  this.vao.dispose()
  this.buffer.dispose()
}

function tryVectorizeText(text, options) {
  try {
    return vectorizeText(text, options)
  } catch(e) {
    console.warn('error vectorizing text:', e)
    return {
      cells: [],
      positions: []
    }
  }
}

function createTextSprites(
    gl,
    bounds,
    labels,
    labelFont,
    ticks,
    tickFont) {

  var buffer = createBuffer(gl)
  var vao = createVAO(gl, [
    { "buffer": buffer,
      "size": 3
    }
  ])

  var shader = createShader(gl)
  shader.attributes.position.location = 0

  var result = new TextSprites(
    gl,
    shader,
    buffer,
    vao)

  result.update(bounds, labels, labelFont, ticks, tickFont)

  return result
}
