/**
 * @class HD.util.Paging
 * @description The purpose of Paging is to provide a mechanism to paginate a set of data.
 * @constructor
 * @property {object} config
 *                 The configuration for the widget
 * @property {array}  config.containers
 *                 A list of elements that will contain pagers.
 * @property {number} config.defaultPageSize
 *                 The default size for a page of media.
 * @property {boolean} config.showPageSizeSwitch
 *                 Whether to provide the ability to change the page size.
 * @property {number} config.startingPage
 *                 Which page of data to start at (first page is 1).
 * @property {number}  config.viewAllMax
 *                 The maximum page size (when using config.showPageSizeSwitch=true).
 */
HD.util.Paging = function(config) {
	/** 
	 * Model for paging.
	 * @type HD.util.Paging.Model
	 */
	this.model = new HD.util.Paging.Model(config);
	/** 
	 * View for paging.
	 * @type HD.util.Paging.Model
	 */
	this.view = new HD.util.Paging.View(this.model);
	/** 
	 * Controller for paging.
	 * @type HD.util.Paging.Model
	 */
	this.controller = new HD.util.Paging.Controller(this.model, this.view);
	this.init(config);
};

HD.util.Paging.prototype = {
	/**
	 * Initializes paging (overrides default values).
	 * @param {object} config
	 *                 The configuration to use to initialize paging.
	 */
	init : function(config) {
		this.model.startingPage = config.startingPage;
		this.view.containers = config.containers;
		this.model.viewAllMax = config.viewAllMax;
		this.model.defaultPageSize = config.defaultPageSize;
		this.model.showPageSizeSwitch = config.showPageSizeSwitch;
	}
};

/**
 * @class HD.util.Paging.Model
 * @description The paging model provides a storage mechanism for paging state.
 * @constructor
 * @extends HD.util.Observable
 * @property {object} config
 *                 The configuration for the model
 */
HD.util.Paging.Model = function(config) {
	this.config = config;
	
	/** 
	 * Total pages in the current media set.
	 * @default 0
	 * @type number
	 */
	this.totalPages = 0;
	/** 
	 * The currently selected page.
	 * @default 0
	 * @type number
	 */
	this.pageNumber = this.startingPage || 0;
	/** 
	 * Observer collection.
	 */
	this.observers = [];
	/** 
	 * Whether to provide the ability to change the page size.
	 * @default false
	 * @type boolean
	 */
	this.showPageSizeSwitch = this.showPageSizeSwitch || false;
	/** 
	 * The maximum page size (when using showPageSizeSwitch=true).
	 * @default 50
	 * @type number
	 */
	this.viewAllMax = this.viewAllMax || 50;
	/** 
	 * The default size for a page of media.
	 * @type number
	 */
	this.defaultPageSize = this.defaultPageSize || this.pageSize;
};

HD.util.Paging.Model.prototype = {
	
};

HD.extend(HD.util.Paging.Model, [HD.util.Observable]);

/**
 * @class HD.util.Paging.View
 * @description The paging view renders the current state of the paging widget.
 * @constructor
 * @extends HD.util.Observable, HD.util.Template
 * @property {object} config
 *                 The configuration for the widget
 * @property {object}  [config.hooks=HD.util.Paging.View.hooks]
 *                 Customized subset of hooks (merged with the defaults).
 * @property {object}  [config.templates=HD.util.Paging.View.templates]
 *                 Customized subset of templates (merged with the defaults).
 * @param {HD.util.Paging.Model} model
 *                 The paging model to use with the view.
 */
HD.util.Paging.View = function(model){
	/**
	 * The paging model to use with the view.
	 * @type HD.util.Paging.Model
	 */
	this.model = model;
	/**
	 * The configuration for the view (inherited from the model).
	 * @type object.
	 */
	this.config = model.config;
	/** A copy of the collection of container elements. */
	this.containers = this.config.containers;
	
	/** Observers collection */
	this.observers = [];
	this.model.addObserver(this);
	this.loadTemplates(arguments.callee);
};

/**
 * The collection of default templates.
 */
HD.util.Paging.View.templates = {
	/**
	 * Label for the next page
	 * @memberOf HD.util.Paging.View
	 */
	nextText : "Next&gt;",
	/**
	 * Label for the previous page
	 * @memberOf HD.util.Paging.View
	 */
	previousText : "&lt;Prev",
	/**
	 * Text to use for ellipsis
	 * @memberOf HD.util.Paging.View
	 */
	ellipsisText : ' ... ',
	/**
	 * Label for viewing all content
	 * @memberOf HD.util.Paging.View
	 */
	showAllText : "View All", 
	/**
	 * Label for the changing the page size <br/>
	 * Must contain "${number}" which will be replaced with dynamic page sizes
	 * @memberOf HD.util.Paging.View
	 */
	showPageSizeText : "View ${number} per Page",
	
	/**
	 * @constant
	 * @return {string} Label for the next page
	 * @memberOf HD.util.Paging.View
	 */
	getNextText : function() {
		return this.nextText;
	},
	
	/**
	 * @constant
	 * @return {string} Label for the previous page
	 * @memberOf HD.util.Paging.View
	 */
	getPreviousText : function() {
		return this.previousText;
	},
	
	/**
	 * @constant
	 * @return {string} Text to use for ellipsis
	 * @memberOf HD.util.Paging.View
	 */
	getEllipsisText : function() {
		return this.ellipsisText;
	},
	
	/**
	 * @constant
	 * @return {string} Label for viewing all content
	 * @memberOf HD.util.Paging.View
	 */
	getShowAllText : function() {
		return this.showAllText;
	},
	
	/**
	 * @constant
	 * @param {number} pageSize
	 *                 A number for the page size.
	 * @return {string} Label for the changing the page size
	 * @memberOf HD.util.Paging.View
	 */
	getShowPageSizeText : function(pageSize) {
		return this.showPageSizeText.replace(/\$\{number\}/, pageSize);
	},
	
	/**
	 * @constant
	 * @return {string} Main template for the widget
	 * @memberOf HD.util.Paging.View
	 */
	getHtml : function() {
		return this.html;
	},
	
	/** 
	 * Main template for the widget
	 * @memberOf HD.util.Paging.View
	 * @type jst_template
	 */
	html :  '\
		<span class="${classes.PAGING}">\
		{if hasPrevious}\
			<span class="${hooks.PREVIOUS} ${hooks.PAGING_ARROW}">${templates.getPreviousText()}</span> \
			{if previousPages == 1}\
				 <span class="${hooks.PAGING_ARROW}">${pageNumber - 1}</span>\
			{elseif previousPages == 2}\
				 <span class="${hooks.PAGING_ARROW}">${startPage}</span>\
				 <span class="${hooks.PAGING_ARROW}">${pageNumber - 1}</span>\
			{elseif previousPages > 2}\
				 <span class="${hooks.PAGING_ARROW}">${startPage}</span>\
				${templates.getEllipsisText()}\
				{if !hasNext}\
					 <span class="${hooks.PAGING_ARROW}">${pageNumber - 2}</span>\
				{/if}\
				 <span class="${hooks.PAGING_ARROW}">${pageNumber - 1}</span>\
			{/if}\
		{/if}\
		 <span class="${classes.CURRENT}">${pageNumber}</span> \
		{if hasNext}\
			{if nextPages == 1}\
				 <span class="${hooks.PAGING_ARROW}">${pageNumber + 1}</span>\
			{elseif nextPages == 2}\
				 <span class="${hooks.PAGING_ARROW}">${pageNumber + 1}</span>\
				 <span class="${hooks.PAGING_ARROW}">${totalPages}</span>\
			{elseif nextPages > 2}\
				 <span class="${hooks.PAGING_ARROW}">${pageNumber + 1}</span>\
				{if !hasPrevious}\
					 <span class="${hooks.PAGING_ARROW}">${pageNumber + 2}</span>\
				{/if}\
				${templates.getEllipsisText()}\
				 <span class="${hooks.PAGING_ARROW}">${totalPages}</span>\
			{/if}\
			 <span class="${hooks.NEXT} ${hooks.PAGING_ARROW}">${templates.getNextText()}</span>\
		{/if}\
		{if showPageSize}\
			<span class="${classes.SHOW_VIEW}">\
			{if pageSize == viewAllMax}\
		    	<span class="${classes.SHOW_DEFAULT}">${templates.getShowPageSizeText(defaultPageSize)}</span>\
			{elseif totalMedia < viewAllMax}\
		    	<span class="${classes.SHOW_MAX}">${templates.getShowPageSizeText(viewAllMax)}</span>\
			{else}\
		    	<span class="${classes.SHOW_ALL}">${templates.getShowAllText()}</span>\
			{/if}\
			</span>\
		{/if}\
		</span>'
};

(function() {
	var classes = HD.CSS_CLASSES;
	
	/**
	 * The collection of hooks.
	 */
	HD.util.Paging.View.hooks = {
		/**
		 * Standalone: 0..1 <br/>
		 * Use: A node that switches to the next page
		 * @memberOf HD.util.Paging.View
		 */
		NEXT : classes.NEXT,
		
		/**
		 * Standalone: 0..1 <br/>
		 * Use: A node that switches to the previous page
		 * @memberOf HD.util.Paging.View
		 */
		PREVIOUS : classes.PREVIOUS,
		
		/**
		 * Standalone: 0..n <br/>
		 * Use: Selecting nodes to apply paging switches to
		 * @memberOf HD.CategoriesWidget
		 */
		PAGING_ARROW : classes.PAGING_ARROW
	};
})();

HD.util.Paging.View.prototype = {	
	/**
	 * Renders the HTML for the widget based on dynamic data.
	 * @returns HTML for the widget
	 * @type string
	 */
	render : function() {
		var classes = HD.CSS_CLASSES;
		var startPage = this.model.startingPage;
		var pageNumber = this.model.pageNumber;
		var totalPages = this.model.totalPages;
				
		if(pageNumber == startPage && pageNumber == totalPages) {
			for(var i = 0, len = this.containers.length; i < len; i++) {
				var container = HD.get(this.containers[i]);
				HD.addClass(container.parentNode, classes.PAGING_NONE);
			}
			return "";
		} 
		
		// how many previous pages available
		var previousPages = pageNumber - startPage;
		var hasPrevious = previousPages > 0;
		
		// how many next pages available
		var nextPages = totalPages - pageNumber;
		var hasNext = nextPages > 0;
		
		var htmlTemplate = this.processTemplate(this.config.templates.getHtml(), {
			startPage       : startPage,
			pageNumber      : pageNumber,
			totalPages      : totalPages,
			hasPrevious     : hasPrevious,
			hasNext         : hasNext,
			previousPages   : previousPages,
			nextPages       : nextPages,
			showPageSize    : this.model.showPageSizeSwitch,
			pageSize        : this.model.pageSize,
			viewAllMax      : this.model.viewAllMax,
			totalMedia      : this.model.totalMedia,
			defaultPageSize : this.model.defaultPageSize
		});
		
		for(var i = 0, len = this.containers.length; i < len; i++) {
			var container = HD.get(this.containers[i]);
			container.innerHTML = htmlTemplate;
			HD.removeClass(container.parentNode, classes.PAGING_NONE);
		}
		this.setListeners();
	},
	
	/**
	 * Sets event listeners for the widget.
	 * @param {object} [data]
	 *                 Event data from the render phase
	 */
	setListeners : function() {
		for(var i = 0, len = this.containers.length; i < len; i++) {
			this.setListener(HD.get(this.containers[i]));
		}
	},
	
	/**
	 * Sets event listeners for a container within the widget.
	 * @param {object} container
	 *                 An element containing a paging instance.
	 */
	setListener : function(container) {
		var hooks = this.config.hooks;
		var cb = this;
		var prevEls = HD.getByClass(hooks.PREVIOUS, "*", container, function(el) {
			el.onclick = function() {
				cb.notifyObservers.call(cb, "paging_Previous");
			};
		});
		var nextEls = HD.getByClass(hooks.NEXT, "*", container, function(el) {
			el.onclick = function() {
				cb.notifyObservers.call(cb, "paging_Next");
			};
		});
		var numberEls = HD.getByClass(hooks.PAGING_ARROW, "*", container, function(el) {
			if(!HD.hasClass(el, hooks.PREVIOUS) && !HD.hasClass(el, hooks.NEXT)) {
				el.onclick = function() {
					cb.notifyObservers.call(cb, "paging_Page", parseInt(this.innerHTML));
				};
			} 
		});
		var ShowAllEls = HD.getByClass(hooks.SHOW_ALL, "*", container, function(el) {
			el.onclick = function() {
				cb.notifyObservers.call(cb, "show_All");
			};
		});
		var ShowMaxEls = HD.getByClass(hooks.SHOW_MAX, "*", container, function(el) {
			el.onclick = function() {
				cb.notifyObservers.call(cb, "show_Max");
			};
		});
		var ShowDefaultEls = HD.getByClass(hooks.SHOW_DEFAULT, "*", container, function(el) {
			el.onclick = function() {
				cb.notifyObservers.call(cb, "show_Default");
			};
		});
	},
	
	/**
	 * Monitors events by HD.CommunityDAO.
	 * @param {string} eventName
	 *                 The name of the event
	 * @param {object} [eventData]
	 *                 Data for the event
	 */
	update : function(eventName, eventData) {
		if(eventName == "paging_Update") {
			this.render();
		}
	}
};

HD.extend(HD.util.Paging.View, [HD.util.Observable, HD.util.Template]);

/**
 * @class HD.util.Paging.Controller
 * @description The paging controller initiates updates within the paging widget.
 * @constructor
 * @param {HD.util.Paging.Model} model
 *                 The paging model to use with the controller.
 * @param {HD.util.Paging.View} view
 *                 The paging view to use with the controller.
 */
HD.util.Paging.Controller = function(model, view){
	/**
	 * The paging model to use with the controller.
	 * @type HD.util.Paging.Model
	 */
	this.model = model;
	/**
	 * The paging view to use with the controller.
	 * @type HD.util.Paging.View
	 */
	this.view = view;
};

HD.util.Paging.Controller.prototype = {
	/** 
	 * Passes updated paging data to the model/view.
	 * @param {object} pagingData
	 *                 An updated set of paged media.
	 */
	updatePagingData : function(pagingData) {
		this.model.pageNumber = pagingData.pageNumber;
		this.model.totalPages = pagingData.totalPages;
		this.model.totalMedia = pagingData.totalMedia;
		this.model.pageSize = pagingData.pageSize;
		this.model.notifyObservers("paging_Update");
	}
};

HD.register("hd_paging", HD.util.Paging, {version: "1.0", build: "1"});