<?php

/**
 * @file classes/core/ArrayItemIterator.inc.php
 *
 * Copyright (c) 2014-2019 Simon Fraser University
 * Copyright (c) 2000-2019 John Willinsky
 * Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
 *
 * @class ArrayItemIterator
 * @ingroup db
 *
 * @brief Provides paging and iteration for arrays.
 */


import('lib.pkp.classes.core.ItemIterator');

class ArrayItemIterator extends ItemIterator {
	/** @var array The array of contents of this iterator. */
	var $theArray;

	/** @var int Number of items to iterate through on this page */
	var $itemsPerPage;

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

	/** @var int The total number of items. */
	var $count;

	/** Whether or not the iterator was empty from the start */
	var $wasEmpty;

	/**
	 * Constructor.
	 * @param $theArray array The array of items to iterate through
	 * @param $page int the current page number
	 * @param $itemsPerPage int Number of items to display per page
	 */
	function __construct(&$theArray, $page=-1, $itemsPerPage=-1) {
		parent::__construct();
		if ($page>=1 && $itemsPerPage>=1) {
			$this->theArray = $this->array_slice_key($theArray, ($page-1) * $itemsPerPage, $itemsPerPage);
			$this->page = $page;
		} else {
			$this->theArray =& $theArray;
			$this->page = 1;
			$this->itemsPerPage = max(count($this->theArray),1);
		}
		$this->count = count($theArray);
		$this->itemsPerPage = $itemsPerPage;
		$this->wasEmpty = count($this->theArray)==0;
		reset($this->theArray);
	}

	/**
	 * Static method: Generate an iterator from an array and rangeInfo object.
	 * @param $theArray array
	 * @param $theRange object
	 */
	function &fromRangeInfo(&$theArray, &$theRange) {
		if ($theRange && $theRange->isValid()) {
			$theIterator = new ArrayItemIterator($theArray, $theRange->getPage(), $theRange->getCount());
		} else {
			$theIterator = new ArrayItemIterator($theArray);
		}
		return $theIterator;
	}

	/**
	 * Return the next item in the iterator.
	 * @return object
	 */
	function &next() {
		if (!is_array($this->theArray)) {
			$value = null;
			return $value;
		}
		$value = current($this->theArray);
		if (next($this->theArray)===false) {
			$this->theArray = null;
		}
		return $value;
	}

	/**
	 * Return the next item in the iterator, with key.
	 * @return array (key, value)
	 */
	function nextWithKey() {
		$key = key($this->theArray);
		$value = $this->next();
		return array($key, $value);
	}

	/**
	 * Determine whether or not this iterator represents the first page
	 * @return boolean
	 */
	function atFirstPage() {
		return $this->page==1;
	}

	/**
	 * Determine whether or not this iterator represents the last page
	 * @return boolean
	 */
	function atLastPage() {
		return ($this->page * $this->itemsPerPage) + 1 > $this->count;
	}

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

	/**
	 * Get the total count of items
	 * @return int
	 */
	function getCount() {
		return $this->count;
	}

	/**
	 * Get the number of pages
	 * @return int
	 */
	function getPageCount() {
		return max(1, ceil($this->count / $this->itemsPerPage));
	}

	/**
	 * Return a boolean indicating whether or not we've reached the end of results
	 * @return boolean
	 */
	function eof() {
		return (($this->theArray == null) || (count($this->theArray)==0));
	}

	/**
	 * Return a boolean indicating whether or not this iterator was empty from the beginning
	 * @return boolean
	 */
	function wasEmpty() {
		return $this->wasEmpty;
	}

	/**
	 * Convert this iterator to an array
	 * @return array
	 */
	function &toArray() {
		return $this->theArray;
	}

	/**
	 * Return this iterator as an associative array.
	 */
	function &toAssociativeArray() {
		return $this->theArray;
	}

	/**
	 * Determine whether or not the iterator is within bounds.
	 * @return boolean
	 */
	function isInBounds() {
		return ($this->getPageCount() >= $this->page);
	}

	/**
	 * Get the range info representing the last page of results.
	 * @return object DBResultRange
	 */
	function &getLastPageRangeInfo() {
		import('lib.pkp.classes.db.DBResultRange');
		$returner = new DBResultRange(
			$this->itemsPerPage,
			$this->getPageCount()
		);
		return $returner;
	}

	/**
	 * A version of array_slice that takes keys into account.
	 * Thanks to pies at sputnik dot pl.
	 * This is made redundant by PHP 5.0.2's updated
	 * array_slice, but we can't assume everyone has that.
	 * FIXME: Reconcile this against the dupe in VirtualArrayIterator.
	 * @see http://ca3.php.net/manual/en/function.array-slice.php
	 */
	function array_slice_key($array, $offset, $len=-1) {
		if (!is_array($array)) return false;

		$return = array();
		$length = $len >= 0? $len: count($array);
		$keys = array_slice(array_keys($array), $offset, $length);
		foreach($keys as $key) {
			$return[$key] = $array[$key];
		}

		return $return;
	}
}


