var Pagination = new Class({
  Implements:     Log,
  initialize:     function (elements, perPage, startPage, where, how)
  {
    // Assign the parameters for this pagination instance
    this.startPage    = startPage ? startPage : 1;
    this.perPage    = perPage ? perPage : 10;
    this.currentPage  = this.startPage;
    
    // Pull in the elements to paginate and work out how many pages
    // we'll need to display them, using this.perPage
    this.elements     = elements;
    this.pages      = Math.ceil(this.elements.length/this.perPage);
    
    // This is the div that'll contain the actually pagination links
    this.jumpers    = new Element('div',{
      'class':  'pagination'
    });

    // ...and this will be an array of each of the numeric pagination links
    this.pageNumbers  = new Array;

    // Create the text to describe the current pagination status
    // e.g. Showing 1-10 of 77 (page 1 of 8)
    this.statusText   = new Element('h3',{ 'class': 'pagination-status' });
    this.jumpers.adopt(this.statusText);

    // These will make up the containers for the page numbers
    ul = new Element('ol');
    li = new Element('li');

    lis = new Array;
    
    // Create the individual page number links
    for(x = 0; x < this.pages; x++)
    {
      this.pageNumbers[x] = new Element('a',{
        'html':   x+1,
        'href':   '#'
      });
      
      // If this link is for the current page, set it as 'active'
      lis[x] = li.clone().addClass('i'+(x+2));

      this.pageNumbers[x].addEvent('click', this.clicked.bind(this));
      lis[x].adopt(this.pageNumbers[x]);
    }
    
    // Create the previous page link
    this.previous = new Element('a',{
      'href':   '#',
      'html':   'back'
    });
    this.previous.addEvent('click', this.previousPage.bind(this));
    prev = li.clone().addClass('previous i1');
    prev.adopt(this.previous);

    // Create the next page link
    this.next = new Element('a',
    {
      'href':   '#',
      'html':   'next'
    });
    this.next.addEvent('click', this.nextPage.bind(this));

    next = li.clone().addClass('next i'+ (lis.length + 2));
    next.adopt(this.next);

    // Drop the pagination links and stuff in to the page
    this.jumpers.inject(where,how);

    // Adopt the list.
    ul.adopt(
      prev,
      lis,
      next
    );

    this.fx = new Fx.Tween(this.elements[0].getParent());
    this.jumpers.adopt(ul);
    this.updateStatus();
    this.showHideEntities(false,true);
  },
  
  // Bound to a the click event on a page number link
  clicked:      function (ev)
  {
    ev.preventDefault();
    // Set the current page based on the contents of the link
    if(ev.target.get('html').toInt() !== this.currentPage)
    {
      this.currentPage = ev.target.get('html').toInt();
      this.changePage(); // do the stuff with the shit
    }
  },

  // Update the current pagination status, e.g. Showing 1-10 of 77 (page 1 of 8)
  updateStatus:     function ()
  {
    this.statusText.set('html',
      'Showing '
      +(((this.currentPage-1)*this.perPage)+1).toString()+' - '
      +((this.currentPage*this.perPage) > this.elements.length ? this.elements.length : (this.currentPage*this.perPage))+' of '
      +this.elements.length.toString()
      +' (Page '+this.currentPage.toString()+' of '+this.pages.toString()+')'
    );
    this.highlightPage();
  },
  
  nextPage:       function (ev)
  {
    ev.preventDefault();
    if(this.currentPage < this.pages)
    {
      this.currentPage++;
      this.changePage();
    }
  },

  previousPage:     function (ev)
  {
    ev.preventDefault();
    if(this.currentPage > 1)
    {
      this.currentPage--;
      this.changePage();
    }
  },
  
  highlightPage:    function()
  {
    for(x = 0; x < this.pageNumbers.length; x++)
    {
      if(x + 1 == this.currentPage)
      {
        this.pageNumbers[x].getParent().addClass('active');
      }
      else
      {
        this.pageNumbers[x].getParent().removeClass('active');
      }
    }
  },
  
  changePage:     function (instant)
  {
    this.updateStatus();
    if(!Browser.Engine.trident && !instant)
    {
      fx = new Fx.Scroll(window);
      this.fx.start('opacity',1,0).chain(
        this.showHideEntities.bind(this)
      );
    }
    else
    {
      this.showHideEntities(false,instant);
    }
  },
  
  showHideEntities:   function (ev,instant)
  {
    for(x = 0; x < this.elements.length; x++)
    {
      if(x < ((this.currentPage-1)*this.perPage) || x >= ((this.currentPage)*this.perPage))
      {
        this.elements[x].setStyle('display','none');
      }
      else
      {
        this.elements[x].setStyle('display','block');
      }
    }
    // IE also seems to dislike the fade between pages. Not so good
    if(!Browser.Engine.trident && !instant)
    {
      this.fx.start('opacity',0,1);
    }
    window.scrollTo(0,(this.currentPage-1)*this.perPage);
  },
  
  setPageByNode:      function (node)
  {
    for(x = 0; x < this.elements.length; x++)
    {
      if(this.elements[x] === node)
      {
        this.currentPage = Math.ceil((x+1) / this.perPage);
        this.elements[x].addClass('selected');
        this.changePage(true);
      }
    }
  }
});

