/**
 * Create the meal object.
 */
var meal = {
  /**
   * Timeout for auto search.
   */
  autoSearch: null,
  
  /**
   * Dietary Reference Values.
   */
  DRV: {
    calcium: 1000,
    carbohydrates: 300,
    cholesterol: 300,
    dietaryFiber: 25,
    fat: 65,
    iron: 18,
    protein: 50,
    saturatedFat: 20,
    sodium: 2400,
    vitaminA: 5000,
    vitaminC: 60
  },
  
  /**
   * Object containg current state.
   */
  viewState: {
    category: null,
    establishment: null,
    mealItems: {},
    mealNutrition: {
      calcium: 0,
      calories: 0,
      caloriesFromFat: 0,
      carbohydrates: 0,
      cholesterol: 0,
      dietaryFiber: 0,
      fat: 0,
      iron: 0,
      protein: 0,
      saturatedFat: 0,
      servingSize: 0,
      sodium: 0,
      sugars: 0,
      transFat: 0,
      vitaminA: 0,
      vitaminC: 0
    },
    search: ''
  }
};


/**
 * Add action listeners for the page.
 * @function
 */
meal.addActionListeners = function()
{
  $(window).resize(meal.resizeColumns);
  $('#search').keyup(meal.doAutoSearch);
};


/**
 * Add action listeners for the establishments.
 * #function
 */
meal.addEstablishmentsActionListeners = function() {
  // Add action listeners for the establishments.
  $('#establishments li > a, #establishments li > span').click(function(event) {
    event.stopPropagation();
    event.preventDefault();
    if($(this).text() == meal.viewState.establishment) {
      $('#categories').toggle();
    } else {
      meal.viewState.establishment = $(this).text();
      meal.loadCategories();
    }
    return false;
  });
};


/**
 * Add the item's data where applicable.
 * @function
 * @param {Object} item The item received from the server.
 */
meal.addItemData = function(item) {
  var itemID = meal.getItemID(item.establishment, item.category, item.name);
  meal.viewState.mealItems[itemID] = item;
  meal.viewState.mealItems[itemID].quantity = 1;
  meal.addOrSubtractItemNutritionToMealNutrition(itemID, 1);
  meal.updateGUI();
};


/**
 * Add the given item to the meal and display the total nutritional value.
 * @function
 * @param {String} item The item we want to add.
 */
meal.addItemToMeal = function(item) {
  meal.makeAjaxCall(
    {
      category: meal.viewState.category,
      cmd: 'addItem',
      establishment: meal.viewState.establishment,
      item: item
    },
    meal.addItemData
  );
  pageTracker._trackEvent('items', 'add', meal.getItemID(meal.viewState.establishment, meal.viewState.category, item), 1);
};


/**
 * Add the given item's nutrition values to the meal's nutrition values.
 * @function
 * @param {String} itemID The item ID who's nutrition values we want to add to the meal's nutrition value.
 * @param {Integer} aos Add or subtract the nutrition values from the total.  To add set aos = 1; to subtract set aos = -1.
 */
meal.addOrSubtractItemNutritionToMealNutrition = function(itemID, aos) {
  var total = meal.viewState.mealNutrition;
  var item = meal.viewState.mealItems[itemID];
  if(item) {
    total.calcium += item.calcium * aos;
    total.calories += item.calories * aos;
    total.caloriesFromFat += item.caloriesFromFat * aos;
    total.carbohydrates += item.carbohydrates * aos;
    total.cholesterol += item.cholesterol * aos;
    total.dietaryFiber += item.dietaryFiber * aos;
    total.fat += item.fat * aos;
    total.iron += item.iron * aos;
    total.protein += item.protein * aos;
    total.saturatedFat += item.saturatedFat * aos;
    total.servingSize += item.servingSize * aos;
    total.sodium += item.sodium * aos;
    total.sugars += item.sugars * aos;
    total.transFat += item.transFat * aos;
    total.vitaminA += item.vitaminA * aos;
    total.vitaminC += item.vitaminC * aos;
  }
};


/**
 * Set the search value and wait 500ms to call the search function.
 * @function
 */
meal.doAutoSearch = function() {
  meal.viewState.search = this.value;
  if(meal.autoSearch) {
    clearTimeout(meal.autoSearch);
  }
  meal.autoSearch = setTimeout('meal.doSearch()', 500);
};


/**
 * Perform a search by reloading the list of establishemnts with the search value set.
 * @function
 */
meal.doSearch = function() {
  meal.viewState.establishment = null;
  meal.viewState.category = null;
  meal.loadEstablishments();
};


/**
 * Generate a unique ID for an item with the specified establishment, category and name.
 * @function
 * @param {String} establishment The item's establishment.
 * @param {String} category The item's category.
 * @param {String} establishment The item's name.
 * @return {String} The unique ID for the item provided.
 */
meal.getItemID = function(establishment, category, name) {
  var sb = new Array();
  sb.push(establishment);
  sb.push(' ');
  sb.push(category);
  sb.push(' ');
  sb.push(name);
  return sb.join('');
};


/**
 * Initialize the components of the page.
 * @function
 */
meal.initialize = function() {
  meal.resizeColumns();
  meal.addActionListeners();
  meal.loadImages();
  meal.addEstablishmentsActionListeners();
};


/**
 * Load the categories for the selected establishment.
 * @function
 */
meal.loadCategories = function() {
  meal.makeAjaxCall(
    {
      cmd: 'getCategories',
      establishment: meal.viewState.establishment,
      search: meal.viewState.search
    },
    meal.showCategories
  );
  pageTracker._trackEvent('categories', 'load', meal.viewState.establishment);
};


/**
 * Load the list of establishments
 * @function
 */
meal.loadEstablishments = function() {
  meal.makeAjaxCall(
    {
      cmd: 'getEstablishments',
      search: meal.viewState.search
    },
    meal.showEstablishments
  );
};


/**
 * Load any images that will be dynamically shown and cache them.
 */
meal.loadImages = function() {
  $.cacheImage('./images/iconAdd.png');
  $.cacheImage('./images/iconSubtract.png');
};


/**
 * Load the items for the selected establishment and category.
 * @function
 */
meal.loadItems = function() {
  meal.makeAjaxCall(
    {
      category: meal.viewState.category,
      cmd: 'getItems',
      establishment: meal.viewState.establishment,
      search: meal.viewState.search
    },
    meal.showItems
  );
  pageTracker._trackEvent('items', 'load', meal.viewState.establishment + ' ' + meal.viewState.category);
};


/**
 * Make AJAX call to the server with the provided data and call the callback function on success.
 * @function
 * @param {Object} data The data to call the server with.
 * @param {Function} callback The function to call when the server returns success.
 */
meal.makeAjaxCall = function(data, callback) {
  $.ajax({
    data: data,
    dataType: 'json',
    success: callback,
    type: 'POST',
    url: './includes/runCmd.php'
  });
};


/**
 * Remove the item with the given id from the meal.
 * @function
 * @param {String} itemID The id of the item to be removed.
 */
meal.removeItem = function(itemID)
{
  itemID = unescape(itemID);
  meal.addOrSubtractItemNutritionToMealNutrition(itemID, -1);
  meal.viewState.mealItems[itemID].quantity--;
  if(meal.viewState.mealItems[itemID].quantity == 0)
  {
   delete meal.viewState.mealItems[itemID];
  }
  meal.updateGUI();
  pageTracker._trackEvent('items', 'remove', itemID);
}


/**
 * Resize the establishments column to take full advantage of the window's height.
 * @function
 */
meal.resizeColumns = function()
{
  var $establishments = $('#establishments');
  
  $establishments.height('').height($establishments.parent().height() - $establishments.siblings().outerHeight());
};


/**
 * Show the list of categories for the selected establishment.
 * @function
 * @param {Object} categories The list of categories.
 */
meal.showCategories = function(categories) {
  // Create the list of categories.
  var sb = new Array();
  sb.push('<ul id="categories">');
  for(var i = 0; i < categories.length; i++) {
    sb.push('<li><span>');
    sb.push(categories[i]);
    sb.push('</span></li>');
  }
  sb.push('</ul>');
  $('#categories').remove();
  $('#establishments li').filter(function() {
    return $(this).text() == meal.viewState.establishment;
  }).append(sb.join(''));
  
  // Add action listeners for the categories.
  $('#categories li > span').click(function(event) {
    event.stopPropagation();
    if($(this).text() == meal.viewState.category) {
      $('#items').toggle();
    } else {
      meal.viewState.category = $(this).text();
      meal.loadItems();
    }
  });
  
  // Automatically load the items if we only have on category.
  if(categories.length == 1) {
    meal.viewState.category = categories[0];
    meal.loadItems();
  }
};


/**
 * Show the list of establishments.
 * @function
 * @param {Object} establishments The list of establishments.
 */
meal.showEstablishments = function(establishments) {
  // Create the list of establishments.
  var sb = new Array();
  sb.push('<ul>');
  for(var i = 0; i < establishments.length; i++) {
    sb.push('<li><span>');
    sb.push(establishments[i]);
    sb.push('</span></li>');
  }
  sb.push('</ul>');
  $('#establishments').html(sb.join(''));
  meal.addEstablishmentsActionListeners();
  
  // Automatically load the categories if we only have one establishment.
  if(establishments.length == 1) {
    meal.viewState.establishment = establishments[0];
    meal.loadCategories();
  }
};


/**
 * Show the list of items for the selected establishment and category.
 * @function
 * @param {Object} items The list of items.
 */
meal.showItems = function(items) {
  var sb = new Array();
  sb.push('<ul id="items">');
  for(var i = 0; i < items.length; i++) {
    sb.push('<li><span class="itemName">');
    sb.push(items[i]);
    sb.push('</span> <img alt="" class="itemAdd" height="11" src="./images/iconAdd.png" width="17"></li>');
  }
  sb.push('</ul>');
  $('#items').remove();
  $('#categories li').filter(function() {
    return $(this).text() == meal.viewState.category;
  }).append(sb.join(''));
  $('#items li > img.itemAdd').click(function(event) {
    event.stopPropagation();
    var itemName = $(this).siblings('.itemName').text();
    var itemID = meal.getItemID(meal.viewState.establishment, meal.viewState.category, itemName);
    if(meal.viewState.mealItems[itemID]) {
      meal.viewState.mealItems[itemID].quantity++;
      meal.addOrSubtractItemNutritionToMealNutrition(itemID, 1);
      meal.updateGUI();
      pageTracker._trackEvent('items', 'add', itemID, 0);
    } else {
      meal.addItemToMeal(itemName);
    }
  });
};


/**
 * Sort the meal items by name.
 * @function
 * @param {Array} a An array to compare.
 * @param {Array} b An array to compare.
 * @return The sorted array.
 */
meal.sortItemsByName = function(a, b) {
  return a[0] > b[0];
}


/**
 * Update the page contents.
 * @function
 */
meal.updateGUI = function() {
  meal.updateMealItems();
  meal.updateMealNutritionFacts();
};


/**
 * Update the meal's items.
 * @function
 */
meal.updateMealItems = function() {
  var items = new Array();
  for(var i in meal.viewState.mealItems) {
    items.push([meal.viewState.mealItems[i].name, meal.viewState.mealItems[i].quantity, i]);
  }
  items.sort(meal.sortItemsByName);
  var sb = new Array();
  for(var i = 0; i < items.length; i++) {
    sb.push('<span');
    if(i >= items.length - 3) {
      sb.push(' style="color: gray;"');
    }
    sb.push('>');
    sb.push(items[i][0]);
    if(items[i][1] > 1) {
      sb.push(' X ');
      sb.push(items[i][1]);
    }
    sb.push('</span>');
    sb.push(' <img alt="" class="itemSubtract" height="11" onclick="meal.removeItem(\'');
    sb.push(escape(items[i][2]));
    sb.push('\')" src="./images/iconSubtract.png" width="17"><br>');
  }
  $('#meal').html(sb.join(''));
};


/**
 * Update the meal's nutrition facts,
 * @function
 */
meal.updateMealNutritionFacts = function() {
  var total = meal.viewState.mealNutrition;
  $('#servingSize').text((total.servingSize < 2) ? Math.round(total.servingSize*10)/10 : Math.round(total.servingSize));
  $('#calories').text((total.calories > 50) ? Math.round(total.calories/10)*10 : Math.round(total.calories/5)*5);
  $('#caloriesFromFat').text((total.caloriesFromFat > 50) ? Math.round(total.caloriesFromFat/10)*10 : Math.round(total.caloriesFromFat/5)*5);
  $('#fat').text((total.fat < 5) ? Math.round(total.fat*2)/2 : Math.round(total.fat));
  $('#fatDV').text(Math.round(total.fat/meal.DRV.fat*100));
  $('#saturatedFat').text((total.saturatedFat < 5) ? Math.round(total.saturatedFat*2)/2 : Math.round(total.saturatedFat));
  $('#saturatedFatDV').text(Math.round(total.saturatedFat/meal.DRV.saturatedFat*100));
  $('#transFat').text((total.transFat < 5) ? Math.round(total.transFat*2)/2 : Math.round(total.transFat));
  $('#cholesterol').text((total.cholesterol > 5) ? Math.round(total.cholesterol/5)*5 : (total.cholesterol > 2) ? 'less than 5' : 0);
  $('#cholesterolDV').text(Math.round(total.cholesterol/meal.DRV.cholesterol*100));
  $('#sodium').text((total.sodium > 140) ? Math.round(total.sodium/10)*10 : Math.round(total.sodium/5)*5);
  $('#sodiumDV').text(Math.round(total.sodium/meal.DRV.sodium*100));
  $('#carbohydrates').text((total.carbohydrates >= 1) ? Math.round(total.carbohydrates) : (total.carbohydrates >= .5) ? 'less than 1' : 0);
  $('#carbohydratesDV').text(Math.round(total.carbohydrates/meal.DRV.carbohydrates*100));
  $('#dietaryFiber').text((total.dietaryFiber >= 1) ? Math.round(total.dietaryFiber) : (total.dietaryFiber >= .5) ? 'less than 1' : 0);
  $('#dietaryFiberDV').text(Math.round(total.dietaryFiber/meal.DRV.dietaryFiber*100));
  $('#sugars').text((total.sugars >= 1) ? Math.round(total.sugars) : (total.sugars >= .5) ? 'less than 1' : 0);
  $('#protein').text((total.protein >= 1) ? Math.round(total.protein) : (total.protein >= .5) ? 'less than 1' : 0);
  $('#vitaminADV').text(Math.round(total.vitaminA/meal.DRV.vitaminA*100));
  $('#vitaminCDV').text(Math.round(total.vitaminC/meal.DRV.vitaminC*100));
  $('#calciumDV').text(Math.round(total.calcium/meal.DRV.calcium*100));
  $('#ironDV').text(Math.round(total.iron/meal.DRV.iron*100));
};


/**
 * Initialize the scripts on page load.
 */
$(document).ready(meal.initialize);