var catalogManager = function() {
  var consts = {
    HISTORY_PREFIX_SORT : "sort:",
    HISTORY_PREFIX_FILTER : "filter:",
    IMAGE_REPO : "data/"    
  };
  var priv = {
    queryBaseUrl : "/solr/catalog/select/?",
    collectionPageUrl : "main/digital-comics/issues.html",
    store : "US-EN",
    sort : {},
    fields : {
      displayName : "fieldName",
      story : "fieldStory",
      genre : "fieldGenre",
      publisherName : "fieldPublisherName",
      credit_author : "fieldWriter",
      credit_artist : "fieldPencil"
    },
    filterFields : {},
    sortFields : {},
    arrows : {},
    sortDescriptionId : "",
    sortDescriptionTexts : {},
    defaultSortField : "",
    secondSortField : "",
    filters : {},
    queryStart : null,
    itemsPerPage : 16,
    // pageIndex is 1-based!!!!
    pageIndex : 1,
    maxPageIndex : 1,
    listAjaxRequest : null,
    currentSearchTerm : null,
    queryDelayTimer : 0,
    queryDelayMsec : 200,
    historyValue : "",
    addingToHistory : false,
    useHistory : true,
    thumbnailTemplate : "",
    popupTemplate : "",
    mouseOver: null,
    mouseOut: null,
    showDetailDiv: null,
    showDetailPage: null,
    items: {},
    scrollToResult: false,

    /* private functions */

    itemToHtml : function(item, column) {

      var newHtml = priv.thumbnailTemplate;

      newHtml = newHtml.replace(/\{id\}/g, item.id);
      newHtml = newHtml.replace(/\{displayName\}/g, item.displayName);
      newHtml = newHtml.replace(/\{image\}/g, consts.IMAGE_REPO + item.image);
      newHtml = newHtml.replace(/\{detailPageId\}/g, urlEncoder.encode(item.displayName) + "_" + item.id);
      
      if (column == 4) {
        newHtml = newHtml.replace(/>/, " style='background-image: none;'>")
      }

      return newHtml
    },
    
    arrayToString: function(array, limit) {
      if (!array) {
        return "";
      }
      var string = "";
      for ( var j in array) {
        if (j != 0) {
          string += ", ";
        }
        if (string.length + array[j].length > limit) {
          string += "...";
          break;
        }
        string += array[j];
      }
      return string;
    },
    
    updateFacetCounts : function(values) {
      for (var key in values.facet_counts.facet_fields) {
        jQuery("#" + priv.filterFields[key].fieldId).html(
            priv.getOptionsHtml(key, priv.filterFields[key].noSelection,
                values.facet_counts.facet_fields[key]));
      }
    },

    updateResultList : function(values) {
      priv.listAjaxRequest = null;
      var items = values.response.docs;

      var numFound = values.response.numFound;
      var newHtml = "";
      
      priv.maxPageIndex = Math.ceil(numFound / priv.itemsPerPage);
      jQuery(".pageIndex").attr("innerHTML", priv.pageIndex);
      jQuery(".pageCount").attr("innerHTML", priv.maxPageIndex);
      
      jQuery(".prevPageBtn").attr("disabled", priv.pageIndex <= 1);
      jQuery(".nextPageBtn").attr("disabled",
          (priv.pageIndex >= priv.maxPageIndex));
      jQuery("#searchResultItemCount").html(numFound);
      
      var firstItemIndex = (priv.pageIndex - 1) * priv.itemsPerPage + 1;
      var lastItemIndex = firstItemIndex + items.length - 1;
      
      jQuery("#firstItemIndex").html(firstItemIndex);
      if (numFound == 0) {
        jQuery("#firstItemIndex").html("0");
      }
      jQuery("#lastItemIndex").html(lastItemIndex);
      jQuery("#hasResults").css("display", (numFound > 0 ? "block" : "none"));
      jQuery("#noResults").css("display", (numFound == 0 ? "block" : "none"));

      priv.items = new Object();
      for ( var i in items) {
        var item = items[i];
        priv.items[item.id] = item;
        newHtml += priv.itemToHtml(item, i % 5);
      }

      jQuery("#resultList").html(newHtml);
      if (typeof(priv.mouseOver) == "function" && typeof(priv.mouseOver) == "function") {
        jQuery(".resultElement").hover(priv.mouseOver, priv.mouseOut);
      }

      if (priv.scrollToResult) {
        priv.scrollToResult = false;
        window.scrollTo(0, 400);
      }

      priv.postWebtrendsStats(priv.currentSearchTerm, numFound != "0");
    },

    getOptionsHtml : function(filterType, noSelString, data) {
      var optionsHtml = "<option value='ALL'>" + noSelString
          + "</option>";

      for ( var key in data) {
        var count = data[key];
        var selectedAttrVal = (priv.filters[filterType] == key) ? " selected='selected'" : "";
        var disabledAttrVal = (count == 0) ? " disabled='disabled'" : "";
        
        if (count != 0) {
          optionsHtml += "<option value='" + key.replace("\'","&#39;") + "'"
              + selectedAttrVal + disabledAttrVal + ">" + key + " (" + count
              + ")</option>";
        }
      }

      return optionsHtml;
    },

    updateDetailDiv : function(id) {
      var item = priv.items[id];
      var newHtml = priv.popupTemplate;

      var story = item.story;
      if (story.length > 250) {
        // Regex matches max 250 characters and assures it will end on the end of a word
        story = story.match(/^.{0,250}(?=\b)/) + '...';
      }

      var publishDate = item.publishDate.match(/\d\d\d\d-\d\d/);

      newHtml = newHtml.replace(/\{id\}/g, item.id);
      newHtml = newHtml.replace(/\{displayName\}/g, item.displayName);
      newHtml = newHtml.replace(/\{image\}/g, consts.IMAGE_REPO + item.image);
      newHtml = newHtml.replace(/\{authors\}/g, priv.arrayToString(item.credit_author, 80));
      newHtml = newHtml.replace(/\{artists\}/g, priv.arrayToString(item.credit_artist, 80));
      newHtml = newHtml.replace(/\{publishDate\}/g, publishDate);
      newHtml = newHtml.replace(/\{story\}/g, story);
      newHtml = newHtml.replace(/\{detailPageId\}/g, urlEncoder.encode(item.displayName) + "_" + item.id);

      jQuery("body").append(newHtml);
      
      var detailDiv = jQuery("#pop" + item.id);
      var windowObj = jQuery(window);
      var windowHeight = windowObj.height();
      
      if (!document.getElementById("backgroundPopup")) {
        jQuery("body").append("<div id='backgroundPopup'><!-- --></div>");
      }
      jQuery("#backgroundPopup").show();
      detailDiv.css({left: (windowObj.width() - detailDiv.width()) / 2, top: (windowObj.height() - detailDiv.height()) / 2 + windowObj.scrollTop()}).fadeIn(250);
    },

    updatePageButtonTexts : function() {
      jQuery("#pageIndex").html(priv.pageIndex);
      jQuery("#pageCount").html(priv.maxPageIndex);
    },

    formatSearchTerm : function(searchTerm) {
      if (!searchTerm) {
        return null;
      }
      searchTerm = searchTerm.replace(/^\s+/, "").replace(/\s+$/, "");
      searchTerm = searchTerm.replace(/\#|\%|\!|\+|\-|\(|\)|\{|\}|\[|\]|\^|\"|\~|\*|\?|\:|\\|\&\&|\|\|/g, "");

      if (!searchTerm || searchTerm == "") {
        return null;
      }
      searchTerm = "(" + searchTerm.replace(/\s+/, "+AND+") + ")";      
      
      return searchTerm;
    },

    cleanSearchTerm : function(searchTerm) {
      if (searchTerm) {
        // Remove illegal characters
        searchTerm = searchTerm.replace(/[&()]/g, "");

        // Trim leading/trailing whitespace and replace multiple whitespace with a single blank
        searchTerm = searchTerm.replace(/^\s+/, "").replace(/\s+$/, "");
        searchTerm = searchTerm.replace(/\s+/g, " ");
      }
      return searchTerm;
    },

    clearQueryDelayTimer : function() {
      if (priv.queryDelayTimer != 0) {
        clearInterval(priv.queryDelayTimer);
        priv.queryDelayTimer = 0;
      }
    },

    queryCatalogAgain : function() {
      priv.clearQueryDelayTimer();
      priv.queryCatalog(priv.currentSearchTerm);
    },

    queryCatalog : function(searchTerm) {
      priv.historyValue = "catalog?page=" + priv.pageIndex;

      if (searchTerm && searchTerm != "") {
        priv.historyValue += "&searchTerm=" + priv.encodeUrlParam(searchTerm);
      }

      var queryText = "";
      var fields = null;

      // Code support searching specifc fields. Not currently used.
      /*
      if (!jQuery("#fieldAll").attr("checked")) {
        fields = new Array();
        for ( var key in priv.fields) {
          var inputId = priv.fields[key];
          if (jQuery("#" + inputId).attr("checked")) {
            fields.push(key);
          }
        }
      }
      */

      var filters = new Object();
      for ( var key in priv.filterFields) {
        var inputId = priv.filterFields[key].fieldId;
        var optionIndex = jQuery("#" + inputId).attr("selectedIndex");
        if (optionIndex > 0) {
          var options = jQuery("#" + inputId).attr("options");
          var value = options[optionIndex].value;
          filters[key] = value;
          priv.historyValue += "&" + consts.HISTORY_PREFIX_FILTER
              + key + "=" + priv.encodeUrlParam(value);
        }
      }
      priv.filters = filters;

      var sorts = new Object();
      for (var key in priv.sort) {
        if (priv.sort[key]) {
          sorts[key] = priv.sort[key];
          priv.historyValue += "&" + consts.HISTORY_PREFIX_SORT + key
              + "=" + priv.sort[key];
        }
      }

      priv.addingToHistory = true;
      priv.doQueryCatalog(priv.pageIndex, searchTerm, fields, filters,
          sorts);

      if(priv.useHistory) {
        jQuery.address.value(priv.historyValue);
      }
      priv.addingToHistory = false;
    },

    /*
     * pageIndex: page index, required, 1 based
     * searchTerm: string, required
     * fields: array, optional
     * filters: map like {publisher: "Marvel"}, optional
     * sorts: map like {publishDate: "asc"}, optional
     */
    doQueryCatalog : function(pageIndex, searchTerm, fields, filters, sorts) {
      if (priv.listAjaxRequest) {
        priv.listAjaxRequest.abort();
      }

      priv.currentSearchTerm = searchTerm;
      searchTerm = priv.formatSearchTerm(searchTerm);

      var fullQueryText = "store:\"" + priv.store + "\"+AND+visible:true";
      
      if (searchTerm) {
        var queryText = "";
        if (!fields) {
          for ( var key in priv.fields) {
            if (queryText != "") {
              queryText += "+OR+";
            }
            queryText += key + ":" + searchTerm;
          }
        } else {
          for ( var i in fields) {
            if (queryText != "") {
              queryText += "+OR+";
            }
            queryText += fields[i] + ":" + searchTerm;
          }
        }
        fullQueryText += "+AND+(" + queryText + ")"
      }

      var start = (pageIndex - 1) * priv.itemsPerPage;
      var queryUrl = priv.queryBaseUrl
          + "start=" + start
          + "&rows=" + priv.itemsPerPage
          + "&indent=on&fl=id,image,displayName,story,publishDate,credit_artist,credit_author&wt=json&json.nl=map&hl=false&facet=false";

      queryUrl += "&q=" + fullQueryText;
      if (filters) {
        for (var key in filters) {
          queryUrl += "&fq=" + key + ":\"" + filters[key].replace(/\"/g, "\\\"").replace(/&/g, "%26") + "\"";
        }
      }

      var sortClause = "";
      for (var key in sorts) {
        if (sorts[key]) {
          if (sortClause != "") {
            sortClause += ",";
          }
          sortClause += key + "%20" + sorts[key];
        }
      }

      if (sortClause == "") {
        sortClause = priv.defaultSortField + "%20desc";
      }
      if (!sorts[priv.secondSortField]) {
        sortClause += "," + priv.secondSortField + "%20asc";
      }
      queryUrl += "&sort=" + sortClause;

      jQuery("#queryLink").attr("href", queryUrl);

      priv.queryStart = (new Date()).getTime();

      priv.listAjaxRequest = jQuery.getJSON(queryUrl, priv.updateResultList);

      // Different query for facets
      var facetQueryUrl = priv.queryBaseUrl
          + "start=0&rows=1&facet.limit=10000"
          + "&indent=on&fl=id&wt=json&json.nl=map&hl=false&facet=true&facet.sort=index";
          facetQueryUrl += "&q=" + fullQueryText;
      
      for (var facetKey in priv.filterFields) {
        queryUrl = facetQueryUrl;
        if (filters) {
          for (var key in filters) {
            if (key != facetKey) {
              queryUrl += "&fq=" + key + ":\"" + priv.encodeUrlParam(filters[key]) + "\"";
            }
          }
        }
        queryUrl += "&facet.field=" + facetKey;
        jQuery.getJSON(queryUrl, priv.updateFacetCounts);
      }
      
     },

    toggleSortValue : function(key, sort) {
      if (sort == null || sort == "") {
        return priv.sortFields[key].defSort;
      }
      if (sort == "desc") {
        return "asc";
      }
      return "desc";
    },

    delayedQueryCatalogAgain : function() {
      priv.clearQueryDelayTimer();
      priv.queryDelayTimer = setInterval(priv.queryCatalogAgain,
          priv.queryDelayMsec);
    },

    setSortDirIndicators : function() {
      jQuery(".sortAscIcon").attr("src", priv.arrows.arrowAsc);
      jQuery(".sortDescIcon").attr("src", priv.arrows.arrowDesc);
      for (var key in priv.sort) {
        var inputId = priv.sortFields[key].tagId;
        if (priv.sort[key] == "asc") {
          jQuery("#" + inputId + " > .sortAscIcon").attr("src", priv.arrows.arrowAscRoll);
        } else if (priv.sort[key] == "desc") {
          jQuery("#" + inputId + " > .sortDescIcon").attr("src", priv.arrows.arrowDescRoll);
        }
        jQuery("#" + priv.sortDescriptionId).html(priv.sortDescriptionTexts[key][priv.sort[key]]);
      }
    },

    queryDetail : function(id) {
      var queryUrl = priv.queryBaseUrl
          + "q=id:"
          + id
          + "&start=0&rows=10&indent=on&fl=*&wt=json&json.nl=map";

      jQuery.getJSON(queryUrl, priv.updateDetailDiv);
    },
    
    openCatalog : function(hashValue) {
      var path = "/"
      var match = location.pathname.match(/(\/[a-z]*magnolia[a-z]*)\//i);
      if (match) {
        path = match[0];
      }
      location.href = path + priv.collectionPageUrl + hashValue;
    },
    
    postStats : function(action, value) {
      pageTracker._trackEvent("catalogue", action, value);
    },
    
    postWebtrendsStats : function(searchTerm, success) {
      if (!searchTerm) {
        searchTerm = "";
      }
      dcsMultiTrack('DCS.dcsuri', "/internalSearch", 'WT.ti', "internalSearch", 'WT.oss', priv.encodeUrlParam(searchTerm), 'WT.oss_r', success ? "1" : "0")
        _tag.dcsCleanUp('DCS.dcsuri', 'WT.ti', 'WT.oss', 'WT.oss_r');
    },
    
    encodeUrlParam : function(param) {
    	return encodeURI(param).replace(/&/g, "%26");
    },
    
    decodeUrlParam : function(param) {
    	return param.replace(/%26/g, "&");
    }
  };

  // Public methods
  return {
    queryCatalog : function() {
      priv.pageIndex = 1;

      var searchTerm = jQuery("#searchterm").attr("value");
      searchTerm = priv.cleanSearchTerm(searchTerm);
      jQuery("#searchterm").attr("value", searchTerm ? searchTerm : "");

      priv.queryCatalog(searchTerm);
      
      priv.postStats("search", priv.encodeUrlParam(searchTerm));
    },

    toggleSort : function(key) {
      var oldVal = priv.sort[key];
      priv.sort = {};
      priv.sort[key] = priv.toggleSortValue(key, oldVal);

      priv.setSortDirIndicators();

      priv.pageIndex = 1;
      priv.delayedQueryCatalogAgain();
      
      priv.postStats("sortBy_" + key, priv.sort[key]);
    },

    clearFilters : function() {
      priv.pageIndex = 1;

      jQuery("#filterDiv > select").attr("selectedIndex", 0);
      jQuery("#searchterm").attr("value", "");
      
      priv.sort = new Object();
      priv.sort[priv.defaultSortField] = "desc";
      priv.setSortDirIndicators();

      priv.queryCatalog("");

      priv.postStats("clearSearch", "");
    },

    applyFilter : function(id, value) {
      priv.pageIndex = 1;
      priv.delayedQueryCatalogAgain();
      priv.postStats("filter_" + id, value);
    },

    prevPage : function() {
      priv.pageIndex = Math.max(priv.pageIndex - 1, 1);
      priv.updatePageButtonTexts();
      priv.delayedQueryCatalogAgain();
      priv.postStats("pagination", "" + priv.pageIndex);
    },

    nextPage : function() {
      priv.pageIndex = Math.min(priv.pageIndex + 1, priv.maxPageIndex);
      priv.updatePageButtonTexts();
      priv.delayedQueryCatalogAgain();
      priv.postStats("pagination", "" + priv.pageIndex);
    },

    onHistoryChange : function(event) {
      if (priv.addingToHistory) {
        return;
      }
      var parameters = event.parameters;
      var searchTerm = parameters["searchTerm"];
      searchTerm = priv.cleanSearchTerm(searchTerm);
      jQuery("#searchterm").attr("value", searchTerm ? searchTerm : "");

      var pageIndex = parseInt(parameters["page"]);
      if (isNaN(pageIndex)) {
        priv.pageIndex = 1;
      } else {
        priv.pageIndex = Math.max(parseInt(parameters["page"]), 1);
      }

      var filters = new Object();
      var sorts = new Object();
      var foundSort = false;

      for (var key in event.parameters) {
        var pos = key.indexOf(consts.HISTORY_PREFIX_FILTER);
        if (pos == 0) {
          filters[key.substring(consts.HISTORY_PREFIX_FILTER.length)] = priv.decodeUrlParam(parameters[key]);
          if (window.console) console.log(parameters[key]);
        }
        pos = key.indexOf(consts.HISTORY_PREFIX_SORT);
        if (pos == 0) {
          sorts[key.substring(consts.HISTORY_PREFIX_SORT.length)] = priv.decodeUrlParam(parameters[key]);
          priv.sort = sorts;
          foundSort = true;
        }
      }
      if (!foundSort) {
        sorts[priv.defaultSortField] = "desc";
        priv.sort = sorts;
      }
      priv.filters = filters;
      priv.setSortDirIndicators();

      priv.doQueryCatalog(priv.pageIndex, searchTerm, null, filters, sorts);
      if (parameters['scrollToResult']) {
        priv.scrollToResult = true;
      }
    },

    onHistoryInit : function(event) {
      catalogManager.onHistoryChange(event);
    },

    initialize : function(queryBaseUrl, itemsPerPage) {
      priv.queryBaseUrl = queryBaseUrl;
      priv.itemsPerPage = itemsPerPage;
    },
    
    setUseHistory : function(booleanValue) {
      priv.useHistory = booleanValue;
    },    
    
    setFiltersFields : function(filterFields) {
      priv.filterFields = filterFields;
    },

    setSortFields : function(sortFields, defaultSortField, secondSortField, arrows, sortDescriptionId, sortDescriptionTexts) {
      priv.sortFields = sortFields;
      priv.defaultSortField = defaultSortField; 
      priv.secondSortField = secondSortField; 
      priv.arrows = arrows;
      priv.sortDescriptionId = sortDescriptionId;
      priv.sortDescriptionTexts = sortDescriptionTexts;
    },

    setThumbnailTemplate : function(thumbnailTemplate) {
      priv.thumbnailTemplate = thumbnailTemplate;
    },
    
    setPopupTemplate : function(popupTemplate) {
      priv.popupTemplate = popupTemplate;
    },
    
    setStore : function(store) {
      priv.store = store;
    },
    
    setCallbacks: function(mouseOver, mouseOut, showDetailDiv, showDetailPage) {
      priv.mouseOver = mouseOver;
      priv.mouseOut = mouseOut;
      priv.showDetailDiv = showDetailDiv;
      priv.showDetailPage = showDetailPage;
    },
    
    showPopup: function(id) {
      priv.updateDetailDiv(id);
    },
    
    openAndSearchCatalog : function(searchTerm) {
      priv.openCatalog("#/catalog?searchTerm=" + priv.encodeUrlParam(searchTerm) + "&scrollToResult=1");
      return null;
    },
    
    openAndFilterCatalog : function(filterKey, filterValue) {
      priv.openCatalog("#/catalog?filter:" + filterKey + "=" + priv.encodeUrlParam(filterValue) + "&scrollToResult=1");
      return null;
    }
  };
}();

var urlEncoder = function() {
  var priv = {
    // private method for UTF-8 encoding
    utf8Encode : function(string) {
      string = string.replace(/\r\n/g, "\n");
      var utftext = "";

      for ( var n = 0; n < string.length; n++) {

        var c = string.charCodeAt(n);

        if (c < 128) {
          utftext += String.fromCharCode(c);
        } else if ((c > 127) && (c < 2048)) {
          utftext += String.fromCharCode((c >> 6) | 192);
          utftext += String.fromCharCode((c & 63) | 128);
        } else {
          utftext += String.fromCharCode((c >> 12) | 224);
          utftext += String.fromCharCode(((c >> 6) & 63) | 128);
          utftext += String.fromCharCode((c & 63) | 128);
        }

      }

      return utftext;
    },

    // private method for UTF-8 decoding
    utf8Decode : function(utftext) {
      var string = "";
      var i = 0;
      var c = c1 = c2 = 0;

      while (i < utftext.length) {

        c = utftext.charCodeAt(i);

        if (c < 128) {
          string += String.fromCharCode(c);
          i++;
        } else if ((c > 191) && (c < 224)) {
          c2 = utftext.charCodeAt(i + 1);
          string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
          i += 2;
        } else {
          c2 = utftext.charCodeAt(i + 1);
          c3 = utftext.charCodeAt(i + 2);
          string += String.fromCharCode(((c & 15) << 12)
              | ((c2 & 63) << 6) | (c3 & 63));
          i += 3;
        }

      }

      return string;
    }
  };

  return {
    // public method for url encoding
    encode : function(string) {
      return escape(priv.utf8Encode(string));
    },

    // public method for url decoding
    decode : function(string) {
      return priv.utf8Decode(unescape(string));
    }
  };
}();


var urlParser = function() {
  var priv = {
    getParams : function(urlArg) {
      var eventObjStub = {};
      eventObjStub['parameters'] = {};

      urlArr = urlArg.split("\?");
      if(urlArr.length != 2) {
        return eventObjStub;
      }
      urlParamsArr = urlArr[1].split("\&");
      
      if(urlParamsArr.length == 0) {
        return eventObjStub; 
      }
      
      urlParamsKeyValue = {};
      for(var i = 0; i< urlParamsArr.length; i++) {
        var tempArr = urlParamsArr[i].split("=");
        if(tempArr.length == 2) {
          eventObjStub.parameters[urlEncoder.decode(tempArr[0])] = urlEncoder.decode(tempArr[1]);
        }
        
      }
      return eventObjStub;
    }
  };

  return {
    // public method for url encoding
    getParams : function(urlArg) {
      return priv.getParams(urlArg);
    }
  };
}();
