/*
 * list.js
 *
 * Provides the main functionality for the list.html page. Knows which items
 * are present because of items.js, which is updated by the update.py script.
 */

/*
 * Set up an array containing indices of default fields:
 */
default_indexes = new Array();
for (i = 0; i < default_fields.length; ++i) {
  for (index = 0; index < fields.length; ++index) {
    if (fields[index] == default_fields[i]) {
      default_indexes[default_indexes.length] = index;
      break;
    }
  }
}

/*
 * Handles initial setup of the javascript functionality.
 */
function setup() {
  fillTable(default_fields, default_dirs);
  criterion = "Rating";
  criterection = false;
  list = document.getElementById("list");
  list.style.display = "table";
  help_button = document.getElementById("help-button");
  help_button.setAttribute("onclick", "show_help(); return false;");
  hide_help();
  dontshow = false;
}

/*
 * Reveals the help box and sets the help button to hide it.
 */
function show_help() {
  help_button = document.getElementById("help-button");
  help_button.innerHTML = 'x';
  help_button.setAttribute("onclick", "hide_help(); return false;");
  help = document.getElementById("help-text");
  help.style.display = "block";
}

/*
 * Hides the help box and sets the help button to show it.
 */
function hide_help() {
  help_button = document.getElementById("help-button");
  help_button.innerHTML = '?';
  help_button.setAttribute("onclick", "show_help(); return false;");
  help = document.getElementById("help-text");
  help.style.display = "none";
}

/*
 * Re-sorts the table, destroying the old table and setting up a new one in its
 * place. Uses the field, if given, as an additional (primary) sorting
 * criterion.
 */
function resort(field) {
  dir = true;
  for (i = 0; i < num_fields.length; ++i) {
    if (field == num_fields[i]) {
      dir = false;
      break;
    }
  }
  if (field == criterion) {
    dir = !criterection;
  }
  criterion = field;
  criterection = dir;
  fs = new Array(field);
  ds = new Array(dir);
  fillTable(fs.concat(default_fields), ds.concat(default_dirs));
}

/*
 * Takes an array of field names to sort by and fills in the html table element
 * using the table_fields and sorted by the given criterion. Also takes sorting
 * directions: true for normal sorting, false for reverse sorting.
 */
function fillTable(sortby, dirs) {
  indices = new Array();
  for (i = 0; i < sortby.length; ++i) {
    indices[indices.length] = lookupIndex(sortby[i]);
  }
  things = items.sort(function(a, b) {
                        return compareByFields(indices, dirs, a, b);
                      });
  table = document.getElementById("list");
  table.style.backgroundColor = farColor(gradient, 0.1, 0.15);
  table.style.borderColor = farColor(gradient, 0, 0.15);
  caption = document.getElementById("list-caption");
  caption.style.backgroundColor = nearColor(gradient, 0.5, 0.15);
  caption_text = document.getElementById("list-caption-text");
  caption_text.innerHTML = title;
  help = document.getElementById("help-text");
  help.style.backgroundColor = nearColor(gradient, 0.7, 0.15);
  headrow = document.getElementById("list-headrow");
  foot = document.getElementById("list-foot");
  body = document.getElementById("list-body");
  while (headrow.childNodes.length) {
    headrow.removeChild(headrow.childNodes[0]);
  }
  for (i = 0; i < table_fields.length; ++i) {
    heading = document.createElement("th");
    heading.className = "list-heading";
    heading.innerHTML = '<img id="sortimage_' + table_fields[i] +
                        '" class="sortimage"></img>' +
                        '&nbsp<a href="#">' + table_fields[i] + '</a>';
    heading.setAttribute("onclick",
                         "resort(table_fields[" + i + "]); return false;");
    heading.title = "Sort by " + table_fields[i];
    tint = 0.15 * ((i + 1) % 2);
    heading.style.backgroundColor = nearColor(gradient, tint, 0.1);
    headrow.appendChild(heading);
    found = false;
    for (j = 0; j < sortby.length; ++j) {
      if (sortby[j] == table_fields[i]) {
        found = true;
        direction = dirs[j];
        for (k = 0; k < num_fields.length; ++k) {
          if (table_fields[i] == num_fields[k]) {
            direction = !direction;
          }
        }
        break;
      }
    }
    if (found) {
      sortimg = document.getElementById("sortimage_" + table_fields[i]);
      sortimg.style.display = "inline";
      if (direction) {
        sortimg.title = "Sorted";
        sortimg.setAttribute("src", "bits/sorted-down.png");
        sortimg.setAttribute("alt", ">");
      } else {
        sortimg.title = "Sorted - Reverse";
        sortimg.setAttribute("src", "bits/sorted-up.png");
        sortimg.setAttribute("alt", "<");
      }
    }
  }
  while (body.childNodes.length) {
    body.removeChild(body.childNodes[0]);
  }
  for (i = 0; i < things.length; ++i) {
    row = document.createElement("tr");
    row.className = "list-row";
    row.id = "list-row-" + i;
    row.setAttribute("onclick", "showDetail(" + i + "); return true;");
    body.appendChild(row);
    for (j = 0; j < table_fields.length; ++j) {
      cell = row.insertCell(row.cells.length);
      cell.className = "list-cell col" + j;
      cell.innerHTML = things[i][lookupIndex(table_fields[j])];
      for (k = 0; k < cell.childNodes.length; ++k) {
        if (cell.childNodes[k].nodeName == "A") {
          cell.childNodes[k].setAttribute("onclick",
                                          "dontshow = true; return true;");
        }
      }
      cell.title = table_fields[j];
      tint = 0.2 * (i % 2) + (0.06 * j) +
             0.05 * Math.sin(i*Math.PI/16.0);
      cell.style.backgroundColor = nearColor(gradient, tint, 0.1);
    }
    detail = document.createElement("tr");
    detail.className = "list-detail";
    detail.style.display = "none";
    detail.id = "list-detail-" + i;
    body.appendChild(detail);
    detailcell = detail.insertCell(0);
    detailcell.colSpan = table_fields.length;
    detailcell.className = "detail-cell";
    tint = 0.2 * (i % 2) + 0.05 * Math.sin(i*Math.PI/16.0);
    detailcell.style.backgroundColor = nearColor(gradient, tint, 0.1);
    detailcell.innerHTML = "";
    for (j = 0; j < detail_fields.length; ++j) {
      key = detail_fields[j];
      value = things[i][lookupIndex(detail_fields[j])];
      detailcell.innerHTML += "<div>" + key + ": " + value + "</div>\n";
    }
  }
}

function showDetail(index) {
  if (!dontshow) {
    document.getElementById("list-detail-" + index).style.display = "table-row";
    document.getElementById("list-row-" + index).setAttribute("onclick",
                               "hideDetail(" + index + "); return true;");
  }
  dontshow = false;
}

function hideDetail(index) {
  if (!dontshow) {
    document.getElementById("list-detail-" + index).style.display = "none";
    document.getElementById("list-row-" + index).setAttribute("onclick",
                               "showDetail(" + index + "); return true;");
  }
  dontshow = false;
}

/*
 * Looks up the index corresponding to the given field name. Uses the default
 * field's index if it can't find the given field name.
 */
function lookupIndex(field_name) {
  for (index = 0; index < fields.length; ++index) {
    if (fields[index] == field_name) {
      break;
    }
  }
  if (index == fields.length) {
    return default_indexes[0];
  } else {
    return index;
  }
}

/*
 * Takes two Array objects and a list of indices within them and compares them
 * by their contents at the given indices.
 */
function compareByFields(indices, directions, a, b) {
  if (indices.length == 0) {
    return 0;
  }
  rep1 = a[indices[0]];
  rep2 = b[indices[0]];
  for (i = 0; i < num_fields.length; ++i) {
    if (num_fields[i] == fields[indices[0]]) {
      rep1 = parseInt(rep1);
      rep2 = parseInt(rep2);
      break;
    }
  }
  if (!directions[0]) {
    rep3 = rep2;
    rep2 = rep1;
    rep1 = rep3;
  }
  if (rep1 < rep2) {
    return -1;
  } else if (rep1 > rep2) {
    return 1;
  } else {
    fs = new Array();
    for (i = 1; i < indices.length; ++i) {
      fs[fs.length] = indices[i];
    }
    ds = new Array();
    for (i = 1; i < directions.length; ++i) {
      ds[ds.length] = directions[i];
    }
    return compareByFields(fs, ds, a, b);
  }
}

/*
 * Returns a random color relatively near along the given gradient. The offset
 * pushes the color farther out, and should be between 0 and 1. The range
 * determines the breadth of possibilities. offest + range should not exceed 1.
 */
function nearColor(gradient, offset, range) {
  fraction = offset + Math.random() * range;
  d = new Array(gradient[1][0] - gradient[0][0],
                gradient[1][1] - gradient[0][1],
                gradient[1][2] - gradient[0][2]);
  result = new Array(Math.floor(gradient[0][0] + d[0]*fraction),
                     Math.floor(gradient[0][1] + d[1]*fraction),
                     Math.floor(gradient[0][2] + d[2]*fraction));
  strs = new Array(result[0].toString(16),
                   result[1].toString(16),
                   result[2].toString(16));
  for (index = 0; index < strs.length; ++index) {
    if (strs[index].length < 2) {
      strs[index] = "0" + strs[index];
    }
  }
  return '#' + strs.join('');
}

/*
 * Returns a random color relatively far along the given gradient. The offset
 * pulls the color farther in, and should be between 0 and 1. The range
 * determines the breadth of possibilities. offset + range should not exceed 1.
 */
function farColor(gradient, offset, range) {
  fraction = 1.0 - offset - Math.random() * range;
  d = new Array(gradient[1][0] - gradient[0][0],
                gradient[1][1] - gradient[0][1],
                gradient[1][2] - gradient[0][2]);
  result = new Array(Math.round(gradient[0][0] + d[0]*fraction),
                     Math.round(gradient[0][1] + d[1]*fraction),
                     Math.round(gradient[0][2] + d[2]*fraction));
  strs = new Array(result[0].toString(16),
                   result[1].toString(16),
                   result[2].toString(16));
  for (index = 0; index < strs.length; ++index) {
    if (strs[index].length < 2) {
      strs[index] = "0" + strs[index];
    }
  }
  return '#' + strs.join('');
}
