<?php
namespace ModulesAbstract\ImportedOrdersFileManager\Model\Utils;

/**
 * Paginate & sort the results, basically prepares them for display
 * @author Lucian Sirbu
 */
class FilesystemPaginatorAndSorter
{
	/**
	 * Stores the iterator object that will be paginated
	 */
	protected $iterator = NULL;

	/**
	 * Stores the iterator object that will be paginated
	 */
	protected $internalIterator = NULL;

	/**
	 * Stores the current page
	 * @var int
	 */
	protected $currentPage = NULL;

	/**
	 * Stores the number of results per page
	 * @var int
	 */
	protected $itemsPerPage = NULL;

	/**
	 * Stores the total number of elements before limiting the iterator
	 * @var int
	 */
	protected $totalNumberOfResults = NULL;

	/**
	 * Stores the order column (eg: filename)
	 * @var String
	 */
	protected $orderColumn = NULL;

	/**
	 * Stores the order direction (eg: ASC, DESC)
	 * @var String
	 */
	protected $orderDirection = NULL;

	/**
	 * Set iterator object
	 * @return \ModulesAbstract\ImportedOrdersFileManager\Model\Utils\FilesystemPaginatorAndSorter
	 */
	public function setIterator($iterator)
	{
		$this->iterator = $iterator;
		return $this;
	}

	/**
	 * Set the items per page
	 * @param int $count
	 */
	public function setResultsPerPage($count)
	{
		$this->itemsPerPage = (int)$count;
		return $this;
	}

	/**
	 * Returns the number of items per page
	 * @return number
	 */
	public function getItemsPerPage()
	{
		return $this->itemsPerPage;
	}

	/**
	 * Set the current page
	 * @param int $page
	 */
	public function setCurrentPage($page)
	{
		$this->currentPage = (int)$page;
		return $this;
	}

	/**
	 * Returns the current page
	 * @return number
	 */
	public function getCurrentPage()
	{
		return $this->currentPage;
	}

	/**
	 * Returns the total number of results
	 * @return number
	 */
	public function getTotalNumberOfResults()
	{
		return $this->totalNumberOfResults;
	}

	/**
	 * Check if any results are stores
	 * @return number
	 */
	public function hasResults()
	{
		return $this->totalNumberOfResults>0;
	}

	/**
	 * Set sort style
	 * @param String $orderColumn
	 * @param String $orderDirection
	 * @throws \Exception
	 */
	public function setSortStyle($orderColumn, $orderDirection)
	{
		// validate order column
		if (!in_array($orderColumn, ['originalName', 'fullName', 'size', 'none', ])) {
			throw new \Exception("Invalid \$orderColumn in `".get_called_class()."` class.");
		}

		// validate order direction
		if (!in_array($orderDirection, ['ASC', 'DESC' ])) {
			throw new \Exception("The order by direction can only be ASC or DESC in `".get_called_class()."` class.");
		}

		$this->orderColumn = 	$orderColumn;
		$this->orderDirection = $orderDirection;

		return $this;
	}

	/**
	 * Execute the sort & pagination
	 * @return \ArrayIterator
	 */
	public function execute()
	{
		// sort the elements
		if ($this->orderColumn && $this->orderColumn!='none' && $this->orderDirection) {
			$elements = iterator_to_array($this->iterator);
			$this->totalNumberOfResults = count($elements);
			$elements = $this->sortList($elements, $this->orderColumn, ($this->orderDirection == 'ASC'));
			return new \ArrayIterator(array_slice($elements, $this->currentPage * $this->itemsPerPage, $this->itemsPerPage));
		}

		$elements = iterator_to_array($this->iterator);
		$this->totalNumberOfResults = count($elements);
		return array_slice($elements, $this->currentPage * $this->itemsPerPage, $this->currentPage * $this->itemsPerPage + $this->itemsPerPage);
	}

	/**
	 * Perform the sorting
	 * @param array $elements
	 * @param string $orderColumn
	 * @param bool $orderAscending
	 * @return array
	 */
	protected function sortList($elements, $orderColumn, $orderAscending=true)
	{
		usort($elements, function($a, $b) use($orderColumn, $orderAscending) {
			/* @var $a \ModulesAbstract\ImportedOrdersFileManager\Model\File */
			/* @var $b \ModulesAbstract\ImportedOrdersFileManager\Model\File */
			// do not hardcode the above hints, as in reallity the same algo is used for Dir sorting

			// skip sorting, for faster listings
			if ($orderColumn == 'none') {
				return 0;
			}

			// sort by name
			if ($orderColumn == 'fullName') {
				if ($orderAscending) {
					return strnatcmp($a->getFullName(), $b->getFullName());
				}
				else {
					return strnatcmp($b->getFullName(), $a->getFullName());
				}
			}

			// sort by size
			if ($orderColumn == 'size') {
				if ($orderAscending) {
					return $a->getFilesize() - $b->getFilesize();
				}
				else {
					return $b->getFilesize() - $a->getFilesize();
				}
			}

			// sort by original name
			if ($orderColumn == 'originalName') {
				// this should remove the FluX added part from "20190305 182109 02 01.FluX.AIR30013.Txt" and leave only "AIR30013.Txt"
				$aName = preg_replace("/^[0-9]{8} [0-9]{6} [0-9]{2} [0-9]{2}\.FluX\./", '', $a->getFullName());
				$bName = preg_replace("/^[0-9]{8} [0-9]{6} [0-9]{2} [0-9]{2}\.FluX\./", '', $b->getFullName());
				if ($orderAscending) {
					return strnatcmp($aName, $bName);
				}
				else {
					return strnatcmp($bName, $aName);
				}
			}
		});

		return $elements;
	}

}
