(function($){
  
  function rebutton ( btn, callback, config ) {
    var b = $( '<a href="#" class="' + $.trim( btn.attr('class') ) + 
                '" title="' + $.trim( btn.attr('title') ) + '">' + 
                $.trim( btn.text() ) + '</a>' ).removeClass( config.disabled );
    btn.replaceWith( b.click( callback ) );
    return b;
  }
  
  function FMT ( str, obj ) {
    if ( !obj.length ) { 
      return str.replace( /%[sd]/g, obj );
    }
    var i = 0;
    return str.replace( /%[sd]/g, function () {
      return obj[ i++ ] || '';
    });
  }
  
  function CB ( callback, data, context, param1, param2, param3 ) {
    if ( $.isFunction(callback) ) {
      return callback.call( context || window, data, param1, param2, param3 );
    }
    else {
      return data;
    }
  }
    
  function Pagecontrol ( table, pager, config ) {
    // set the basic values
    this.table = table;
    this.pager = pager;
    this.config = config;
    this.responseCache = {};
    
    this.config.count = $( 'tbody tr', table ).length;
    
    this.btn = {
      first:    $( '.first',    pager[0] ),
      last:     $( '.last',     pager[0] ),
      prev:     $( '.prev',     pager[0] ),
      next:     $( '.next',     pager[0] ),
      status:   $( '.status',   pager[0] ),
      position: $( '.position', pager[0] )
    };
    // rewrite buttons
    var s = 'span:first, a:first';
    var self = this;
    var click = function ( e ) {
      // change the context of the event handler to controler instance
      return self.clickhandler( e, this ); 
    };
    
    this.btn.first_a = rebutton( this.btn.first.find( s ), click, config );
    this.btn.last_a  = rebutton( this.btn.last.find( s ),  click, config );
    this.btn.prev_a  = rebutton( this.btn.prev.find( s ),  click, config );
    this.btn.next_a  = rebutton( this.btn.next.find( s ),  click, config );
    
    this.buttons = [ this.btn.first_a, this.btn.last_a,
                     this.btn.prev_a, this.btn.next_a ];

    this.evalstate( this.buttons );

  }
  $.extend(Pagecontrol.prototype, {
    
    clickhandler: function ( e, elm ) {
      var link = $( elm );
      var self = this;
      var parent = link.parent();
      var btn = this.btn;
      var dest = this.config.offset;
      var lastpage = Math.floor( this.config.last / this.config.stepsize ) * this.config.stepsize;

      // never mind clicks on disabled buttons
      if ( link.hasClass( this.config.disabled ) ) { return false; }
      
      if ( parent[0] === btn.first[0] ) { // first
        dest = this.config.first || 0;
      }
      else if ( parent[0] === btn.last[0] ) { // last
        dest = lastpage || ( dest + this.config.stepsize );
      }
      else if ( parent[0] === btn.prev[0] ) { // prev
        dest -= this.config.stepsize;
      }
      else if ( parent[0] === btn.next[0] ) { // next
        dest += this.config.stepsize;
      }

      dest = Math.min( Math.max( dest, this.config.first ), lastpage );
      if ( dest != this.config.offset ) {
        
        // build url
        var dataurl = this.config.baseurl.replace( /%[ds]/g, dest );

        // lock controls
        this.disablebuttons( this.buttons );
        this.table.addClass( 'pager-loading' );
        
        // data cached?
        if ( this.responseCache[ dest ] ) {
          this.handleData( this.responseCache[ dest ] );
          this.evalstate( this.buttons );
          this.table.removeClass( 'pager-loading' );
        }
        else {
          // request data ...
          $.ajax({
            type: 'GET',
            url: dataurl,
            dataType: 'json',
            error: function () {
              // woops...
              CB( self.config.errorCallback, self.table, self );
              self.evalstate( self.buttons );
            },
            success: function ( ret ) {
              self.responseCache[ dest ] = CB( self.config.dataCallback, ret, self );
              self.handleData( ret );
            },
            complete: function () {
              // reevaluate controls
              self.evalstate( self.buttons );
              self.table.removeClass( 'pager-loading' );
            }
          });
        }
      }

      return false;
    },
    
    
    handleData: function ( ret ) {
      // correct any skewed values
      $.extend(this.config, {
         'last':   ret.meta.totalresults,  // lastpage = int( totalresults / stepsize )
         'count':  ret.meta.count,
         'first':  ret.meta.firstpage,
         'offset': ret.meta.startoffset
      });
      // redraw table
      var tb = $( '<tbody></tbody>' );
      $.each( ret.data, function ( i, a ) {
        tb.append( '<tr class="' + ( i % 2 ? 'odd' : 'even' ) + '">' +
                   '<td>' + a.join( '</td><td>' ) + '</td>' + '</tr>' )
      });
      
      this.table
        .find( 'tbody:first' ).replaceWith( tb ).end()
        .trigger( 'pager.page' );
    },
    
    evalstate: function ( btns ) {
      
      var btn = this.btn;
      var cfg = this.config;
      var off = cfg.offset;
      var dis = cfg.disabled;
      
      $.each(btns, function () {
        var parent = this.parent()
        if ( parent[0] === btn.first[0] || parent[0] === btn.prev[0] ) {
          // prev | first
          this.toggleClass( dis, ( off <= cfg.first ) );
        } 
        else if ( parent[0] === btn.last[0] || parent[0] === btn.next[0] ) {
          // next | last
          this.toggleClass( dis, ( off >= ( cfg.last - cfg.stepsize ) ) );
        }
      });
      
      btn.status.html( 
        CB( this.config.statusCallback,
          FMT( this.config.status, [ cfg.offset+1, cfg.offset + cfg.count, cfg.last ] ),
          this
        )
      );
      
    },
    
    disablebuttons: function ( btns ) {
      var dis = this.config.disabled;
      // btns is not a jquery object
      $.each(btns, function () { this.addClass( dis ); });
    }
    
  });


  $.pager = {
    config : {
      baseurl:  ( document.location + '' ).replace( /[?&]startoffset=[^?&]*/g, '' ) + '&startoffset=%d',
      stepsize: 20,
      first:    0,
      last:     9999,
      offset:   0,
      pager:    'pager',
      disabled: 'ui-state-disabled',
      status:   'Sýni %d - %d af %d',
      statusCallback: null,
      errorCallback:  null,
      dataCallback:   null
    }
  }

  $.fn.pager = function ( cfg ) {
    var config = $.extend( {}, $.pager.config, cfg );
    return this.each(function () {
      // only once per table atm. no reconfiguring yet
      var table = $( this );
      var tst = table.data('pager');
      if (!tst) {
        var pager = $( '.' + config.pager, this );
        table.data( 'pager', new Pagecontrol( table, pager, config ) );
      }
    });
  };

  
})(jQuery);

