/*jslint browser: true, unparam: true, sloppy: true, nomen: true, indent: 2 */

/*globals AZCAT,YAHOO */

AZCAT.search = (function () {
  // Shared private functions
  function _showLoader() {
    var loader = document.getElementById(this._loaderEl);
    if (loader) {
      YAHOO.util.Dom.setStyle(loader, "display", "block");
    }
  }

  function _hideLoader() {
    var loader = document.getElementById(this._loaderEl);
    if (loader) {
      YAHOO.util.Dom.setStyle(loader, "display", "none");
    }
  }

  function _showResultsEl() {
    var resultsField = document.getElementById(this._resultsField);
    YAHOO.util.Dom.setStyle(resultsField, "visibility", "visible");
  }

  function _hideResultsEl() {
    var resultsField = document.getElementById(this._resultsField);
    YAHOO.util.Dom.setStyle(resultsField, "visibility", "hidden");
  }

  function _getForm() {
    return document.getElementById(this._searchForm);
  }

  function _updateFromServerCallback(o) {
    var fadeIn, resultsField;
    this._hideLoader();
    if (this._fadeOut.isAnimated) {
      this._fadeOut.stop();
    }
    this._showResultsEl();
    resultsField = document.getElementById(this._resultsField);
    this.dataReturnEvent.fire({"results" : o, "el" : resultsField, "formEl" : this._searchForm});
    fadeIn = new YAHOO.util.Anim(this._resultsField, {opacity: {to: 1}}, 0.3);
    fadeIn.animate();
  }

  function _updateFromServer() {
    var query, x;
    this._fadeOut = new YAHOO.util.Anim(this._resultsField, {opacity: {to: 0}}, 0.3);
    this._showLoader();
    this._fadeOut.onComplete.subscribe(this._hideResultsEl, this, true);
    this._fadeOut.animate();
    YAHOO.util.Connect.setForm(this._getForm());
    query = "page:int=" + this.getCurrentPage();
    if (!this._rowsPerPageInForm) {
      query += "&" + this._rowsPerPageName + ":int=" + this.getRowsPerPage();
    }
    for (x in this._submitVars) {
      if (this._submitVars.hasOwnProperty(x)) {
        query += ("&" + x + "=" + this._submitVars[x]);
      }
    }
    YAHOO.util.Connect._sFormData += ("&" + query);
    YAHOO.util.Connect.asyncRequest("POST", this._requestURL, {success : this._updateFromServerCallback, scope : this});
    YAHOO.util.Connect.resetFormState();
  }

  // Shared public functions
  function getCurrentPage() {
    return this._currentPage;
  }

  function setCurrentPage(page, fireChange) {
    var pag;
    this._currentPage = page;
    pag = this.getPaginator();
    if (pag) {
      pag.setPage(page, !fireChange);
    }
  }

  function submitSearch(e) {
    var el = YAHOO.util.Event.getTarget(e);
    if (el.type.toLowerCase() === "submit") {
      YAHOO.util.Event.stopEvent(e);
    }
    this.setCurrentPage(1);
    this.beforeSearchEvent.fire({"el" : el, "formEl" : this._searchForm});
    this._updateFromServer();
  }

  function resetSearch() {
    var searchField;
    if (!this._searchField) {
      return;
    }
    searchField = document.getElementById(this._searchField);
    if (searchField) {
      searchField.value = "";
      this.setCurrentPage(1);
      this._updateFromServer();
    } else {
      throw {"name" : "Unknown Element",
             "message" : "Unable to find field " + this._searchField};
    }
  }

  function searchFieldEnter(e) {
    if ((e.keyCode || e.which) === 13) {
      YAHOO.util.Event.stopEvent(e);
      this.setCurrentPage(1);
      this._updateFromServer();
    }
  }

  function getPaginator() {
    return this._paginator;
  }

  function setPaginator(pag) {
    this._paginator = pag;
  }

  function showPaginator() {
    var els, x, xLen;
    els = this._paginator.getContainerNodes();
    for (x = 0, xLen = els.length; x < xLen; x += 1) {
      YAHOO.util.Dom.setStyle(els[x], "visibility", "visible");
    }
  }

  function hidePaginator() {
    var els, x, xLen;
    els = this._paginator.getContainerNodes();
    for (x = 0, xLen = els.length; x < xLen; x += 1) {
      YAHOO.util.Dom.setStyle(els[x], "visibility", "hidden");
    }
  }

  function getTotalRecords() {
    return this._totalRecords;
  }

  function setTotalRecords(num, fireChange) {
    var pag;
    this._totalRecords = num;
    pag = this.getPaginator();
    if (pag) {
      pag.set('totalRecords', num);
      if (num === 0) {
        this.hidePaginator();
      } else {
        this.showPaginator();
      }
      if (fireChange) {
        pag.setTotalRecords(num);
      }
    }
  }

  function getRowsPerPage() {
    return this.getPaginator().get('rowsPerPage');
  }

  function setRowsPerPage(num, fireChange) {
    var pag;
    this._rowsPerPage = num;
    pag = this.getPaginator();
    if (pag) {
      pag.set('rowsPerPage', num);
      if (fireChange) {
        pag.setRowsPerPage(num);
      }
    }
  }

  function handlePagination(newState) {
    var pag;
    this.setCurrentPage(newState.page);
    pag = this.getPaginator();
    pag.setState(newState);
    this._updateFromServer();
  }

  return function (config) {
    // My resources, this instance
    // Private varables, actual
    var pagConfig, paginator, configItem, el, x, xLen, eventType,
      that = {//Private varables, by convention not actually hidden
        _currentPage : 1,
        _rowsPerPage : config.rowsPerPage,
        _rowsPerPageName : config.rowsPerPageName || "rows_per_page",
        _rowsPerPageInForm : config.rowsPerPageInForm || false,
        _totalRecords : config.totalRecords || 0,
        _loaderEl: config.loader,
        _requestURL : config.requestURL,
        _submitVars : config.submitVars || {},
        _searchForm : config.searchForm,
        _searchField : config.searchField,
        _submitFields : config.submitFields,
        _resetFields : config.resetFields,
        _resultsField : config.resultsField,
        _paginator : null,
        _fadeOut : null,

        //Private Methods
        _showLoader : _showLoader,
        _hideLoader : _hideLoader,
        _showResultsEl : _showResultsEl,
        _hideResultsEl : _hideResultsEl,
        _getForm : _getForm,
        _updateFromServer : _updateFromServer,
        _updateFromServerCallback : _updateFromServerCallback,

        // Public methods
        getCurrentPage : getCurrentPage,
        setCurrentPage : setCurrentPage,
        submitSearch : submitSearch,
        resetSearch : resetSearch,
        searchFieldEnter : searchFieldEnter,
        getPaginator : getPaginator,
        setPaginator : setPaginator,
        showPaginator : showPaginator,
        hidePaginator : hidePaginator,
        getTotalRecords : getTotalRecords,
        setTotalRecords : setTotalRecords,
        getRowsPerPage : getRowsPerPage,
        setRowsPerPage : setRowsPerPage,
        handlePagination : handlePagination,

        // Public Variables

        // Public Events
        dataReturnEvent : new YAHOO.util.CustomEvent("dataReturnEvent", this),
        beforeSearchEvent : new YAHOO.util.CustomEvent("beforeSearchEvent", this)
      };

    // Init

    // Setup Paginator
    if (config.paginatorEls && config.paginatorEls.length) {
      pagConfig = {containers : config.paginatorEls,
                   rowsPerPage : config.rowsPerPage,
                   totalRecords : config.totalRecords};
      if (config.rowsPerPageOptions) {
        pagConfig.rowsPerPageOptions = config.rowsPerPageOptions;
      }
      if (config.paginatorTemplate) {
        pagConfig.template = config.paginatorTemplate;
      }
      paginator = new YAHOO.widget.Paginator(pagConfig);
      that.setPaginator(paginator);
      if (config.pagConfig) {
        for (configItem in config.pagConfig) {
          if (config.pagConfig.hasOwnProperty(configItem)) {
            paginator.setAttributeConfig(configItem, {value : config.pagConfig[configItem]});
          }
        }
      }
      paginator.subscribe('changeRequest', handlePagination, that, true);
      paginator.render();
      if (that._totalRecords === 0) {
        that.hidePaginator();
      }
    }
    // Setup Search Field
    if (that._searchField) {
      el = document.getElementById(that._searchField);
      if (el) {
        YAHOO.util.Event.on(el, "keydown", that.searchFieldEnter, that, true);
      }
    }
    // Setup Submit Fields
    if (that._submitFields) {
      if (!YAHOO.lang.isArray(that._submitFields)) {
        that._submitFields = [that._submitFields];
      }
      for (x = 0, xLen = that._submitFields.length; x < xLen; x += 1) {
        if (YAHOO.lang.isString(that._submitFields[x])) {
          el = document.getElementById(that._submitFields[x]);
        } else {
          el = that._submitFields[x];
        }
        if (!el) {
          continue;
        }
        switch (el.tagName.toLowerCase()) {
        case "select":
          eventType = "change";
          break;

        case "input":
          switch (el.type.toLowerCase()) {
          case "submit":
          case "radio":
          case "checkbox":
            eventType = "click";
            break;

          }
          break;
        }
        if (!eventType) {
          throw {"name" : "Unknown Event For Element",
                 "message" : "Unable to determine what type of event to subscribe to field " + that._submitFields[x]};
        }
        YAHOO.util.Event.on(el, eventType, that.submitSearch, that, true);
      }
    }
    // Setup Reset Fields
    if (that._resetFields) {
      if (!YAHOO.lang.isArray(that._resetFields)) {
        that._resetFields = [that._resetFields];
      }
      for (x = 0, xLen = that._resetFields.length; x < xLen; x += 1) {
        if (YAHOO.lang.isString(that._resetFields[x])) {
          el = document.getElementById(that._resetFields[x]);
        } else {
          el = that._resetFields[x];
        }
        YAHOO.util.Event.on(el, "click", that.resetSearch, that, true);
      }
    }

    return that;
  };
}());





(function () {

  var Paginator = YAHOO.widget.Paginator,
    l         = YAHOO.lang;


/**
 * ui Component to generate the page links
 *
 * @namespace YAHOO.widget.Paginator.ui
 * @class PageLinks
 * @for YAHOO.widget.Paginator
 *
 * @constructor
 * @param p {Pagintor} Paginator instance to attach to
 */
  Paginator.ui.PageLinksSeparated = function (p) {
    this.paginator = p;

    p.subscribe('recordOffsetChange', this.update, this, true);
    p.subscribe('rowsPerPageChange', this.update, this, true);
    p.subscribe('totalRecordsChange', this.update, this, true);
    p.subscribe('pageLinksChange',   this.rebuild, this, true);
    p.subscribe('pageLinkClassChange', this.rebuild, this, true);
    p.subscribe('currentPageClassChange', this.rebuild, this, true);
    p.subscribe('destroy', this.destroy, this, true);

    //TODO: Make this work
    p.subscribe('pageLinksContainerClassChange', this.rebuild, this, true);
  };

/**
 * Decorates Paginator instances with new attributes. Called during
 * Paginator instantiation.
 * @method init
 * @param p {Paginator} Paginator instance to decorate
 * @static
 */
  Paginator.ui.PageLinksSeparated.init = function (p) {

    /**
     * Separator.
     * @attribute pageSeparator
     * @default ' | '
     */
    p.setAttributeConfig('pageSeparator', {
      value : ' | ',
      validator : l.isString
    });

    /**
     * CSS class assigned to each page link/span.
     * @attribute pageLinkClass
     * @default 'yui-pg-page'
     */
    p.setAttributeConfig('pageLinkClass', {
      value : 'yui-pg-page',
      validator : l.isString
    });

    /**
     * CSS class assigned to the current page span.
     * @attribute currentPageClass
     * @default 'yui-pg-current-page'
     */
    p.setAttributeConfig('currentPageClass', {
      value : 'yui-pg-current-page',
      validator : l.isString
    });

    /**
     * CSS class assigned to the span containing the page links.
     * @attribute pageLinksContainerClass
     * @default 'yui-pg-pages'
     */
    p.setAttributeConfig('pageLinksContainerClass', {
      value : 'yui-pg-pages',
      validator : l.isString
    });

    /**
     * Maximum number of page links to display at one time.
     * @attribute pageLinks
     * @default 10
     */
    p.setAttributeConfig('pageLinks', {
      value : 10,
      validator : Paginator.isNumeric
    });

    /**
     * Function used generate the innerHTML for each page link/span.  The
     * function receives as parameters the page number and a reference to the
     * paginator object.
     * @attribute pageLabelBuilder
     * @default function (page, paginator) { return page; }
     */
    p.setAttributeConfig('pageLabelBuilder', {
      value : function (page, paginator) { return page; },
      validator : l.isFunction
    });
  };

/**
 * Calculates start and end page numbers given a current page, attempting
 * to keep the current page in the middle
 * @static
 * @method calculateRange
 * @param {int} currentPage  The current page
 * @param {int} totalPages   (optional) Maximum number of pages
 * @param {int} numPages     (optional) Preferred number of pages in range
 * @return {Array} [start_page_number, end_page_number]
 */
  Paginator.ui.PageLinksSeparated.calculateRange = function (currentPage, totalPages, numPages) {
    var UNLIMITED = Paginator.VALUE_UNLIMITED,
      start,
      end,
      delta;

    // Either has no pages, or unlimited pages.  Show none.
    if (!currentPage || numPages === 0 || totalPages === 0 ||
        (totalPages === UNLIMITED && numPages === UNLIMITED)) {
      return [0, -1];
    }

    // Limit requested pageLinks if there are fewer totalPages
    if (totalPages !== UNLIMITED) {
      numPages = numPages === UNLIMITED ?
          totalPages :
          Math.min(numPages, totalPages);
    }

    // Determine start and end, trying to keep current in the middle
    start = Math.max(1, Math.ceil(currentPage - (numPages / 2)));
    if (totalPages === UNLIMITED) {
      end = start + numPages - 1;
    } else {
      end = Math.min(totalPages, start + numPages - 1);
    }

    // Adjust the start index when approaching the last page
    delta = numPages - (end - start + 1);
    start = Math.max(1, start - delta);

    return [start, end];
  };


  Paginator.ui.PageLinksSeparated.prototype = {

    /**
     * Current page
     * @property current
     * @type number
     * @private
     */
    current     : 0,

    /**
     * Span node containing the page links
     * @property container
     * @type HTMLElement
     * @private
     */
    container   : null,


    /**
     * Generate the nodes and return the container node containing page links
     * appropriate to the current pagination state.
     * @method render
     * @param id_base {string} used to create unique ids for generated nodes
     * @return {HTMLElement}
     */
    render : function (id_base) {
      var p = this.paginator;

      // Set up container
      this.container = document.createElement('span');
      this.container.id        = id_base + '-pages';
      this.container.className = p.get('pageLinksContainerClass');
      YAHOO.util.Event.on(this.container, 'click', this.onClick, this, true);

      // Call update, flagging a need to rebuild
      this.update({newValue : null, rebuild : true});

      return this.container;
    },

    /**
     * Update the links if appropriate
     * @method update
     * @param e {CustomEvent} The calling change event
     */
    update : function (e) {
      if (e && e.prevValue === e.newValue) {
        return;
      }

      var p           = this.paginator,
        currentPage = p.getCurrentPage(),
        labelBuilder,
        range,
        start,
        end,
        content,
        pageSeparator,
        linkTemplate,
        i;

      // Replace content if there's been a change
      if (this.current !== currentPage || !currentPage || e.rebuild) {
        labelBuilder = p.get('pageLabelBuilder');
        range        = Paginator.ui.PageLinks.calculateRange(
          currentPage,
          p.getTotalPages(),
          p.get('pageLinks')
        );
        start        = range[0];
        end          = range[1];
        content      = '';
        pageSeparator = p.get('pageSeparator');

        linkTemplate =
          '<a href="#" class="' +
          p.get('pageLinkClass') +
          '" page="';
        for (i = start; i <= end; i += 1) {
          if (i === currentPage) {
            content +=
              '<span class="' +
              p.get('currentPageClass') + ' ' +
              p.get('pageLinkClass') +
              '">' +
              labelBuilder(i, p) +
              '</span>';
          } else {
            content +=
              linkTemplate + i + '">' + labelBuilder(i, p) + '</a>';
          }
          if (i !== end) {
            content += pageSeparator;
          }
        }

        this.container.innerHTML = content;
      }
    },

    /**
     * Force a rebuild of the page links.
     * @method rebuild
     * @param e {CustomEvent} The calling change event
     */
    rebuild     : function (e) {
      e.rebuild = true;
      this.update(e);
    },

    /**
     * Removes the page links container node and clears event listeners
     * @method destroy
     * @private
     */
    destroy : function () {
      YAHOO.util.Event.purgeElement(this.container, true);
      this.container.parentNode.removeChild(this.container);
      this.container = null;
    },

    /**
     * Listener for the container's onclick event.  Looks for qualifying link
     * clicks, and pulls the page number from the link's page attribute.
     * Sends link's page attribute to the Paginator's setPage method.
     * @method onClick
     * @param e {DOMEvent} The click event
     */
    onClick : function (e) {
      var t = YAHOO.util.Event.getTarget(e);
      if (t && YAHOO.util.Dom.hasClass(t,
            this.paginator.get('pageLinkClass'))) {

        YAHOO.util.Event.stopEvent(e);

        this.paginator.setPage(parseInt(t.getAttribute('page'), 10));
      }
    }

  };

}());

