CatalogChoice.Finder = {
  offset: 0,
  pageCount: 0,
  currentPage: null,
  selectedCatalogs: [],
  query: '',
  
  init: function(element) {
    var defaults = {
      strings: {
        next: 'Next',
        previous: 'Previous'
      },
      partials: {
        result: '<li id="catalog_%id%" class="%class%">' +
                '  <h4>%title%</h4>' +
                '  <div id="hidden_content_optout_%id%_catalogs" style="display: none;"></div><a href="#" onclick="new Ajax.Updater(\'hidden_content_optout_%id%_catalogs\', \'/catalogs/optout?catalog_id=%id%\', {asynchronous:true, evalScripts:true, method:\'get\', onComplete:function(request){RedBox.addHiddenContent(\'hidden_content_optout_%id%_catalogs\'); }, onLoading:function(request){RedBox.loading(); }}); return false;">Opt-out</a>' +
                '</li>'
      },
      elements: {
        searchField: 'input#search',
        basket:   '.basket',
        results1: '.search_results.left',
        results2: '.search_results.right',
        header:   '.results_header',
        footer:   '.results_footer',
        paginator: '.paginator',
        numbers:  '.numbers',
        offset:   '.offset',
        total:    '.total',
        length:   '.length'
      }
    }
    this.options = Object.extend(defaults, arguments[1] || {});
		this.element = $(element);
		
		this.elements = {
		  searchField: this.element.down(this.options.elements.searchField),
		  basket: this.element.down(this.options.elements.basket),
		  results1: this.element.down(this.options.elements.results1),
		  results2: this.element.down(this.options.elements.results2),
		  header: this.element.down(this.options.elements.header),
		  footer: this.element.down(this.options.elements.footer),
  		paginator: this.element.down(this.options.elements.paginator),
  		numbers: this.element.down(this.options.elements.numbers),
  		offset: this.element.down(this.options.elements.offset),
  		total: this.element.down(this.options.elements.total),
  		length: this.element.down(this.options.elements.length)
		}
  },
  
  // Reset the variables
  reset: function() {
    this.currentPage = null;
    this.offset = 0;
    this.pageCount = 0;
  },
  
  /**
   * Initiate a search, triggered by user input or pagination link.
   * If the temporary choices have not yet been displayd, show them as well.
   *
   * @param {String} prefix the prefixes to search for
   * @param {Number} start offset from pagination
   * @return {false}
   */
  find: function(query) {
    if (query == null || query == '') {
      this.reset();
      this.elements.searchField.removeClassName('throbbing');
      this.elements.results1.update('');
      this.elements.results2.update('');
    } else {
      this.elements.searchField.addClassName('throbbing');
      if (this.query != query) {
        this.reset();
      }
      this.query = query;
      new Ajax.Request('/finder/search', {
        method: 'post',
        parameters: {prefix: this.query, start: this.offset},
        onSuccess: this.renderResults.bind(this)
      });
    }
    return false;
  },
  
  // Navigate to a specific page if allowed
  gotoPage: function(page) {
    if (page > 0 && page <= this.pageCount) {
      this.currentPage = page;
      this.offset = this.options.limit * (page - 1);
      this.find(this.query);
    }
  },
  
  // Navigate to next page if allowed
  nextPage: function() {
    if (this.currentPage < this.pageCount)
      this.gotoPage(this.currentPage + 1);
  },
  
  // Navigate to previous page if allowed
  previousPage: function() {
    if (this.currentPage > 1)
      this.gotoPage(this.currentPage - 1);
  },
  
  // Evaluate the response, construct the HTML and update the page elements
  renderResults: function(transport) {
    var result = this._parseResponse(transport.responseText)
    
    var output = [];
    $A(result.catalogs).each(function(catalog) {
      className = '';
      if (this.selectedCatalogs.indexOf(catalog.id) > -1) { className = 'selected' }
      output.push(this.options.partials['result'].gsub('%title%', catalog.title.truncate()).
                                                  gsub('%id%', catalog.id).
                                                  gsub('%class%', className));
    }.bind(this));
    
    this.elements.results1.update(output.slice(0, this.options.limit / 2).join("\n"));
    this.elements.results2.update(output.slice(this.options.limit / 2, output.length).join("\n"));
    this.renderPaginator(result);
    this.elements.searchField.removeClassName('throbbing');
    introSwitcher.fixDimensions();
  },
  
  // Construct and render the paginator
  renderPaginator: function(result) {
    this.pageCount = Math.round(result.total / this.options.limit) || 1;
    this.currentPage = result.offset == 0 ? 1 : Math.floor(result.offset / this.options.limit) + 1;
    
    var length = result.total < (this.options.limit + result.offset) ? result.total : (this.options.limit + result.offset);
    
    // Previous link
    var output = '<li class="prev">';
    if (this.currentPage == 1)
      output += '<span>&laquo; ' + this.options.strings['previous'] + '</span>';
    else
      output += '<a href="#" onclick="CatalogChoice.Finder.previousPage();return false;">&laquo; ' + this.options.strings['previous'] + '</a>';
    output += '</li>';
    
    // Numbered links
    for (var i = 1; i <= this.pageCount; i++) {
      if (i == this.currentPage) {
        output += '<li class="current"><span>' + i + '</span></li>';
      } else {
        output += '<li><a href="#" onclick="CatalogChoice.Finder.gotoPage(' + i + ');return false;">' + i + '</a></li>';
      }
    }
    
    // Next link
    output += '<li class="next">';
    if (this.currentPage == this.pageCount)
      output += '<span>' + this.options.strings['next'] + ' &raquo;</span>';
    else
      output += '<a href="#" onclick="CatalogChoice.Finder.nextPage();return false;">' + this.options.strings['next'] + ' &raquo;</a>';
    output += '</li>';
    
    // Update page elements
    this.elements.offset.update(result.offset + 1);
    this.elements.total.update(result.total);
    this.elements.length.update(length);
    this.elements.paginator.update(output);
  },
  
  /**
   * Change the selection status of catalog from Unselected to Selected or vice
   * versa. It calls either ChoiceController#create or ChoiceController#destroy2
   * to move the catalog to/from the list of choices and changes the display
   * string of originating DOM element as well the css of the DOM element catalog_%id%.
   *
   * @param {Object} anchor the DOM element clicked to trigger this action
   * @param {Number} id the numerical id of the catalog
   * @return {false}
   */
  addToOrRemoveFromBasket: function(id) {
    var catalog = $('catalog_' + id);
    if (this.selectedCatalogs.indexOf(id) > -1) {
      action = 'destroy2';
      if (catalog) catalog.removeClassName('selected');
      new Effect.Fade(this.elements.basket.down('#choice_' + id), {duration: 0.25});
      this.selectedCatalogs = this.selectedCatalogs.without(id);
      new Ajax.Request('/choice/' + action + '/' + id, {asynchronous:true, evalScripts:true});
    } else {
      this._animateAddToBasket(catalog);
      catalog.addClassName('selected');
      action = 'create';
      this.selectedCatalogs.push(id);
      new Ajax.Updater('paperless_basket', '/choice/' + action + '/' + id, {asynchronous:true, evalScripts:true});
    }
  },
  
  _animateAddToBasket: function(catalog) {
    var offset = Position.positionedOffset(catalog);
    var left = offset[0];
    var top = offset[1];
    // Animate transition to the basket
    var _clone = catalog.cloneNode(true);
    _clone.addClassName('ghost');
    Position.absolutize(catalog);
    catalog.parentNode.insertBefore(_clone, catalog);
    Position.relativize(catalog);
    Position.absolutize(_clone);
    new Effect.Parallel([
      new Effect.Opacity(_clone, {from:1.0, to:0.0, sync:true}),
      new Effect.MoveBy(_clone, 120 - top, 550 - left, {sync:true})
    ], { duration: 0.5, afterFinish: function() { _clone.remove(); }} );
  },
  
  _parseResponse: function(response) {
    var data = response.split("\n");
    
    var pagination = data[0].split(",");
    var result = {
      total: parseInt(pagination[0]),
      offset: parseInt(pagination[1]),
      catalogs: []
    }
    
    for (i = 1; i < data.length; i++) {
      result.catalogs.push({
        id: data[i].split(',')[0],
        title: data[i].split(',')[1]
      });
    }
    return result;
  }
}