/*
 * Copyright 2009, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/**
 * @fileoverview  Manage all the dynamic lights in a level.
 */


var s_light_array = new Array();
var s_number_of_lights_in_scene = 0;

var s_number_of_lights_in_program = 0;
var s_light_parameter_array = new Array();

// Call this before any UpdateLightsInProgram.
function InitializeLightParameters(context, number_of_lights) {
  s_number_of_lights_in_program = number_of_lights;
  for (var l = 0; l < number_of_lights; ++l) {
    s_light_parameter_array[l] = {};
    s_light_parameter_array[l].location = context.createParam(
        'light' + l + '_location', 'ParamFloat3');
    s_light_parameter_array[l].location.value = [0, 0, 0];

    // For the light, the final color parameter is the attentuation.
    s_light_parameter_array[l].color = context.createParam(
        'light' + l + '_color', 'ParamFloat4');
    s_light_parameter_array[l].color.value = [0, 0, 0, 1];
  }
}

// Returns the index of the new light for future manipulation.
// World location and color should be arrays.
// These will be assigned to Param objects.
function AddLight(world_location, color) {
  var light_index = s_number_of_lights_in_scene++;
  s_light_array[light_index] = {};
  s_light_array[light_index].location = world_location;
  s_light_array[light_index].color = color;
  return light_index;
}

// Sets the shader parameters to render using the closest lights
// to the 'focus' (whatever's passed in).
// |focus_location| must provide .x, .y, and .z for determining the
//                  distance to each light.
function UpdateLightsInProgram(focus_location) {
  // TODO: Modulate the lights' color / add behaviors for the lights.
  var lights_to_render = new Array();
  for (var i = 0; i < s_number_of_lights_in_program; ++i) {
    lights_to_render[i] = {};
    lights_to_render[i].distance = 10000000000000.0; // Arbitrary large number.
    lights_to_render[i].index = -1;
  }
  var number_of_lights = s_number_of_lights_in_scene;
  for (var l = 0; l < number_of_lights; ++l) {
    var x_diff = s_light_array[l].location.x - focus_location.x;
    var y_diff = s_light_array[l].location.y - focus_location.y;
    // TODO: This should be focus_location.z, but the avatar's location that is
    // passed in is offset from the world coordinates.  For our relatively flat
    // levels, this works more predictably.
    var z_diff = s_light_array[l].location.z - 0;
    var distance_to_focus = x_diff * x_diff + y_diff * y_diff + z_diff * z_diff;
    // This may change if we assign to an entry early in the list.
    var index_to_assign = l;
    for (var i = 0; i < s_number_of_lights_in_program; ++i) {
      if (distance_to_focus < lights_to_render[i].distance) {
        // Swap the index into this entry.  We will percolate the
        // light up.
        var replaced_index = lights_to_render[i].index;
        lights_to_render[i].index = index_to_assign;
        index_to_assign = replaced_index;

        var replaced_distance = lights_to_render[i].distance;
        lights_to_render[i].distance = distance_to_focus;
        distance_to_focus = replaced_distance;
      }
      if (index_to_assign < 0) {
        break;
      }
    }
  }

  // Finally, assign to the actual shader parameters.
  for (var i = 0; i < s_number_of_lights_in_program; ++i) {
    var light_index = lights_to_render[i].index;
    if (light_index >= 0) {
      s_light_parameter_array[i].location.value =
          s_light_array[light_index].location;
      s_light_parameter_array[i].color.value =
          s_light_array[light_index].color;
    }
  }
}