// IES file format: http://lumen.iee.put.poznan.pl/kw/iesna.txt

var IES = function(ies_file) {
  
  var that = {}
    
  var _file = null
  
  
  var split_on_next_line = function(str) {
    var first_i = str.indexOf("\n")
    var first_line = str.substring(0, first_i).split(' ')
    var rest = str.substring(first_i+1)
    return [first_line, rest]
  }
  
  
  var array_last = function(array) {
    var length = array.length
    if(length == 0) return null
    return array[length - 1]
  }
  
  
  var initialize_values = function() {
    that.vertical_angles = []
    that.horizontal_angles = []
    that.watts = 0
    that.candelas = []
  }
  
  
  var parse_ies_file = function(file, params) {
    
    initialize_values()
    
    params = params || {}
    
    if(file) {
      _file = file
      
      _file = _file.replace(/[\r|\n]+/, "\n")
      if(!_file.substring(0, 20).match(/^IESNA:\s*LM-63-/) || !_file.match(/\nTILT=NONE\n/)) {
         throw 'invalid IES file'
      }
      
      // Remove metadata
      _file = _file.split("\nTILT=NONE\n")[1].replace(/^\s+/, '').replace(/\s+$/, '')
    }
    
    
    // Convert to an array of values
    file = _file.split(/\s+/)
    
    // Pull out some juicy data
    that.num_lamps = parseInt(file[0])
    that.lumens_per_lamp = parseFloat(file[1])
    that.candela_multiplier = params.candela_multiplier || parseFloat(file[2])
    var num_vangles = parseInt(file[3])
    var num_hangles = parseInt(file[4])
    that.watts = params.watts || parseFloat(file[12])
    file.splice(0, 13)
    
    that.absolute_photometry = (that.lumens_per_lamp == -1)
    that.total_lumens = that.absolute_photometry ? 0 : (that.num_lamps * that.lumens_per_lamp)
    
    // Convert remaining numbers to floats to get at candela values
    for(var i=0; i < file.length; i++) file[i] = parseFloat(file[i]);
    
    that.vertical_angles = file.splice(0, num_vangles)
    that.horizontal_angles = file.splice(0, num_hangles)
    
    // Multiple all values by the candela multiplier
    var num = file.length
    for(var i=0; i < num; i++) file[i] *= that.candela_multiplier;
    
    // Populate candelas array
    while(file.length > 0) that.candelas.push(file.splice(0, num_vangles));
    
    return true
  }
  
  that.reparse = function(params) {
    parse_ies_file(null, params)
  }
  
  
  that.candelas_at_angle = function(vangle, hangle) {
    
    // Normalize the angles
    vangle %= 360
    hangle %= 360
    
    // If the vertical angle is greater than the maximum angle,
    // then the point is out of range and completely dark
    if(vangle > array_last(that.vertical_angles)) return 0;
    
    // Adjust for horizontal angle symmetry
    if(hangle > array_last(that.horizontal_angles)) {
      var last = array_last(that.horizontal_angles);
      if(last == 0) {
        hangle = 0
      } else {
        hangle = last - (hangle % last)
      }
    }
    
    var vi1 = 0;
    var vi2 = 0;
    var hi1 = 0;
    var hi2 = 0;
    
    // Determine which two vertical angles are our closest neighbors
    for(var i=0; i < that.vertical_angles.length; i++) {
        if(vangle == that.vertical_angles[i]) {
          vi1 = i
          vi2 = i
          break
        } else if(that.vertical_angles[i] >= vangle) {
          vi2 = i
          break
        }
        vi1 = i
    }
    
    // Determine which two horizontal angles are our closest neighbors
    for(var i=0; i < that.horizontal_angles.length; i++) {
        if(hangle == that.horizontal_angles[i]) {
          hi1 = i
          hi2 = i
          break
        } else if(that.horizontal_angles[i] >= hangle) {
          hi2 = i
          break
        }
        hi1 = i
    }
    
    // Interpolate to find the candelas at our point,
    // given the candelas at the four reference points
    var candelas = 0
    var va1 = that.vertical_angles[vi1]
    var va2 = that.vertical_angles[vi2]
    var ha1 = that.horizontal_angles[hi1]
    var ha2 = that.horizontal_angles[hi2]
    
    // FIXME: Hack for same-point case
    if(va1 == va2) {
      va1 += 0.0001
      va2 -= 0.0001
    }
    if(ha1 == ha2) {
      ha1 += 0.0001
      ha2 -= 0.0001
    }
    
    // Convert vangle and hangle based on cosine interpolation
    var mu = (vangle - va1) / (va2 - va1)
    vangle = (va2 + va1 + (va1 - va2) * Math.cos(Math.PI * mu)) / 2
    
    mu = (hangle - ha1) / (ha2 - ha1)
    hangle = (ha2 + ha1 + (ha1 - ha2) * Math.cos(Math.PI * mu)) / 2
    
    // Now perform normal bilinear interpolation
    var denom = (ha2 - ha1) * (va2 - va1)
    candelas += (that.candelas[hi1][vi1] / denom) * (ha2 - hangle) * (va2 - vangle)
    candelas += (that.candelas[hi2][vi1] / denom) * (hangle - ha1) * (va2 - vangle)
    candelas += (that.candelas[hi1][vi2] / denom) * (ha2 - hangle) * (vangle - va1)
    candelas += (that.candelas[hi2][vi2] / denom) * (hangle - ha1) * (vangle - va1)
    
    return candelas
  }
  
  
  // Determine the angle of the maximum candela value
  that.maximum_candelas = function() {
    
    var candelas = 0
    var max = 0
    var vangle = 0
    var hangle = 0
    
    for(hi = 0; hi < that.horizontal_angles.length; hi++) {
      for(vi = 0; vi < that.vertical_angles.length; vi++) {
        candelas = that.candelas[hi][vi]
        if(candelas > max) {
          max = candelas
          vangle = that.vertical_angles[vi]
          hangle = that.horizontal_angles[hi]
        }
      }
    }
        
    return {
      candelas: max,
      vangle: vangle,
      hangle: hangle
    }
  }
  
  
  that.candelas_on_plane = function(height, x, y) {
    var r = Math.sqrt(x*x + y*y)
    var d_squared = height*height + r*r
    
    var vangle = Math.atan(r / height)
    var hangle = (r == 0) ? 0 : Math.abs(Math.atan(x / y))
    
    if(y > 0 && x < 0) hangle = 2*Math.PI - hangle
    else if(y < 0 && x > 0) hangle = Math.PI - hangle
    else if(y < 0 && x < 0) hangle = Math.PI + hangle
    
    return that.candelas_at_angle(57.2957795*vangle, 57.2957795*hangle)
  }
    
  
  that.illuminance_on_plane = function(height, x, y) {
    var r_squared = x*x + y*y
    var d_squared = height*height + r_squared
    
    var vangle = Math.atan(Math.sqrt(r_squared) / height)
    var hangle = (r_squared == 0) ? 0 : Math.abs(Math.atan(x / y))
    
    if(y > 0 && x < 0) hangle = 2*Math.PI - hangle
    else if(y < 0 && x > 0) hangle = Math.PI - hangle
    else if(y < 0 && x < 0) hangle = Math.PI + hangle
    
    return Math.cos(vangle) * that.candelas_at_angle(57.2957795*vangle, 57.2957795*hangle) / d_squared
  }
  
  
  parse_ies_file(ies_file)
  return that
}
