<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package fusionLib
 * @copyright Copyright (c) 2008 - 2013 fusionLib. All rights reserved.
 * @link http://fusionlib.com
 */

/**
 * flWidgetPagination Class
 *
 * Class to generate a page selection widget.
 *
 * @package fusionLib
 */
class flWidgetPagination {

	/**
	 * The current page.
	 *
	 * @var int
	 */
	private $page;

	/**
	 * The total number of rows.
	 *
	 * @var int
	 */
	private $totalRows;

	/**
	 * The number of rows to show per page.
	 *
	 * @var int
	 */
	private $rowsPerPage;

	/**
	 * The range of pages to show.
	 *
	 * @var int
	 */
	private $range;

	/**
	 * The number of pages.
	 *
	 * @var int
	 */
	private $numPages;

	/**
	 * Flags if the page ID should be included for page 0
	 *
	 * @var bool
	 */
	protected $withPage0ID = false;

	/**
	 * The route to use.
	 *
	 * @var flRouterRoute
	 */
	protected $route = null;

	/**
	 * The URI of the page if not using a flRouterRoute object.
	 *
	 * @var string
	 */
	protected $uri = null;

	/**
	 * Array of base parameters.
	 *
	 * @var array
	 */
	protected $baseParams = array();

	/**
	 * The name of the parameter holding the page number.
	 *
	 * @var string
	 */
	protected $pageParamName = 'page';

	/**
	 * The page number marker.
	 *
	 * @var null
	 */
	protected $marker = null;

	/**
	 * The CSS class to apply to the table.
	 *
	 * @var string
	 */
	protected $css = 'paginationwidget';

	/**
	 * Paramters to append to uri when using routes
	 *
	 * @var array
	 */
	protected $parameters = '';

	/**
	 * Construct a new object.
	 *
	 * @param int $page The current page number.
	 * @param int $totalRows The total number of rows.
	 * @param int $rowsPerPage The number of rows to show per page.
	 * @param int $range The number of pages to show either side of the active page.
	 */
	function __construct($page, $totalRows, $rowsPerPage = 20, $range = 5) {
		$this->page = $page;
		$this->totalRows = $totalRows;
		$this->rowsPerPage = $rowsPerPage;
		$this->range = $range;

		// Number of available pages
		$this->numPages = ceil($this->totalRows / $this->rowsPerPage);
	}

	/**
	 * Set the route object used for building the URIs.
	 *
	 * @param flRouterRoute $route The route object.
	 * @param array $baseParams Array of parameters to use when construction URLs
	 * @param string $pageParamName The name of the page parameter in the URI.
	 * @param array $parameters Key name pairs for parameters to append to URI.
	 * @param string $marker Name of optional marker for the page number, replaces :marker for pages > 0.
	 * @return flWidgetPagination This object
	 */
	function setRoute($route, $baseParams, $pageParamName = '*', $parameters = array(), $marker = null) {
		$this->route = $route;
		$this->baseParams = $baseParams;
		$this->pageParamName = $pageParamName;
		$this->marker = $marker;

		if(count($parameters)) {
			$p = array();
			foreach($parameters as $k => $v) {
				if(is_array($v)) {
					foreach($v as $v2)
						$p[] = $k . '[]=' . urlencode($v2);
				}
				else
					$p[] = $k . '=' . urlencode($v);
			}
			$this->parameters = '?' . join('&amp;', $p);
		}

		return $this;
	}

	/**
	 * Set the URI and parameters to use for building the URIs.
	 *
	 * @param string $uri The page URI.
	 * @param array $baseParams Parameters to include in every URI.
	 * @param string $pageParamName The name of the page parameter in the URI
	 * @return flWidgetPagination This object
	 */
	function setURI($uri, $baseParams, $pageParamName = 'page') {
		$this->uri = $uri;
		$this->baseParams = array();
		foreach($baseParams as $k => $v) {
			if(is_array($v)) {
				foreach($v as $v2)
					$this->baseParams[] = $k . '[]=' . urlencode($v2);
			}
			else
				$this->baseParams[] = $k . '=' . urlencode($v);
		}
		$this->pageParamName = $pageParamName;
		return $this;
	}

	/**
	 * Set the CSS class to use.
	 *
	 * @param string $css The CSS class, default is 'paginationwidget'.
	 * @return flWidgetPagination This object.
	 */
	function setClass($css) {
		$this->css = $css;
		return $this;
	}

	/**
	 * Draw the page selection as an unordered list.
	 *
	 * @return string The string holding the html.
	 */
	function render() {
		$html = '';

		// Number of available pages - 1
		$numPages = $this->numPages - 1;

		// If more than 1 page
		if($numPages) {

			// Calculate page range
			$startPage = max(0, $this->page - $this->range);
			$endPage = min($numPages, $this->page + $this->range);

			$html = '<ul class="' . $this->css . "\">\n";

			if($startPage > 0) {
				$html .= "<li><a href=\"" . $this->assemble(0) . "\">&laquo;</a></li>\n";
				$html .= "<li><a href=\"" . $this->assemble($this->page - 1) . "\">&lt;</a></li>\n";
			}

			for($i=$startPage;$i<=$endPage;$i++) {
				if($i == $this->page)
					$html .= '<li><span>' . ($i + 1) . "</span></li>\n";
				else
					$html .= "<li><a href=\"" . $this->assemble($i) . "\">" . ($i + 1) . "</a></li>\n";
			}

			if($endPage < $numPages) {
				$html .= "<li><a href=\"" . $this->assemble($this->page + 1) . "\">&gt;</a></li>\n";
				$html .= "<li><a href=\"" . $this->assemble($numPages) . "\">&raquo;</a></li>\n";
			}

			$html .= "</ul>\n";
		}

		return $html;
	}

	/**
	 * Make the URI for the given page.
	 *
	 * @param int $page The page number to make the URI for.
	 * @return string The URI.
	 */
	protected function assemble($page) {
		$params = $this->baseParams;
		if($this->route !== null) {
			if($page || $this->withPage0ID) {
				$params[$this->pageParamName] = $this->pageParamName == '*' ? array($page) : $page;
				if($this->marker)
					$params['marker'] = $this->marker;
			}
			return $this->route->assemble($params) . $this->parameters;
		}
		else {
			if($page || $this->withPage0ID)
				$params[] = $this->pageParamName . '=' . urlencode($page);
			return $this->uri . '?' . join('&amp;', $params);
		}
	}

	/**
	 * Get the start value for use in SQL LIMIT.
	 *
	 * @return int The start value.
	 */
	function getStart() {
		return $this->page * $this->rowsPerPage;
	}

	/**
	 * Get the rows for use in SQL LIMIT.
	 *
	 * @return int The rows value.
	 */
	function getRows() {
		return $this->rowsPerPage;
	}

	/**
	 * Get the total number of pages required to show the rows.
	 *
	 * @return int The number of pages.
	 */
	function getNumPages() {
		return $this->numPages;
	}

	/**
	 * Get the current page.
	 *
	 * @return int The current page number.
	 */
	function getPage() {
		return $this->page + 1;
	}

	/**
	 * Force page 0 ID to be included within the generated URL.
	 *
	 * @param bool $include true to include the page 0 ID; else false.
	 * @return flWidgetPagination This object.
	 */
	function withPage0ID($include = true) {
		$this->withPage0ID = $include;
		return $this;
	}
}
