var sorting = false;   // boolean: sorting operation currently in progress?
var tbVisible = false; // boolean: progress dialog visible?
var productList = [];  // array:   filtered product objects to be ordered/paged
var activeSortField;   // object:  contains string:name and boolean:asc properties
var sortFields;        // object:  available sort fields for each product type
var pageLength = 10;   // integer: products to show per page
var pageHTML;          // string:  global buffer for constructing list results

////////////////////////////////////////////////////////////////////////////////

/**
 * Inititalize an object for the given product type to specify each available
 * field for sorting and its default ordering (ASC if true, DESC if false).
 * The activeSortfield should also be initialized here.
 */
$(document).ready(function(){
  /* Make sure both variables we need to access are defined.  They should have
   * been declared in producttype_dashboard.js and initialized in the page.
   */
  if (typeof(producttype_id) == 'undefined' ||
      typeof(producttypes) == 'undefined') {
    return;
  }
  
  switch (producttype_id) {
    case producttypes.CREDIT_CARD:
      sortFields = [
        { name: 'vty',          asc: false, linkName: 'Value',        field: 'vty' },
        { name: 'name',         asc: true,  linkName: 'Alphabetical', field: 'name' },
        { name: 'rating_count', asc: false, linkName: 'Reviews',      field: ['rating_count', 'rating'] },
        { name: 'rating',       asc: false, linkName: 'Rating',       field: ['rating', 'rating_count'] }
      ];
      activeSortField = { name: sortFields[0].name, asc: sortFields[0].asc, field: sortFields[0].field };
      break;
    case producttypes.CD:
      sortFields = [
        { name: 'vty',          asc: false, linkName: 'Value',   field: 'vty' },
        { name: 'company_name', asc: true,  linkName: 'Company', field: 'company_name' },
        { name: 'rating_count', asc: false, linkName: 'Reviews', field: ['rating_count', 'rating'] },
        { name: 'rating',       asc: false, linkName: 'Rating',  field: ['rating', 'rating_count'] }
      ];
      activeSortField = { name: sortFields[0].name, asc: sortFields[0].asc, field: sortFields[0].field };
      break;
    default:
      sortFields = [
        { name: 'name',         asc: true,  linkName: 'Alphabetical', field: 'name' },
        { name: 'rating_count', asc: false, linkName: 'Reviews',      field: ['rating_count', 'rating'] },
        { name: 'rating',       asc: false, linkName: 'Rating',       field: ['rating', 'rating_count'] },
        { name: 'company_name', asc: true,  linkName: 'Company',      field: 'company_name' }
      ];
      activeSortField = { name: sortFields[2].name, asc: sortFields[2].asc, field: sortFields[2].field };
    }
});

////////////////////////////////////////////////////////////////////////////////

/**
 * Escape backslashes, single and double-quotes in a string with a backslash.
 *
 * @param string str
 * @return string
 */
function addslashes(str) {
  return (str+'').replace(/([\\"'])/g, "\\$1").replace(/\0/g, "\\0");
}

/**
 * Show progress dialog (ThickBox) for the product picker.  If the dialog is
 * already visible, this function will do nothing.
 */
function showProgressDialog() {
  if (tbVisible) {
    return;
  }
  
  // Remember that we've been called, in case we're invoked a second time
  tbVisible = true;
  tb_show(null, '#TB_inline?height=300&width=600&inlineId=productpicker-processing', null);
}

/**
 * Hides progress dialog (ThickBox) for the product picker.
 */
function hideProgressDialog() {
  tb_remove();
  tbVisible = false;
}

/**
 * Callback function for product sorting. Returns 1, -1, or 0 if the first
 * parameter is considered greater, lesser, or equal to the second, respectively.
 *
 * @param object First product
 * @param object Second product
 * @return integer
 */
function sortProducts(a, b) {
  
  // Check if this sorting depends on multiple fields' ordering 
  if (typeof(activeSortField.field) == 'object') {
    var result = 0;
    
    // Iterate through fields 
    $.each(activeSortField.field, function(){
      result = compareProductFields(a, b, this);
      
      if (result != 0) {
        return false;
      }
    });
    
    return result;
  } else {
    return compareProductFields(a, b, activeSortField.field);
  }
}

/**
 * Compares a common field in two products. Returns 1, -1, or 0 if the first
 * parameter is considered greater, lesser, or equal to the second, respectively.
 *
 * @param object First product
 * @param object Second product
 * @param string Field name
 * @return integer
 */
function compareProductFields(a, b, field) {
  // Perform case-insensitive comparisons for strings
  var aVal = typeof(a[field]) == 'string' ? a[field].toLowerCase() : a[field];
  var bVal = typeof(b[field]) == 'string' ? b[field].toLowerCase() : b[field];
  
  // If the order is descending, reverse the return values for greater/lesser
  if      (aVal > bVal) { return activeSortField.asc ? 1 : -1; }
  else if (aVal < bVal) { return activeSortField.asc ? -1 : 1; }
  else                  { return 0; }
}

/**
 * Toggles the sorting on a field (including sort order if the field remains the
 * same) and triggers the listing to be rendered again.
 *
 * @param  string  sortField Field name
 */
function toggleSortField(sortField) {
  // Place a lock on sorting while this function executes
  sorting = true;
  
  // We must have a valid activeSortField object to continue
  if (typeof(activeSortField)       != 'object'  ||
      typeof(activeSortField.name)  != 'string'  ||
      typeof(activeSortField.asc)   != 'boolean' ||
      (typeof(activeSortField.field) != 'object' && typeof(activeSortField.field) != 'string')) {
    sorting = false;
    return;
  }
  
  // Toggle ordering if sort field doesn't change, or switch to a new sort field 
  if (sortField == activeSortField.name) {
    activeSortField.asc = !activeSortField.asc;
  } else {
    // Search for new sort field within the array of available fields
    $.each(sortFields, function() {
      if (sortField == this.name) {
        activeSortField.name  = this.name;
        activeSortField.asc   = this.asc;
        activeSortField.field = this.field;
        return false;
      }
    });
    // If the requested sort field is not found, activeSortField remains as-is
  }
  
  productList = productList.sort(sortProducts);
  sorting = false;
}
 
/**
 * Renders new HTML for the sorting controls DIV based on the current set of
 * sort fields and active field.
 */
function renderSorting() {
  
  var buffer = '<div class="sorting bm_1 r"><div class="sorting_container fr"><h3 class="fl">Sort by:</h3><div class="fl"><ul>';
  
  // Generate sorting links for each available field
  $.each(sortFields, function() {
    var liClass = '';
    
    if (this.name == activeSortField.name) {
      liClass = ' active use_sprite ' + (activeSortField.asc ? 'asc' : 'desc') + '"';
    }
    
    buffer += '<li class="fl'+liClass+'"><a href="#" onclick="toggleSortField(\''+this.name+'\'); render(false, 1); return false;">'+this.linkName+'</a></li>';
  });
  
  buffer += '</ul></div></div><div class="clear"></div></div>';
  
  $('#productpicker-sorting').html(buffer);
}

/**
 * Renders new HTML for the pagination controls DIV based on the current page
 * and total number of products in the list.
 *
 * @param integer currentPage
 * @param integer productListLength
 */
function renderPagination(currentPage, productListLength) {
  // Calculate the total number of pages for the product list
  var totalPages = Math.ceil(productListLength / pageLength);
  
  // No need to render pagination controls for a single page of results
  if (totalPages <= 1) {
    $('#productpicker-pagination').empty();
    return;
  }
  
  var buffer = '<div class="sorting tm_1 bm_1"><div class="sorting-container r">Page:';
  
  // Do not show "previous" link for the first page
  if (currentPage != 1) {
    buffer += '<a href="#productpicker-render" onclick="render(false, '+(currentPage - 1)+');">&laquo; Prev</a> | ';
  }
    
  for (var p = 1; p <= totalPages; p++) {
    if (Math.abs(p - currentPage) < 10) {
      // Do not create a link for the current page
      buffer += (p == currentPage) ? p+' | ' : '<a href="#productpicker-render" onclick="render(false, '+p+');">'+p+'</a> |';
    }
  }
    
  buffer += '</ul>';
    
  // Do not show "next" link for the last page
  if (currentPage != totalPages) {
    buffer += '<a href="#productpicker-render" onclick="render(false, '+(currentPage + 1)+');">Next &raquo;</a>';
  }
  
  // TODO: if more than 16 pages are displayed, link directly to the last page
  
  buffer += '</div></div>';
  
  $('#productpicker-pagination').html(buffer);
}

////////////////////////////////////////////////////////////////////////////////

/**
 * Render HTML for any product. Suit up!
 *
 * @param integer i       Product's index in the paginated product list
 * @param object  product Product record
 */
function renderLegen___Wait_for_it___dary(i, product) {
  
  //Create product URL and title (CD's need rate ID and prefix, respectively)
  var id = (product.cd_rate_id ? product.cd_rate_id : product.id);
  var product_name = (producttype_id == producttypes.CD && cd_prefix ? cd_prefix + product.name : product.name);
  var product_url = '/reviews/' + product.slug + (product.cd_rate_id ? '/' + product.cd_rate_id : '');
  var company_url = '/companies/' + product.company_slug;
  
  pageHTML += '<div class="ghf p_review'+(i % 2 === 1 ? ' p_review_alt' : '')+'">';
  pageHTML += '<div class="grid_2 alpha"><div class="p_r_info c pt lm">';
  
  if (!product.is_available) {
    pageHTML += '<div class="p_r_expired"><span>No Longer<br /> Available</span></div>';
  } else if (!product.is_accepting_applicants) {
    pageHTML += '<div class="p_r_expired"><span>Not Accepting<br /> New Applicants</span></div>';
  }
  
  if (product.image_url) {
    pageHTML += '<div class="p_r_img"><a href="'+product_url+'"><img src="'+product.image_url+'" /></a></div>';
  }
  
  if (producttype_id == producttypes.CREDIT_CARD && product.link_partner_url) {
    pageHTML += '<div class="p_r_apply"><a href="'+product.link_partner_url+'" rel="'+product.slug+'" class="informa btn_2 use_sprite autocenter tm_0" target="_blank">Apply Now</a></div>';
  } else if (producttype_id == producttypes.CD && product.insturl) {
    pageHTML += '<div class="p_r_apply"><a href="'+product.insturl+'" rel="'+product.slug+'" class="informa btn_2 use_sprite autocenter tm_0" target="_blank">Apply Now</a></div>';
  }else if (product.affiliate_url) {
    pageHTML += '<div class="p_r_apply"><a href="'+product.affiliate_url+'" rel="'+product.slug+'" class="direct btn" target="_blank">Apply Now</a></div>';
  }
  
  pageHTML += '<div class="p_r_learnmore"><a href="'+product_url+'" class="tm_0 autocenter">Learn More</a></div>';
  
  if (producttype_id == producttypes.CREDIT_CARD || producttype_id == producttypes.CD) {
    pageHTML += '<form action=""><div class="p_r_compare bm_1 tm_1">';
    pageHTML += '<input id="compare-checkbox-'+id+'" type="checkbox" />';
    pageHTML += '<label for="compare-checkbox-'+id+'"> Compare it</label>';
    pageHTML += '</div></form>';
  }
  
  pageHTML += '</div></div>'; // <!-- /.p_r_info --> <!-- /.grid_2 alpha -->
  
  pageHTML += '<div class="product_picker_listing fl tm_1 bm_1">';
  
  if (producttype_id != producttypes.CREDIT_CARD && producttype_id != producttypes.CD) {
    pageHTML += '<h3 id="product-name-'+id+'"><a href="'+product_url+'">'+product_name+'</a> from <a href="'+company_url+'">'+product.company_name+'</a></h3>';
  }
  
  pageHTML += '<div class="p_col_contain">';
  
  if (producttype_id == producttypes.CREDIT_CARD) {
    pageHTML += '<div class="p_col_alt">';
    pageHTML += '<h3 id="product-name-'+id+'"><a href="'+product_url+'">'+product_name+'</a> from <a href="'+company_url+'">'+product.company_name+'</a></h3>';
    pageHTML += '<div class="cc-listings">';
    pageHTML += '<table class="listing-stats use_sprite tm_1">';
    pageHTML += '<thead><tr><th class="one">APR (As Low As)</th><th class="two">Annual Fee</th><th class="three">Intro APR</th></thead>';
    
    if (product.informa_update_at) {
      pageHTML += '<tfoot><tr><td class="footnote" colspan="3">Rates current as of '+product.informa_update_at+'</td></tr></tfoot>';
    }
    
    pageHTML += '<tbody><tr class="values">';
    pageHTML += '<td>'+product.rate+'%'+(product.m_type & bitmasks.m_type.is_type_fixedrate ? '' : ' (V)')+'</td>';
    pageHTML += '<td>$'+product.annual_fee+'</td>';
    pageHTML += '<td>'+product.intro_rate+'%</td>';
    pageHTML += '</tr></tbody>';
    pageHTML += '</table>';
    pageHTML += '</div></div>'; // <!--  /.cc-listings --> <!-- /.p_col_alt -->
  } else if (producttype_id == producttypes.CD) {
    pageHTML += '<div class="p_col_alt">';
    pageHTML += '<h3 id="product-name-'+id+'"><a href="'+product_url+'">'+product_name+'</a> from <a href="'+company_url+'">'+product.company_name+'</a></h3>';
    pageHTML += '<div class="cc-listings">';
    pageHTML += '<table class="listing-stats use_sprite tm_1">';
    pageHTML += '<thead><tr><th class="one">APY</th><th class="two">Minimum to Earn</th><th class="three" style="width: 100px">'+(product.month_max == product.month_min ? 'Term' : 'Term Range')+'</th></thead>';
    
    if (product.informa_update_at) {
      var propNote = (product.propname_undefined ? ' Contact the institution for additional information or requirements on this rate.' : '');
      pageHTML += '<tfoot><tr><td class="footnote" colspan="3">Rates current as of '+product.informa_update_at+'.'+propNote+'</td></tr></tfoot>';
    }
    
    pageHTML += '<tbody><tr class="values">';
    pageHTML += '<td>'+product.apy+'%</td>';
    pageHTML += '<td>$'+product.deposit_min+'</td>';
    pageHTML += '<td>'+(product.month_max == product.month_min ? product.month_max : product.month_min + '-' + product.month_max)+' months</td>';
    pageHTML += '</tr></tbody>';
    pageHTML += '</table>';
    pageHTML += '</div></div>'; // <!--  /.cc-listings --> <!-- /.p_col_alt -->
  } else {
    pageHTML += '<div class="p_col">';
    
    if (product.description) {
      // TODO: Only display "Read More" link if description is abbreviated
      pageHTML += '<p class="blurb bm_1">'+product.description+' <a href="'+product_url+'">Read More</a></p>';
    } else {
      pageHTML += '<p>&nbsp;</p>';
    }
    
    pageHTML += '</div>'; // <!-- /.p_col -->
  }
  
  pageHTML += '<div class="p_col2">';
  pageHTML += '<div class="fl">';
  pageHTML += '<div class="rating"><img src="'+product.rating_image_url+'" alt="'+product.rating+'" /></div>';
  pageHTML += '<div class="rating_count tm_0"><a href="'+product_url+'">'+(product.rating_count === 0 ? 'No' : product.rating_count)+(product.rating_count == 1 ? ' review' : ' Reviews')+'</a></div>';
  pageHTML += '</div>'; // <!-- /.fl -->
  
  if (producttype_id == producttypes.CREDIT_CARD) {
    pageHTML += '<div class="'+(product.vty >= 0 ? 'vty' : 'cty')+' fr">';
    pageHTML += '<div class="vty-tt"><strong>Personal '+(product.vty >= 0 ? 'Value' : 'Cost')+'</strong><a href="http://www.filife.com/stories/how-is-value-to-you-calculated" rel="'+(product.vty >= 0 ? '#pv' : '#pc')+'" class="ms_replace pt-tt">?</a></div>';
    pageHTML += '<h4 class="value"><span>$</span>'+product.vtyDisplay+'</h4>';
    pageHTML += '</div>';
  } else if (producttype_id == producttypes.CD) {
    pageHTML += '<div class="vty fr">';
    pageHTML += '<div class="vty-tt"><strong>Personal Value</strong><a href="#" rel="#pv" class="ms_replace pt-tt">?</a></div>';
    pageHTML += '<h4 class="value"><span>$</span>'+product.vtyDisplay+'</h4>';
    pageHTML += '</div>';
  }
  
  pageHTML += '</div><div class="clear"></div></div>'; // <!-- /.p_col2 --> // <!-- /.p_col_contain -->
  
  // TODO: Implement latest review display
  if (product.review) {
    pageHTML += '<div class="pr_pk">';
    pageHTML += '<div class="usr"><a href="'+product.review_url+'"><img src="'+product.review_icon+'" /></a></div>';
    pageHTML += '<div class="pr_pk_content"><p class="blurb">'+product.review_comment+'</p></div>';
    pageHTML += '<div class="clear"></div></div>'; // <!-- /.pr_pk -->
  }
  
  pageHTML += '</div><div class="clear"></div></div>'; // <!-- /.product_picker_listing --> <!-- /.p_review -->
  
  // True story.
}

////////////////////////////////////////////////////////////////////////////////

/**
 * Main render function examines the product type and calls the respective
 * render method.  Renders the product list and its associated sorting and
 * pagination controls.  If the filteredList parameter is false, the existing
 * productList will be used.
 *
 * @param mixed   filteredList Product list array or boolean false
 * @param integer page         Page number (optional, defaults to 1)
 */
function render(filteredList, page) {
  // Lock sorting until we certainly have a usable productList
  sorting = true;
  
  // Unless boolean false was passed, sort/load filteredList into productList
  if (typeof(filteredList) != 'boolean' || filteredList !== false) {
    productList = filteredList.sort(sortProducts);
  }
  
  // Release sorting lock
  sorting = false;
  
  // Set default value for page parameter if necessary
  if (typeof(page) == 'undefined') {
    page = 1;
  }
  
  // Slice the array by the offset and limit
  var offset = pageLength * (page - 1);
  var products = productList.slice(offset, offset + pageLength);
  
  // Render sorting and pagination controls
  renderSorting();
  renderPagination(page, productList.length);
  
  // Clear the producttype description if it's still visible
  $('#productpicker-producttype-description').remove();
  
  // If the paginated product list is empty, display a message and abort
  if (products.length === 0) {
    var buffer  = '<h3>No results!</h3>';
    buffer     += '<p>Hmmmm. We can&rsquo;t find anything to match your needs. Are you sure this is what you&rsquo;re looking for? Try adjusting the filters above.</p>';
    $('#productpicker-render').html(buffer);
    return;
  }
  
  // Initialize the global pageHTML buffer
  pageHTML = '';
  
  $.each(products, renderLegen___Wait_for_it___dary);
  
  // Update the product listings and then clear the global pageHTML buffer
  $('#productpicker-render').html(pageHTML);
  pageHTML = '';
  
  // Clear the articles widget
  //$('#articles_contain').empty();
  
  // Perform any additional functionality based on the current product type
  switch (producttype_id) {
  case producttypes.CD:
  case producttypes.CREDIT_CARD:
    // Update the Informa note in the page footer
    //$('#provided_note span.informa_note').html('Provided by <a onclick="createInformaModule(\''+informa_last_updated+'\'); return false;" rel="nofollow" href="#">Informa Research Services</a>');
    
    // Enable VTY tooltips that may just have been rendered
    $('#productpicker-render div.vty-tt a').tooltip({
      bodyHandler: function() {
        return $($(this).attr("rel")).html();
      },
      track: true, delay: 0, showURL: false, fade: 250});
    
    // Allow backpack to mark checkboxes for its products and update display count
    updateBackpackPageElements();
    break;
  }
}
