// code shared between FoodFinder and other pages that contain search panels
// a page using it must define its own updateHash() function which is called by each search control
// when a user changes some values. updateHash() should call serialize() (see below) to convert the state of controls
// into a search parameter object.

var nutritionControlToAttribute = {
  "#calories" : "ENERC_KCAL",
  "#fat" : "FAT",
  "#cholesterol" : "CHOLE",
  "#carbs" : "CHOCDF"
};

var searchBoxHint = "FoodFinder";

// gathers the form data from the different search elements
function serialize() {
  var output = {};

  // #search-keywords is created by _keywordPanel.gsp
  var keywordField = $('#search-keywords');

  // #q is the search box in the 
  if (keywordField.length == 0)
    keywordField = $('#q');
  
  keywordField.each(function (index, elt) {
    if (elt && $(elt).is(':visible') && $(elt).val() != $(elt).attr('placeholder')) {
      output['q'] = $(elt).val();
    } else {
      output['q'] = '';
    }
  });

  checkAndAddPrefillHash(output);

  $([
    {selector: '#flavorSweet', sliderValue: 'flavor.sweet'},
    {selector: '#flavorSalty', sliderValue: 'flavor.salty'},
    {selector: '#flavorSour', sliderValue: 'flavor.sour'},
    {selector: '#flavorSavory', sliderValue: 'flavor.meaty'},
    {selector: '#flavorBitter', sliderValue: 'flavor.bitter'}
  ]).each(function (index, elt) {
    // WARNING the :visible > elt.selector is a hack that assumes a certain implementation of the sliders,
    // namely that the (possibly hidden) select element with the desired id is an *immediate* child
    // of an non-hidden ui element.
    // we can use the visibility of the non-hidden parent element as an indicator of whether the taste panel is open or closed
    var val = $(':visible > ' + elt.selector).val();

    if (val) {
      var vals = val.split(',');
      // ensure if slider is in middle, it doesn't get passed into hash
      output = setSliderValue(elt.sliderValue, vals, output);
    }
  });

  // handle courses
  var courseArr = [];
  var allChecked = true;
  $(':visible > .allowedCourse').each(function(index) {
    if ($(this).is(':checked')) {
      courseArr.push($(this).attr('value')+"|"+$(this).attr('name'));
    } else {
      allChecked = false;
    }
  });
  if (!allChecked) {
    output['allowedCourse'] = courseArr;
  } else {
    // If all the course checkboxes are checked, we should not add any course parameters for search.
    output['allowedCourse'] = [];
  }
  
  for (control in nutritionControlToAttribute) {
    var attr = nutritionControlToAttribute[control];
    var maxNutrValue = 1000000.0;
    var ctl = $(':visible > ' + control);
    if (! ctl.val() || ctl.val() == maxNutrValue) {
      delete output['nutrition.' + attr + '.min'];
      delete output['nutrition.' + attr + '.max'];
      delete output['start'];
    }
    else {
      output['nutrition.' + attr + '.min'] = 0;
      output['nutrition.' + attr + '.max'] = $(control).val() || maxNutrValue;
    }
  }
  
  // handle diets
  var dietArr = [];
  $('.allowedDiet:visible').each(function(index) {
    if ($(this).is(':checked')) {
      dietArr.push($(this).attr('value')+"|"+$(this).attr('name'));
    }
  });
  output['allowedDiet'] = dietArr;
  
  // handle allergies
  var allergyArr = [];
  $('.allowedAllergy:visible').each(function(index) {
    if($(this).is(':checked')) {
      allergyArr.push($(this).attr('value')+"|"+$(this).attr('name'));
    }
  });
  output['allowedAllergy'] = allergyArr;

  if ($(':visible > #time').val()) {
    var duration = $('#time').val().split(',');
    var maxDuration = 9999;

    if (duration[1] == maxDuration) {
      delete output['maxTotalTimeInSeconds'];
      delete output['start'];
    }
    else {
      output['maxTotalTimeInSeconds'] = duration[1]*60;
    }
  }

  if ($(':visible > #price').val()) {
    var pricing = $('#price').val().split(',');
    var maxPrice = 99;
    
    
    if (pricing[1] == 99) {
      delete output['price.min'];
      delete output['price.max'];
      delete output['start'];
    }
    else {
      output['price.min'] = pricing[0];
      output['price.max'] = pricing[1];
    }
  }
  
  if ($(':visible > #source-favorites').is(':checked')) {
    output['restrictToMine'] = true;
  }
  else {
    delete output['restrictToMine'];
    delete output['start'];
  }

  // handle sources
  var sourceArr = [];
  var allChecked = true;
  $(':visible > .allowedSource').each(function(index) {
    if ($(this).is(':checked')) {
      sourceArr.push($(this).val()+"|"+$(this).val());
    } else {
      allChecked = false;
    }
  });
  if (!allChecked) {
    output['allowedSource'] = sourceArr;
  } else {
    // If all the source checkboxes are checked, we should not add any source parameters for search.
    output['allowedSource'] = [];
  }

  return output;
}

function setSliderValue(sliderHashName, sliderArray, output) {
  var minSliderValue = 0;
  var maxSliderValue = 6;

  if (sliderArray[0] == minSliderValue && sliderArray[1] == maxSliderValue) {
    delete output[sliderHashName+'.min'];
    delete output[sliderHashName+'.max'];
    delete output['start'];
  }
  else {
    output[sliderHashName+'.min'] = sliderArray[0];
    output[sliderHashName+'.max'] = sliderArray[1];
  }

  return output;
}

function cleanseSliderValue(currValue) {
  currValue = currValue.replace('+', '');
  currValue = currValue.replace('(average)', '');
  currValue = currValue.replace(/^\s+|\s+$/g,"");
  return parseInt(currValue);
}

function translateSliderValue(currValue) {
  if (currValue == 0) {
    currValue = '0 (average)';
  }
  else if (currValue > 0) {
    currValue = "+" + currValue.toString();
  }
  return currValue.toString();
}

// Undo the serialize() things by only passing the searchVal to the server.
function undoSerialize(data) {
  var output;
  if (isString(data)) {
    output = data.split("|")[1];
  }
  else if (data != undefined) {
    output = [];
    $.each(data, function(i, o) {
      if (typeof o != 'undefined' && o.split("|")[1] != 'undefined') {
        output[i] = o.split("|")[1];
      }
      else {
        output[i] = '';
      }
    });
  }
  // some last chance sanity checking
  if (output == 'undefined' || output == 'null') {
    output = '';
  }
  return output;
}

function checkAndAddPrefillHash(output) {
  $('input.as-values').each(function(i, obj) {
    var bucketType = $(obj).attr('id').replace('as-values-','');
    var bucketValues = $(obj).val();
    if (bucketType == 'ingredients-include') {
      output['allowedIngredient'] = getKeyAndValues(bucketValues);
    }
    else if (bucketType == 'ingredients-exclude') {
      output['excludedIngredient'] = getKeyAndValues(bucketValues);
    }
    else if (bucketType == 'cuisines') {
      output['allowedCuisine'] = getKeyAndValuesFromBucket(bucketValues, 'cuisine', 'searchValue', 'description', 'searchValue');
    }
  });

}

function getKeyAndValues(bucketValues) {
  var output = [];

  var bucketArray = bucketValues.split(',');
  $.each(bucketArray, function(i, item) {
    if (item != '') {
      output.push(item+"|"+item);
    }
  });

  return output;
}

function getKeyAndValuesFromBucket(bucketValues, bucketName, matchValue, leftSide, rightSide) {
  var output = [];

  var bucketArray = bucketValues.split(',');
  $.each(bucketArray, function(i, item) {
    if (item != '') {
      // search bucket for values
      $.each(buckets[bucketName], function(j, bucketObject) {
        if (bucketObject[matchValue] == item) {
          output.push(bucketObject[leftSide]+"|"+bucketObject[rightSide]);
        }
      });


    }
  });

  return output;
}


// sort functions
function sortFunction(a, b) {
  return (b.value-a.value);
}

function bucketSortFunction(a, b) {
  return (a.name-b.name);
}


// convenience methods
function isEmpty(obj) {
  for (var prop in obj) {
    if (obj.hasOwnProperty(prop))
      return false;
  }
  return true;
}

function isString(arg) {
  return typeof arg === 'string';
}

function createArray ( obj ) {
  var count = 0;
  var newArray = new Array();
  for (var i in obj) {
    var newObj = new Object();
    newObj.name = i;
    newObj.value = obj[i];
    newArray.push(newObj);
    count++;
  }
  return newArray;
}

function createBucketArray ( obj ) {
  var count = 0;
  var newArray = new Array();
  for (var i in obj) {
    var newObj = new Object();
    var currName = i.split("<=");
    newObj.name = currName[1];
    newObj.value = obj[i];
    newArray.push(newObj);
    count++;
  }
  return newArray;
}

$.closeInstructions = function() {
  $('#instructionsDiv').hide();
  $.postJSON("/search/donotshowinstructions", {close : 'yeah' }, function(json) {
  });
};

function objectsAreEqual(a,b){
  if (typeof(a) != 'object' || typeof(b) != 'object') {
    return false;
  }
  
  for(var p in a){
    var av = a[p], bv = b[p];
    //recursion
    if (typeof(av) == 'object' || typeof(bv) == 'object' ) {
      if (objectsAreEqual(av,bv) !== true){
        return false;
      }
    } else { //simple comparisons
      if(a[p] !== b[p]){
        return false;
      }
    }
  }
  
  for(var p in b){
    var av = a[p], bv = b[p];
    //recursion
    if (typeof(av) == 'object' || typeof(bv) == 'object' ) {
      if (objectsAreEqual(av,bv) !== true){
        return false;
      }
    } else { //simple comparisons
      if(a[p] !== b[p]){
        return false;
      }
    }
  }  
  return true;
}

function populateFormElements(windowState) {
  // Open Cuisines?
  if (typeof windowState['allowedCuisine'] != 'undefined' || typeof windowState['excludedCuisine'] != 'undefined') {
    $.nui.drawerOpen($('#cuisinesHolidays'), true);
  } else {
    $.nui.drawerClose($('#cuisinesHolidays'), true);
  }
  // Open Courses?
  if (typeof windowState['allowedCourse'] != 'undefined' || typeof windowState['excludedCourse'] != 'undefined') {
    $.nui.drawerOpen($('#courses'), true);
  } else {
    $.nui.drawerClose($('#courses'), true);
  }
  // Open Time and Price?;
  if (typeof windowState['maxTotalTimeInSeconds'] != 'undefined' || typeof windowState['price.min'] != 'undefined') {
    $.nui.drawerOpen($('#timePrice'), true);
  } else {
    $.nui.drawerClose($('#timePrice'), true);
  }
  
  // Open Source?
  if (typeof windowState['allowedSource'] != 'undefined' || (windowState['restrictToMine'] == 'true')) {
    $.nui.drawerOpen($('#drawerSources'), true);
  } else {
    $.nui.drawerClose($('#drawerSources'), true);
  }

  // Always leave the ingredients drawer open
  // $.nui.drawerOpen($('#ingredients'));
  // $.nui.drawerOpen($('#nutritionDietsAllergies'));
  // $.nui.drawerOpen($('#flavors'));
  
  // Now populate the appropriate values into the input fields
  // First handle all checkboxes
  $('#source-favorites').removeAttr("checked");
  
  // This one checkbox is different from the other four classes. So handle it individually.
  if (windowState['restrictToMine'] == 'true') {
    $('#source-favorites').attr("checked", "checked");
  }
  
  var checkboxClasses = ['allowedAllergy', 'allowedDiet', 'allowedSource', 'allowedCourse'];
  $.each(checkboxClasses, function(index, value) {
    checkAppropriateCheckboxes(value, windowState[value]);
  });
  
  
  // handle sliders.. Since each slider group has a different width and naming convention, we will handle this in three sections
  // flavor sliders
  var flavorUnitWidth = 16;
  var flavors = ['Sweet', 'Salty', 'Sour', 'Savory', 'Bitter'];
  $.each(flavors, function(index, value) {
    var flavorKey = value.toLowerCase();
    if (flavorKey == 'savory') flavorKey = 'meaty';
    var minFlavorValue = windowState['flavor.' + flavorKey + '.min'];
    var maxFlavorValue = windowState['flavor.' + flavorKey + '.max'];
    if (minFlavorValue == undefined || maxFlavorValue == undefined) {
      minFlavorValue = 0;
      maxFlavorValue = 6;
    }
    $.nui.setSliderPosition(minFlavorValue + ',' + maxFlavorValue, flavorUnitWidth, $('#nui-slider-flavor' + value));
  });


  // time and price sliders
  var timePriceUnitWidth = 10;
  
  if (windowState['maxTotalTimeInSeconds'] == undefined || !($.inArray(windowState['maxTotalTimeInSeconds'], [15*60, 30*60, 45*60, 60*60, 120*60, 240*60, 480*60, 720*60, 1140*60, 2160*60]))) {
    $.nui.setSliderPosition('2160,9999', timePriceUnitWidth, $('#nui-slider-time'));
  } else {
    $.nui.setSliderPosition('0,' + windowState['maxTotalTimeInSeconds'] / 60, timePriceUnitWidth, $('#nui-slider-time'));
  }
  
  if (windowState['price.min'] == undefined || windowState['price.max'] == undefined || windowState['price.min'] > 0 || windowState['price.max'] > 10) {
    $.nui.setSliderPosition('10,99', timePriceUnitWidth, $('#nui-slider-price'));
  } else {
    $.nui.setSliderPosition(windowState['price.min'] + ',' + windowState['price.max'], timePriceUnitWidth, $('#nui-slider-price'));
  }

  
  // nutrition sliders
  var caloriesUnitWidth = 10;
  var fatUnitWidth = 16;
  var cholesterolUnitWidth = 18;
  var carbohydrateUnitWidht = 14;
  if (windowState['nutrition.ENERC_KCAL.max'] == undefined || !($.inArray(windowState['nutrition.ENERC_KCAL.max'], [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000]))) {
    $.nui.setSliderPosition(1000000, caloriesUnitWidth, $('#nui-slider-calories'));
  } else {
    $.nui.setSliderPosition(windowState['nutrition.ENERC_KCAL.max'], caloriesUnitWidth, $('#nui-slider-calories'));
  }

  if (windowState['nutrition.FAT.max'] == undefined || !($.inArray(windowState['nutrition.FAT.max'], [1, 10, 20, 30, 40, 50]))) {
    $.nui.setSliderPosition(1000000, fatUnitWidth, $('#nui-slider-fat'));     
  } else {
    $.nui.setSliderPosition(windowState['nutrition.FAT.max'], fatUnitWidth, $('#nui-slider-fat'));
  }
  
  if (windowState['nutrition.CHOLE.max'] == undefined || !($.inArray(windowState['nutrition.CHOLE.max'], [1, 2, 3, 4, 5]))) {
    $.nui.setSliderPosition(1000000, cholesterolUnitWidth, $('#nui-slider-cholesterol'));
  } else {
    $.nui.setSliderPosition(windowState['nutrition.CHOLE.max'], cholesterolUnitWidth, $('#nui-slider-cholesterol'));
  }
  
  if (windowState['nutrition.CHOCDF.max'] == undefined || !($.inArray(windowState['nutrition.CHOCDF.max'], [1, 10, 20, 30, 40, 50, 100]))) {
    $.nui.setSliderPosition(1000000, carbohydrateUnitWidht, $('#nui-slider-carbs'));     
  } else {
    $.nui.setSliderPosition(windowState['nutrition.CHOCDF.max'], carbohydrateUnitWidht, $('#nui-slider-carbs'));
  }    
  
  
  // Finally time to populate the auto-suggest text boxes.
  // ingredientsWithout, allowedIngredient, allowedCuisine are autosuggest text boxes.
  //typeof windowState['allowedIngredient'] != 'undefined' || typeof windowState['excludedIngredient'] != 'undefined'

  if (windowState['allowedIngredient']) {
    var termArray = [];
    $(windowState['allowedIngredient']).each(function(index, ingredient) {
      termArray.push({'term' : ingredient.split("|")[1]});
    });
    
    $('#ingredients-include')[0].setSelectedValues(termArray, false);
  } else {
    $('#ingredients-include')[0].setSelectedValues([], false);
  }
  
  if (windowState['excludedIngredient']) {
    var termArray = [];
    $(windowState['excludedIngredient']).each(function(index, ingredient) {
      termArray.push({'term' : ingredient.split("|")[1]});
    });
    
    $('#ingredients-exclude')[0].setSelectedValues(termArray, false);
  } else {
    $('#ingredients-exclude')[0].setSelectedValues([], false);
  }
  
  
  if (windowState['allowedCuisine']) {
    var termArray = [];
    $(windowState['allowedCuisine']).each(function(index, cuisine) {
      //termArray.push({'description' : cuisine.split("|")[0]});
      termArray.push({'description' : cuisine.split("|")[0], 'searchValue' : cuisine.split("|")[1]});
    });
    
    $('#cuisines')[0].setSelectedValues(termArray, false);
  } else {
    $('#cuisines')[0].setSelectedValues([], false);
  }
  
  
  
}

function checkAppropriateCheckboxes(checkboxClass, checkboxValues) {
  // first clear the checkboxes
  $('.' + checkboxClass).removeAttr('checked');

  // then check the appropriate ones
  if ( !(checkboxValues == undefined)  && !(typeof checkboxValues == undefined) ) {
    $.each(checkboxValues, function(index, value) {
      if (checkboxClass == 'allowedSource') {
        var elementId = '#' + escapeSpace(value.split('|')[0]);
        $(elementId).attr("checked", "checked");
      } else {
        if (checkboxClass == 'allowedCourse') {
          var elementId = '#' + checkboxClass + escapeSpace(value.split('|')[1].split('\^')[1]);
          $(elementId).attr("checked", "checked");           
        } else {
          var elementId = '#' + checkboxClass + (value.split('|')[1]).split('\^')[0];
          $(elementId).attr("checked", "checked");
        }
      }
    });
  }
  // uncomment this code to have all courses and sources checked when none are explicitly selected
  // else if (checkboxClass == 'allowedCourse' || checkboxClass == 'allowedSource') {
  //   // if none of the sources or courses are specified, check all of them
  //   $('.' + checkboxClass).attr("checked", "checked");
  // }
}

function escapeSpace(str) {
  return str.replace(/ /g, "\\ ");
}

/**
 * Formats the number according to the ‘format’ string;
 * adherses to the american number standard where a comma
 * is inserted after every 3 digits.
 *  note: there should be only 1 contiguous number in the format,
 * where a number consists of digits, period, and commas
 *        any other characters can be wrapped around this number, including ‘$’, ‘%’, or text
 *        examples (123456.789):
 *          ‘0′ - (123456) show only digits, no precision
 *          ‘0.00′ - (123456.78) show only digits, 2 precision
 *          ‘0.0000′ - (123456.7890) show only digits, 4 precision
 *          ‘0,000′ - (123,456) show comma and digits, no precision
 *          ‘0,000.00′ - (123,456.78) show comma and digits, 2 precision
 *          ‘0,0.00′ - (123,456.78) shortcut method, show comma and digits, 2 precision
 *
 * @method format
 * @param format {string} the way you would like to format this text
 * @return {string} the formatted number
 * @public
 */
Number.prototype.format = function(format) {
  if (typeof format != 'string') {return '';} // sanity check
  var precision = 0;
  var hasComma = -1 < format.indexOf(','),
  psplit = function(str){
    str += '';
    var rgx = /^\d|\.|-$/;
    var out = '';
    for( var i = 0; i < str.length; i++ )
    {
      if( rgx.test( str.charAt(i) ) ){
        if( !( ( str.charAt(i) == '.' && out.indexOf( '.' ) != -1 ) ||
               ( str.charAt(i) == '-' && out.length != 0 ) ) ){
          out += str.charAt(i);
        }
      }
    }
    return out;}(format).split('.'),
  that = this;

  // compute precision
  if (1 < psplit.length) {
    // fix number precision
    precision = psplit[1].replace(/[^0-9]/g, '').length;
    that = that.toFixed(psplit[1].length);
  }
  // error: too many periods
  else if (2 < psplit.length) {
    throw('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
  }
  // remove precision
  else {
    that = that.toFixed(0);
  }

  var isNegative = that < 0;
  that = Math.abs(that);
  // get the string now that precision is correct
  var fnum = that.toString();

  // format has comma, then compute commas
  if (hasComma) {
    // remove precision for computation
    psplit = fnum.split('.');

    var cnum = psplit[0],
    parr = [],
    j = cnum.length,
    m = Math.floor(j / 3),
    n = cnum.length % 3 || 3; // n cannot be ZERO or causes infinite loop

    // break the number into chunks of 3 digits; first chunk may be less than 3
    for (var i = 0; i < j; i += n) {
      if (i != 0) {n = 3;}
      parr[parr.length] = cnum.substr(i, n);
      m -= 1;
    }

    // put chunks back together, separated by comma
    fnum = parr.join(',');

    // add the precision back in
    if (precision > 0) { if (!psplit[1]) psplit[1]=''; var dec = psplit[1] + '00000000000000'; fnum += '.' + dec.substr(0, precision); }
  }

  // replace the number portion of the format with fnum
  return format
    .replace(/[\d,?\.?]+/, fnum)
    .replace(/\(/g, isNegative?'(':'')
    .replace(/\)/g, isNegative?')':'')
    .replace(/\-/g, isNegative?'-':'')
  ;
};
