/**
 * Escapes the given string, so it is HTML safe.
 * @param {string} html The input HTML to escape.
 * @return {string} The escaped HTML.
 */
function escapeHtml(html) {
  var s = '' + html;
  return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;').replace(/'/g, '&#39;');
}

/**
 * Escapes the given URL.
 * @param {string} url The URL to escape.
 * @return {string} The escaped URL.
 */
function escapeUrl(url) {
  return url.replace(/</g, '%3C').replace(/>/g, '%3E').replace(/"/g, '%22');
}

/**
 * Strips off all HTML tags.
 * @param {string} html The input HTML.
 * @return {string} The stripped version of HTML.
 */
function stripOffHtmlTags(html) {
  var s = '' + html;
  return s.replace(/\<[^\>]+\>/g, '');
}

function isArray(obj) {
  if (obj.constructor.toString().indexOf('Array') == -1) {
    return false;
  } else {
    return true;
  }
}

function getHTMLOneEntryList(thumb, title, href, price) {
  var s = '<tr class="cse-commerce-result"><td>' +
           (thumb ? '<a class="url" href="' + href + '">' +
           '<img src="' + thumb + '" height="75" border="0"></a>' :
           '') + '</td><td><a class="url" href="' + href + '">' +
           title + '&raquo;</a></td><td class="resultsPrice">' + '  $' + price +
           '</td></tr>';
  return s;
}

function getHTMLOneEntryGrid(thumb, title, href, price) {
  var s = '<li class="result-grid"><div class="result-cont-grid">';
  s += thumb ? '<p class="result-image-grid"><a class="url" href="' +
       href + '"><img width="100%" height="75" border="0" src="' + thumb +
       '"></a></p>' : '';

 /* if (title.length > 15) {
    title = title.substring(0, 12) + '...';
  }*/

  s += '<h3 class="result-h3-grid"><a wrap="soft" href="' +
       href + '">' + title + '&raquo;</a></h3><p class="resultsPrice">' + '  $' + price + '</p></div>';

  return s;
}

function getPromotions(promotions) {
  // Create promotions section at the top.
  var str = '';
  if (promotions) {
      for (var i = 0, item; item = promotions.g$promotion[i]; ++i) {
      str += '<div class="cse-commerce-promotion">';
      str += '<table><tr>';
      if (item.g$image_link) {
        str += '<td class="cse-commerce-promotion-image">';
        str += '<a href="' + escapeUrl(item.g$link.$t) + '">';
        str += '<img src="' + escapeUrl(item.g$image_link.$t);
        str += '" height="130"></img>';
        str += '</a>';
        str += '</td>';
      }
      str += '<td class="cse-commerce-promotion-title">' +
             '<strong><a href="' + escapeUrl(item.g$link.$t) + '\">' +
             escapeHtml(stripOffHtmlTags(item.g$title.$t)) + '</a></strong>';
      if (item.g$snippet) {
        str += '<br/><span>' + escapeHtml(item.g$snippet.$t) + '</span>';
      }
      str += '</div></td></tr></table></div>';
    }
  }
  return str;
}

function getHTML(entries, promotions, view, gridImage, listImage) {
  var imagePrefix = 'http://www.google.com/cse/images/commerce/';

  if (view == 'grid') {
    if (gridImage) {
      gridImage.src = imagePrefix + 'grid_view_selected.png';
      gridImage.style.display = '';
    }
    if (listImage) {
      listImage.src = imagePrefix + 'list_view_unselected.png';
      listImage.style.display = '';
    }
  } else {
    if (gridImage) {
      gridImage.src = imagePrefix + 'grid_view_unselected.png';
      gridImage.style.display = '';
    }
    if (listImage) {
      listImage.src = imagePrefix + 'list_view_selected.png';
      listImage.style.display = '';
    }
  }

  var s = getPromotions(promotions);

  s += view == 'grid' ? '<div id="cse-main-div">' : '<table><colgroup>' +
               '<col id="cse-commerce-image">' +
               '<col id="cse-commerce-description">' +
               '<col id="cse-commerce-price"></colgroup><tbody>';

  for (var i = 0, item; item = entries[i]; ++i) {
    var title = item.getTitle().getText();
    var description = item.getContent().getText();
    var href = item.getLink('alternate').getHref();
    var price = item.getAttribute('price').getValue();
    var thumb;

    if ('image_link' in item.getAttributes()) {
      var img_obj = item.getAttribute('image_link');
      if (isArray(img_obj)) {
        thumb = img_obj[0].getValue();
        if (thumb[7] == 0) {
          thumb = img_obj[1].getValue();
        }
        if (thumb[7] == 0) {
          thumb = img_obj[2].getValue();
        }
      } else {
        thumb = item.getAttribute('image_link').getValue();
      }
    }

    if (thumb) {
      thumb = escapeUrl(thumb);
    }

    title = escapeHtml(title);
    href = escapeUrl(href);
    price = escapeHtml(price);
	
	//remove unwanted info from title and price strings
	price = price.replace('usd','');
	href = href.replace(/\?utm.*$/, '');
    s += view == 'grid' ? getHTMLOneEntryGrid(thumb, title, href, price) :
                 getHTMLOneEntryList(thumb, title, href, price);
  }

  s += view == 'list' ? '</tbody></table>' : '';
  return s;
}

/**
 * Initializes Commerce Search preview code.
 *
 * Note: This script requires http://www.google.com/jsapi to be included
 * in your HTML page:
 * <script src="http://www.google.com/jsapi" type="text/javascript"></script>
 */
function initializeCommerceSearch(customerId,
                                  cx,
                                  resultsPanel,
                                  unfilteredAttrsPanel,
                                  filteredAttrsPanel,
                                  opt_doneCallback,
                                  opt_errorHandler,
                                  opt_currency,
                                  opt_gridImage,
                                  opt_listImage) {
  google.load('gdata', '1.x');
  var baseUrlPrefix = 'http://www.google.com/base/feeds/';
  var baseService;

  // Global refinement vars.
  var sortOrder = 'match';
  var priceMatch = '';
  var currency = opt_currency ? opt_currency : 'usd';
  var currentPage = 1;
  var resultsLimit = 10;
  var queryString = '';
  var allEntries;
  var allAttrEntries;
  var viewPref = 'list';
  var allPromotions;
  var gridImage = opt_gridImage;
  var listImage = opt_listImage;
  var filtersMap = {};  // Map which maps an attribute to a filter key.
  var unfilteredAttrs = unfilteredAttrsPanel;
  var filteredAttrs = filteredAttrsPanel;


  // This callback will run when the snippets feed query has complete
  // Customize based on which attributes you want to display.
  var handleSnippetsFeed = function(result) {
    var spellingSuggestions = [];
    for (var i = 0, item; item = result.feed.link[i]; ++i) {
      if (item.rel == 'http://schemas.google.com/g/2006#spellcorrection' &&
          item.title) {
        spellingSuggestions.push(item.title);
      }
    }
    if (opt_doneCallback) {
      var totalResults = result.feed.getTotalResults().getValue();
      var totalPages = Math.min(Math.ceil(totalResults / resultsLimit), 8);
      opt_doneCallback(result.feed, spellingSuggestions, currentPage,
                       totalPages);
    }

    allEntries = result.feed.entry;
    allPromotions = result.feed.g$promotions;
    var str = getHTML(allEntries, allPromotions, viewPref, gridImage,
                      listImage);
    resultsPanel.innerHTML = str;
  };

  function truncateAndAddEllipses(nChars, str) {
    if (str.length > nChars) {
      return str.substring(0, nChars - 3) + '...';
    } else {
      return str;
    }
  }

  // This callback will run when the snippets feed query has completed.
  var handleAttributesFeed = function(result) {
    var attrsFilter = '';
    var attrsUnfilter = '';
    var sums = [];
    var temp;

    allAttrEntries = result.feed.entry;

    // Compute the summation of the Values vector.

    for (var i = 0, item; item = allAttrEntries[i]; ++i) {
      var t = item.getAttribute();
      var attribs = t.getValues();
      sums[i] = 0;
      for (var j = 0, attrib; attrib = attribs[j]; ++j) {
        sums[i] += parseInt(attrib.getCount());
      }
    }

    // Sort by the summation computed in the previous step.
    // This is a heuristic to decide whc are the more relevant
    // attributes.
    for (var i = 0; i < sums.length; ++i) {
      for (var j = i + 1; j < sums.length; ++j) {
        if (sums[j] > sums[i]) {
          temp = sums[i];
          sums[i] = sums[j];
          sums[j] = temp;
          temp = allAttrEntries[i];
          allAttrEntries[i] = allAttrEntries[j];
          allAttrEntries[j] = temp;
        }
      }
    }

    for (var i = 0, item; item = allAttrEntries[i]; ++i) {
      var t = item.getAttribute();
      var attribs = t.getValues();

      if (t.name != 'price') {
        if (filtersMap[t.name]) {
          var count = '';
          for (var j = 0, attrib; attrib = attribs[j]; ++j) {
            if (attrib.getValue() == filtersMap[t.name]) {
              count = attrib.getCount();
              break;
            }
          }

          attrsFilter += '<div><h4>By ' + t.name +
                         '</h4><div class="cse-commerce-facet">';
          attrsFilter += '<ul><li><a href="javascript:void(0)" ' +
                         'onClick="javascript:af(\'' + t.name +
                         '\', \'' + filtersMap[t.name] + '\')">' +
                         truncateAndAddEllipses(20,
                                                escapeHtml(filtersMap[t.name]));

          if (count != '') {
            attrsFilter += '(' + escapeHtml(count) + ')';
          }

          attrsFilter += '</a></li>';
          attrsFilter += '<li><a href="javascript:void(0)" ' +
                         'onClick="javascript:af(\'' + t.name +
                         '\', null)">View all</a></li>';
          attrsFilter += '</ul></div></div>';
        } else {
          attrsUnfilter += '<div id=attr' + i + '>';
          attrsUnfilter += showBuckets(i, false);
          attrsUnfilter += '</div>';
        }
      }
    }

    if (attrsFilter != '') {
      filteredAttrs.innerHTML = attrsFilter;
      filteredAttrs.style.display = '';
    } else {
      filteredAttrs.style.display = 'none';
    }

    if (attrsUnfilter != '') {
      unfilteredAttrs.innerHTML = attrsUnfilter;
      unfilteredAttrs.style.display = '';
    } else {
      unfilteredAttrs.style.display = 'none';
    }
  };

  // This function shows the buckets in an unfiltered facet. If all is set to
  // true all facets are shown with a "less" option if there are more than 5
  // facets. If all is set to false only 5 facets are shown with a "more..."
  // option.
  function showBuckets(i, all) {
    var entry = allAttrEntries[i];
    var attr = entry.getAttribute();
    var str = '<h4>By ' + attr.name +
              '</h4><div class="cse-commerce-facet"><ul>';
    var attribs = attr.getValues();
    var showLess = false;
    var showMore = false;

    for (var j = 0, attrib; attrib = attribs[j]; ++j) {
      if (j > 4) {
        if (!all) {
          showMore = true;
          break;
        } else {
          showLess = true;
        }
      }
      var value = truncateAndAddEllipses(20,
                                         escapeHtml(attrib.getValue()));
      var count = escapeHtml(attrib.getCount());
      str += '<li><a href="javascript:void(0)" ' +
             'onClick="javascript:af(\'' + attr.name +
             '\', \'' + attrib.getValue() + '\')">' + value +
             '(' + count + ')</a></li>';
    }
    if (showLess) {
      str += '<li><a href="javascript:void(0)" ' +
             'onClick="javascript:sb(' + i + ',false)">Less...</a></li>';
    } else if (showMore) {
      str += '<li><a href="javascript:void(0)" ' +
             'onClick="javascript:sb(' + i + ',true)">More...</a></li>';
    }
    str += '</ul></div>';
    return str;
  }

  function showBucketsDiv(index, all) {
    var div = document.getElementById('attr' + index);
    if (div) {
      div.innerHTML = showBuckets(index, all);
    }
  }

  // This callback will run if getSnippetFeed throws any errors.
  var handleError = function(error) {
    var message = error.cause ? error.cause.statusText :
        (error.message ? error.message : error);
    if (opt_errorHandler) {
      opt_errorHandler(message);
    } else {
      alert(message);
    }
  };

  // This function is called to clear the results pane.
  function clearResults() {
    if (gridImage) {
      gridImage.style.display = 'none';
    }

    if (listImage) {
      listImage.style.display = 'none';
    }

    resultsPanel.innerHTML = '';
  }

  // Here we construct the call to Base using the GDATA API.
  // We make two requests:
  //  - One to fetch the search results
  //  - The other to get the attribute counts & buckets
  function getMyFeed(q) {
    if (q == '') {
      handleError('Empty query');
      return;
    }
    if (customerId == '') {
      handleError('Missing or invalid customer ID');
      return;
    }
    queryString = q.replace(/#/g, '%23');

    baseService = new google.gdata.gbase.GoogleBaseService('Commerce-Demo');
    var query = new google.gdata.gbase.SnippetsQuery(
        baseUrlPrefix + 'snippets');
    query.setFullTextQuery(queryString);
    var baseQuery = '[customer id:' + customerId + '][item type:products]';

    for (var key in filtersMap) {
      if (filtersMap[key]) {
        baseQuery += '[' + key + ':' + filtersMap[key] + ']';
      }
    }

    query.setBq(baseQuery + priceMatch);
    if (sortOrder == 'match') {
      query.setOrderby('auto');
    } else if (sortOrder == 'price_low') {
      query.setOrderby('price(float ' + currency + ')');
      query.setSortorder('ascending');
    } else if (sortOrder == 'price_high') {
      query.setOrderby('price(float ' + currency + ')');
      query.setSortorder('descending');
    } else if (sortOrder == 'name') {
      query.setOrderby('title(text)');
      query.setSortorder('ascending');
    }

    query.setStartIndex((currentPage - 1) * resultsLimit + 1);
    query.setMaxResults(resultsLimit);
    query.setParam('cx', cx);
    query.setParam('content', 'promotions');

    baseService.getSnippetsFeed(query, handleSnippetsFeed, handleError);

    // Get attributes.
    var attributes = new google.gdata.gbase.AttributesQuery(baseUrlPrefix +
                                                            'attributes');
    attributes.setFullTextQuery(queryString);
    attributes.setBq(baseQuery + priceMatch);
    attributes.setParam('cx', cx);
    attributes.setParam('max-values', '20');

    baseService.getAttributesFeed(attributes, handleAttributesFeed,
                                  handleError);
  };

  // Called when we click on a price refinement.
  function priceSubmit(query, price) {
    currentPage = 1;
    if (price == 0) {
      priceMatch = '[price < 50.00 ' + currency + ']';
    } else if (price == 50) {
      priceMatch = '[price:50.0..100.00 ' + currency + ']';
    } else if (price == 100) {
      priceMatch = '[price:100.0..250.00 ' + currency + ']';
    } else if (price == 250) {
      priceMatch = '[price:250.0..500.00 ' + currency + ']';
    } else if (price == 500) {
      priceMatch = '[price >= 500.0 ' + currency + ']';
    }
    clearResults();
    getMyFeed(query);
  };

  // Called when we submit a new search term.
  function formSubmit(query, order) {
    currentPage = 1;

    if (order) {
      sortOrder = order;
    } else {
      sortOrder = 'match';
      priceMatch = '';
      filtersMap = {};
    }

    clearResults();
    getMyFeed(query);
  };

  // Called when we submit a new spelling correction.
  function spellSubmit(query) {
    currentPage = 1;
    clearResults();
    getMyFeed(query);
  }

  // Called when we navigate to the given page.
  function pageSubmit(page) {
    currentPage = page;
    clearResults();
    getMyFeed(queryString);
  }

  // Called when we view to change the view
  function changeView(view, resultsPanel) {
    viewPref = view;
    resultsPanel.innerHTML = getHTML(allEntries, allPromotions, view, gridImage,
                                     listImage);
  }

  // Called when a filter is applied.
  function applyFilter(query, key, value) {
    currentPage = 1;
    filtersMap[key] = value;
    clearResults();
    getMyFeed(query);
  }

  return {
    'formSubmit': formSubmit,
    'priceSubmit': priceSubmit,
    'spellSubmit': spellSubmit,
    'pageSubmit': pageSubmit,
    'changeView': changeView,
    'applyFilter': applyFilter,
    'showBucketsDiv': showBucketsDiv };
}

