<?php
/*****************************************************************************
 * PageIndex.php
 * Contains key class PageIndex.
 *
 * Kabuki CMS (C) 2013-2015, Aaron van Geffen
 *****************************************************************************/

class PageIndex
{
	private $base_url;
	private $current_page = 1;
	private $index_class = 'pagination';
	private $items_per_page = 0;
	private $linkBuilder;
	private $needsPageIndex = false;
	private $num_pages = 1;
	private $page_index = [];
	private $page_slug = '%AMP%page=%PAGE%';
	private $recordCount = 0;
	private $sort_direction = null;
	private $sort_order = null;
	private $start = 0;

	public function __construct($options)
	{
		static $neededKeys = ['base_url', 'items_per_page', 'recordCount'];
		foreach ($neededKeys as $key)
		{
			if (!isset($options[$key]))
				throw new Exception('PageIndex: argument ' . $key . ' missing in options');

			$this->$key = $options[$key];
		}

		static $optionalKeys = ['index_class', 'linkBuilder', 'page_slug', 'sort_direction', 'sort_order', 'start'];
		foreach ($optionalKeys as $key)
			if (isset($options[$key]))
				$this->$key = $options[$key];

		$this->generatePageIndex();
	}

	private function buildLink($start = null, $order = null, $dir = null)
	{
		if (isset($this->linkBuilder))
			return call_user_func($this->linkBuilder, $start, $order, $dir);
		else
			return $this->getLink($start, $order, $dir);
	}

	protected function generatePageIndex()
	{
		/*
			Example 1:
			[1] [2] [3] [...] [c-2] [c-1] [c] [c+1] [c+2] [...] [n-2] [n-1] [n]
			\---------/       \-------------------------/       \-------------/
			   lower           current/contiguous pages              upper

			Example 2:
			[1] [2] [3] [4] [5] [c] [6] [7] [...] [n/2] [...] [n-2] [n-1] [n]
			\---------/ \-----------------/       \---/       \-------------/
			   lower     current/cont. pgs.       center            upper
		*/

		$this->num_pages = max(1, ceil($this->recordCount / $this->items_per_page));
		$this->current_page = min(ceil($this->start / $this->items_per_page) + 1, $this->num_pages);
		if ($this->num_pages <= 1)
		{
			$this->needsPageIndex = false;
			return;
		}

		$lowerLower = 1;
		$lowerUpper = min($this->num_pages, 3);

		$contigLower = max($lowerUpper + 1, max($this->current_page - 2, 1));
		$contigUpper = min($this->current_page + 2, $this->num_pages);

		$center = floor($this->num_pages / 2);

		$upperLower = max($contigUpper + 1, max(0, $this->num_pages - 2));
		$upperUpper = $this->num_pages;

		$this->page_index = [];

		// Lower pages
		for ($p = $lowerLower; $p <= $lowerUpper; $p++)
			$this->page_index[$p] = [
				'index' => $p,
				'is_selected' => $this->current_page == $p,
				'href'=> $this->buildLink(($p - 1) * $this->items_per_page, $this->sort_order, $this->sort_direction),
			];

		// The center of the page index.
		if ($center > $lowerUpper && $center < $contigLower)
		{
			// Gap?
			if ($lowerUpper != $center)
				$this->page_index[] = '...';

			$this->page_index[$center] = [
				'index' => $center,
				'is_selected' => $this->current_page == $center,
				'href'=> $this->buildLink(($center - 1) * $this->items_per_page, $this->sort_order, $this->sort_direction),
			];
		}

		// Gap?
		if ($contigLower != $p)
			$this->page_index[] = '...';

		// contig pages
		for ($p = $contigLower; $p <= $contigUpper; $p++)
			$this->page_index[$p] = [
				'index' => $p,
				'is_selected' => $this->current_page == $p,
				'href'=> $this->buildLink(($p - 1) * $this->items_per_page, $this->sort_order, $this->sort_direction),
			];

		// The center of the page index.
		if ($center > $contigUpper && $center < $upperLower)
		{
			// Gap?
			if ($contigUpper != $center)
				$this->page_index[] = '...';

			$this->page_index[$center] = [
				'index' => $center,
				'is_selected' => $this->current_page == $center,
				'href'=> $this->buildLink(($center - 1) * $this->items_per_page, $this->sort_order, $this->sort_direction),
			];
		}

		// Gap?
		if ($upperLower != $p)
			$this->page_index[] = '...';

		// Upper pages
		for ($p = $upperLower; $p <= $upperUpper; $p++)
			$this->page_index[$p] = [
				'index' => $p,
				'is_selected' => $this->current_page == $p,
				'href'=> $this->buildLink(($p - 1) * $this->items_per_page, $this->sort_order, $this->sort_direction),
			];

		// Previous page?
		if ($this->current_page > 1)
			$this->page_index['previous'] = $this->page_index[$this->current_page - 1];

		// Next page?
		if ($this->current_page < $this->num_pages)
			$this->page_index['next'] = $this->page_index[$this->current_page + 1];
	}

	public function getLink($start = null, $order = null, $dir = null)
	{
		$url = $this->base_url;
		$amp = strpos($this->base_url, '?') ? '&' : '?';

		if (!empty($start))
		{
			$page = $start !== '%d' ? ($start / $this->items_per_page) + 1 : $start;
			$url .= strtr($this->page_slug, ['%PAGE%' => $page, '%AMP%' => $amp]);
			$amp = '&';
		}
		if (!empty($order))
		{
			$url .= $amp . 'order=' . $order;
			$amp = '&';
		}
		if (!empty($dir))
		{
			$url .= $amp . 'dir=' . $dir;
			$amp = '&';
		}

		return $url;
	}

	public function getPageIndex()
	{
		return $this->page_index;
	}

	public function getItemsPerPage()
	{
		return $this->items_per_page;
	}

	public function getSortOrder()
	{
		return $this->sort_order;
	}

	public function getSortDirection()
	{
		return $this->sort_direction;
	}

	public function getPageIndexClass()
	{
		return $this->index_class;
	}

	public function getCurrentPage()
	{
		return $this->current_page;
	}

	public function getNumberOfPages()
	{
		return $this->num_pages;
	}

	public function getRecordCount()
	{
		return $this->recordCount;
	}
}