<?php
/**
 * Class for managing "next" and "previous" navigation within result sets.
 *
 * PHP version 5
 *
 * Copyright (C) Villanova University 2010
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * @category VuFind
 * @package  Support_Classes
 * @author   Demian Katz <demian.katz@villanova.edu>
 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
 * @link     http://vufind.org/wiki/system_classes Wiki
 */
require_once 'services/MyResearch/lib/Search.php';

/**
 * Class for managing "next" and "previous" navigation within result sets.
 *
 * @category VuFind
 * @package  Support_Classes
 * @author   Demian Katz <demian.katz@villanova.edu>
 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
 * @link     http://vufind.org/wiki/system_classes Wiki
 */
class ResultScroller
{
    protected $enabled;

    /**
     * Constructor. Create a new search result scroller.
     *
     * @access public
     */
    public function __construct()
    {
        global $configArray;

        // Is this functionality enabled in config.ini?
        $this->enabled = (isset($configArray['Record']) &&
            isset($configArray['Record']['next_prev_navigation']) &&
            $configArray['Record']['next_prev_navigation']);
    }

    /**
     * Initialize this result set scroller. This should only be called
     * prior to displaying the results of a new search.
     *
     * @param object $searchObject The search object that was used to execute the
     * last search.
     * @param array  $result       The result of the last search.
     *
     * @return boolean
     * @access public
     */
    public function init($searchObject, $result)
    {
        // Do nothing if disabled:
        if (!$this->enabled) {
            return;
        }

        // Save the saved search ID of this search in the session
        $_SESSION['lastSearchId'] = $searchObject->getSearchId();

        // Save the current page number to the session
        $_SESSION['currentPageNumber'] = $searchObject->getPage();

        // Save the last search limit to the session
        $_SESSION['lastSearchLimit'] = $searchObject->getLimit();

        // Save the total number of results to the session
        $_SESSION['lastSearchResultTotal'] = $searchObject->getResultTotal();

        // save the IDs of records on the current page to the session
        // so we can "slide" from one record to the next/previous records
        // spanning 2 consecutive pages
        $recordIDs = array();
        foreach ($result['response']['docs'] as $record) {
            $recordIDs[] = $record['id'];
        }
        $_SESSION['recordsOnCurrentPage'] = $recordIDs;

        // clear the previous/next page
        unset($_SESSION['recordsOnPreviousPage']);
        unset($_SESSION['recordsOnNextPage']);

        return true;
    }

    /**
     * Get the previous/next record in the last search
     * result set relative to the current one, also return
     * the position of the current record in the result set.
     * Return array('previousRecord'=>previd, 'nextRecord'=>nextid,
     * 'currentPosition'=>number, 'resultTotal'=>number).
     *
     * @param string $id The ID currently being displayed
     *
     * @return array
     * @access public
     */
    public function getScrollData($id)
    {
        $retVal = array(
            'previousRecord'=>null,
            'nextRecord'=>null,
            'currentPosition'=>null,
            'resultTotal'=>null);

        // Do nothing if disabled:
        if (!$this->enabled) {
            return $retVal;
        }

        if (isset($_SESSION['recordsOnCurrentPage'])
            && isset($_SESSION['lastSearchId'])
        ) {
            // we need to restore the last search object
            // to fetch either the previous/next page of results
            $lastSearch = $this->_restoreLastSearch();

            // give up if we can not restore the last search
            if (!$lastSearch) {
                return $retVal;
            }

            $currentPageNumber = $_SESSION['currentPageNumber'];
            $recordsOnCurrentPage = $_SESSION['recordsOnCurrentPage'];
            $recordsOnPreviousPage
                = isset($_SESSION['recordsOnPreviousPage'])
                ? $_SESSION['recordsOnPreviousPage'] : null;
            $recordsOnNextPage
                = isset($_SESSION['recordsOnNextPage'])
                ? $_SESSION['recordsOnNextPage'] : null;
            $resultTotal
                = isset($_SESSION['lastSearchResultTotal'])
                ? $_SESSION['lastSearchResultTotal'] : 0;
            $retVal['resultTotal'] = $resultTotal;

            // find where this record is in the current result page
            $pos = array_search($id, $recordsOnCurrentPage);
            if ($pos !== false) {
                // OK, found this record in the current result page
                // calculate it's position relative to the result set
                $retVal['currentPosition']
                    = ($currentPageNumber - 1) * $_SESSION['lastSearchLimit']
                    + $pos + 1;

                // count how many records in the current result page
                $count = count($recordsOnCurrentPage);

                // if the current record is somewhere in the middle
                // of the current page, ie: not first or last
                // then it is easy
                if ($pos > 0 && $pos < $count - 1) {
                    $retVal['previousRecord'] = $recordsOnCurrentPage[$pos - 1];
                    $retVal['nextRecord'] = $recordsOnCurrentPage[$pos + 1];
                    // and we're done
                    return $retVal;
                }

                // if this record is first record on the current page
                if ($pos == 0) {
                    // if the current page is NOT the first page, and
                    // the previous page has not been fetched before, then
                    // fetch the previous page
                    if ($currentPageNumber > 1 && $recordsOnPreviousPage == null) {
                        $recordsOnPreviousPage = $this->_fetchPage(
                            $lastSearch, $currentPageNumber - 1
                        );
                        $_SESSION['recordsOnPreviousPage'] = $recordsOnPreviousPage;
                    }

                    // if there is something on the previous page, then the previous
                    // record is the last record on the previous page
                    if (!empty($recordsOnPreviousPage)) {
                        $prevPageSize = count($recordsOnPreviousPage);
                        $retVal['previousRecord']
                            = $recordsOnPreviousPage[$prevPageSize - 1];
                    }

                    // if it is not the last record on the current page, then
                    // we also have a next record on the current page
                    if ($pos < $count - 1) {
                        $retVal['nextRecord'] = $recordsOnCurrentPage[$pos + 1];
                    }

                    // and we're done
                    return $retVal;
                }

                // if this record is last record on the current page
                if ($pos == $count - 1) {
                    // if the next page has not been fetched, then
                    // fetch the next page
                    if ($recordsOnNextPage == null) {
                        $recordsOnNextPage = $this->_fetchPage(
                            $lastSearch, $currentPageNumber + 1
                        );
                        $_SESSION['recordsOnNextPage'] = $recordsOnNextPage;
                    }

                    // if there is something on the next page, then the next
                    // record is the first record on the next page
                    if (!empty($recordsOnNextPage)) {
                        $retVal['nextRecord'] = $recordsOnNextPage[0];
                    }

                    // if it is not the first record on the current page, then
                    // we also have a previous record on the current page
                    if ($pos > 0) {
                        $retVal['previousRecord'] = $recordsOnCurrentPage[$pos - 1];
                    }

                    // and we're done
                    return $retVal;
                }
            } else {
                // the current record is not on the current page

                // if there is something on the previous page
                if (!empty($recordsOnPreviousPage)) {
                    // check if current record is on the previous page
                    $pos = array_search($id, $recordsOnPreviousPage);
                    if ($pos !== false) {
                        // decrease the currentPageNumber in the session because
                        // we're now sliding into the previous page
                        $currentPageNumber--;
                        $_SESSION['currentPageNumber'] = $currentPageNumber;

                        // save current page
                        $tmp = $recordsOnCurrentPage;

                        // the previous page becomes the current page
                        $recordsOnCurrentPage = $recordsOnPreviousPage;
                        $_SESSION['recordsOnCurrentPage'] = $recordsOnCurrentPage;

                        // the old current page becomes the new next page
                        $recordsOnNextPage = $tmp;
                        $_SESSION['recordsOnNextPage'] = $recordsOnNextPage;

                        // destroy the old previous page
                        $recordsOnPreviousPage = null;
                        $_SESSION['recordsOnPreviousPage'] = $recordsOnPreviousPage;

                        // now we can set the previous/next record
                        if ($pos > 0) {
                            $retVal['previousRecord']
                                = $recordsOnCurrentPage[$pos - 1];
                        }
                        $retVal['nextRecord'] = $recordsOnNextPage[0];

                        // recalculate the current position
                        $retVal['currentPosition']
                            = ($currentPageNumber - 1) * $_SESSION['lastSearchLimit']
                            + $pos + 1;

                        // update the search URL in the session
                        $lastSearch->setPage($currentPageNumber);
                        $_SESSION['lastSearchURL'] = $lastSearch->renderSearchUrl();

                        // and we're done
                        return $retVal;
                    }
                }

                // if there is something on the next page
                if (!empty($recordsOnNextPage)) {
                    // check if current record is on the next page
                    $pos = array_search($id, $recordsOnNextPage);
                    if ($pos !== false) {
                        // increase the currentPageNumber in the session because
                        // we're now sliding into the next page
                        $currentPageNumber++;
                        $_SESSION['currentPageNumber'] = $currentPageNumber;

                        // save the current page
                        $tmp = $recordsOnCurrentPage;

                        // the next page becomes the current page
                        $recordsOnCurrentPage = $recordsOnNextPage;
                        $_SESSION['recordsOnCurrentPage'] = $recordsOnCurrentPage;

                        // the old current page becomes the new previous page
                        $recordsOnPreviousPage = $tmp;
                        $_SESSION['recordsOnPreviousPage'] = $recordsOnPreviousPage;

                        // destroy the old next page
                        $recordsOnNextPage = null;
                        $_SESSION['recordsOnNextPage'] = $recordsOnNextPage;

                        // now we can set the previous/next record
                        $prevPageSize = count($recordsOnPreviousPage);
                        $retVal['previousRecord']
                            = $recordsOnPreviousPage[$prevPageSize - 1];
                        if ($pos < count($recordsOnCurrentPage) - 1) {
                            $retVal['nextRecord'] = $recordsOnCurrentPage[$pos + 1];
                        }

                        // recalculate the current position
                        $retVal['currentPosition']
                            = ($currentPageNumber - 1) * $_SESSION['lastSearchLimit']
                            + $pos + 1;

                        // update the search URL in the session
                        $lastSearch->setPage($currentPageNumber);
                        $_SESSION['lastSearchURL'] = $lastSearch->renderSearchUrl();

                        // and we're done
                        return $retVal;
                    }
                }
            }
        }
        return $retVal;
    }


    /**
     * Fetch the given page of results from the given search object and
     * return the IDs of the records in an array.
     *
     * @param object $searchObject The search object to use to execute the search
     * @param int    $page         The page number to fetch
     *
     * @return array
     * @access private
     */
    private function _fetchPage($searchObject, $page)
    {
        $searchObject->setPage($page);
        $result = $searchObject->processSearch(true, false);
        if (PEAR::isError($result)) {
            PEAR::raiseError($result->getMessage());
        }
        $retVal = array();
        foreach ($result['response']['docs'] as $record) {
            $retVal[] = $record['id'];
        }
        return $retVal;
    }

    /**
     * Restore the last saved search.
     *
     * @return SearchObject
     * @access private
     */
    private function _restoreLastSearch()
    {
        if (isset($_SESSION['lastSearchId'])) {
            $search = new SearchEntry();
            $search->id = $_SESSION['lastSearchId'];
            if ($search->find(true)) {
                $minSO = unserialize($search->search_object);
                $savedSearch = SearchObjectFactory::deminify($minSO);
                return $savedSearch;
            }
        }
        return null;
    }
}

?>
