(function($){
	
	$.Table = function(el, url, default_filter, default_sort, default_sort_dir, callback, multiple_filters, pp, options){
		// To avoid scope issues, use 'base' instead of 'this'
		// to reference this class from internal events and functions.
		var base = this;
		
		// Access to jQuery and DOM versions of element
		base.$el = $(el);
		base.el = el; 
		
		// Add a reverse reference to the DOM object
		base.$el.data("Table", base);
		
		base.init = function(){
			base.options = $.extend({},$.Table.defaultOptions, options);
			
			if(default_filter == undefined || default_filter == null) default_filter = '';
			if(default_sort == undefined || default_sort == null) default_sort = '';
			if(default_sort_dir == undefined || default_sort_dir == null) default_sort_dir = 'A';
			if(pp == undefined || pp == null) pp = '10';
			
			if(multiple_filters == undefined || multiple_filters == null) multiple_filters = false;
			
			base.callback = callback
			
			base.active_request = null;
			
			base.url = url;
			base.sort = default_sort;
			base.sort_dir = default_sort_dir;
			base.filter = default_filter;
			base.multiple_filters = multiple_filters;
			base.page = 0;
			base.pages = 1;
			base.opp = parseInt(pp);
			base.pp = base.opp;
			
			base.pfirst = base.$el.find('.data-pagination-first');
			base.pprev = base.$el.find('.data-pagination-prev');
			base.pnext = base.$el.find('.data-pagination-next');
			base.plast = base.$el.find('.data-pagination-last');
			
			base.ppages = base.$el.find('.data-pagination-pages');
			
			base.sorts = base.$el.find('.data-table-col');
			base.filters = base.$el.find('.data-table-filter');
			base.pps = base.$el.find('.data-table-pp');
			
			base.filterz = new Object();
			
			base.data_table = base.$el.find(base.options.data_holder); 
			base.table_tbody = base.$el.find(base.options.data_selector);
			
			base.sorts.each(function(){
				this.id = $(this).attr('id');
				this.name = this.id.replace(base.$el.attr('id')+'-col-', '');
				$('#' + this.id).click(function(){
					if(base.sort == this.name){
						if(base.sort_dir == 'A'){
							base.sort_dir = 'D';
						}else{
							base.sort_dir = 'A';
						}
					}else{
						base.sort = this.name;
						base.sort_dir = 'A';
					}
					base.reload();
				});
			});
			
			base.pps.each(function(){
				$this = $(this);
				var id = $this.attr('id');
				var tpp = parseInt(id.replace(base.$el.attr('id')+'-pp-', ''));
				
				if(base.opp == tpp){
					$(this).addClass('active');
				}
				
				$this.click(function(){
					base.pps.removeClass('active');
					$(this).addClass('active');
					base.pp = tpp;
					base.reload();
				});
			});
			
			base.filters.each(function(){
				this.id = $(this).attr('id');
				this.name = this.id.replace(base.$el.attr('id')+'-filter-', '');
				base.filterz[this.name] = false;
				this.onclk = function(id, name){
					$('#' + id).parent().click(function(){
						if(multiple_filters){
							if(!base.filterz[name]){
								base.filterz[name] = true;
							}else{
								base.filterz[name] = false;
							}
							base.filter = '';
							for(var fltr in base.filterz){
								if(base.filterz[fltr]){
									base.filter += fltr + ';';
								}
							}
						}else{
							for(var fltr in base.filterz){
								if(fltr == name){
									base.filterz[fltr] = true;
								}else{
									base.filterz[fltr] = false;
								}
							}
							base.filter = name;
						}
						base.reload();
					});
				};
				this.onclk(this.id, this.name)
			});
			
			base.filterz[base.filter] = true;
			
			base.pfirst.click(function(){
				if(base.page > 0){
					base.page = 0;
					base.reload();
				}
			});
			
			base.pprev.click(function(){
				if(base.page > 0){
					base.page--;
					base.reload();
				}
			});
			
			base.pnext.click(function(){
				if(base.page < base.pages - 1){
					base.page++;
					base.reload();
				}
			});
			
			base.plast.click(function(){
				if(base.page < base.pages - 1){
					base.page = base.pages - 1;
					base.reload();
				}
			});
			
			base.ppages.click(function(){
				base.ppages.empty();
				var input = $('<input type="text" />');
				base.ppages.append(input);
				base.ppages.append(' / ' + base.pages);
				input.focus();
				input.keypress(function(e){
					if(e.which == 13){
						try {
							var pg = parseInt($(this).val()) - 1;
							if(pg >= 0){
								base.page = pg;
								base.reload();
							}
						}catch(err){}
					}
				});
				input.blur(function(){
					base.reload_pages();
				});
			});
			
			base.reload();
		}
		
		base.reload_all = function(){
			base.reload_pages();
			base.reload_sorts();
			base.reload_filters();
		}
		
		base.reload = function(){
			base.data_table.addClass('data-table-loading');
			
			base.reload_all()
			
			var params = {};
			if(base.sort != ''){
				params['sort'] = base.sort;
				params['sort_dir'] = base.sort_dir;
			}
			
			if(base.filter != ''){
				params['filter'] = base.filter;
			}
			if(base.page != 0){
				params['page'] = base.page;
			}
			
			params['pp'] = base.pp.toString();
            
            params['cache'] = new Date().getTime();
			
			if(base.active_request){
				base.active_request.abort();
			}
			
			base.active_request = $.getJSON(base.url, params, function(data){
				base.active_request = null;
				
				base.page = data.pagination.page;
				base.pages = data.pagination.pages;
				if(data.sort){
					base.sort = data.sort;
				}
				if(data.sort_dir){
					base.sort_dir = data.sort_dir;
				}
				if(data.filter){
					base.filter = data.filter;
				}
				
				base.reload_all();
				
				base.table_tbody.html(data.table_data);
				
				base.data_table.removeClass('data-table-loading');
				
				if(base.callback != undefined && base.callback != null){
					base.callback(base);
				}
			});
		}
		
		base.reload_pages = function(){
			base.pfirst.removeClass('disabled');
			base.pprev.removeClass('disabled');
			base.pnext.removeClass('disabled');
			base.plast.removeClass('disabled');
			
			if(base.page == 0){
				base.pfirst.addClass('disabled');
				base.pprev.addClass('disabled');
			}
			if(base.page == base.pages - 1){
				base.pnext.addClass('disabled');
				base.plast.addClass('disabled');
			}
			
			var p = base.page + 1;
			var ps = base.pages;
			base.ppages.html(p + ' / ' + ps);
		}
		
		base.reload_sorts = function(){
			if(base.sort != ''){
				base.sorts.each(function(){
					$(this).removeClass('asc');
					$(this).removeClass('desc');
				})
				if(base.sort_dir == 'D'){
					aclass = 'desc';
				}else{
					aclass = 'asc' 
				}
				base.sort_to_el(base.sort).addClass(aclass)
			}
		}
		
		base.reload_filters = function(){
			for(var fltr in base.filterz){
				base.filterz[fltr] = false;
			}
			splt = base.filter.split(';');
			for(var fltr in splt){
				if(base.filterz[splt[fltr]] != null && base.filterz[splt[fltr]] != undefined){
					base.filterz[splt[fltr]] = true;
				}
			}
			for(var fltr in base.filterz){
				base.filter_to_el(fltr).parent().removeClass('enabled');
				if(base.filterz[fltr]){
					base.filter_to_el(fltr).parent().addClass('enabled');
				}
			}
		}
		
		base.filter_to_el = function(filter){
			return base.$el.find('#' + base.$el.attr('id')+'-filter-' + filter)
		}
		
		base.sort_to_el = function(sort){
			return base.$el.find('#' + base.$el.attr('id')+'-col-' + sort)
		}
		
		base.init();
	}

	
	$.Table.defaultOptions = {
		data_selector: 'table tbody',
		data_holder: '.data-table'
	}

	$.fn.table = function(url, default_filter, default_sort, default_sort_dir, callback, multiple_filters, pp, options){
		return this.each(function(){
			(new $.Table(this, url, default_filter, default_sort, default_sort_dir, callback, multiple_filters, pp, options));
		});
	}

})(jQuery);