<?php
/**
 * Pagination handler class
 *
 * @package    phpMyFAQ
 * @subpackage PMF_Pagination
 * @license    MPL
 * @author     Anatoliy Belsky <ab@php.net>
 * @since      2009-09-27
 * @version    git: $Id$
 * @copyright  2009 phpMyFAQ Team
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 */

/**
 * PMF_Pagination
 *
 * @package    phpMyFAQ
 * @subpackage PMF_Pagination
 * @license    MPL
 * @author     Anatoliy Belsky <ab@php.net>
 * @since      2007-09-27
 * @copyright  2009 phpMyFAQ Team
 */
class PMF_Pagination
{
    /**
     * Template vars
     */
    const TPL_VAR_LINK_URL        = '{LINK_URL}';
    const TPL_VAR_LINK_TEXT       = '{LINK_TEXT}';
    const TPL_VAR_LAYOUT_CONTENT  = '{LAYOUT_CONTENT}';
    
    /**
     * Url style variants
     * - URL_STYLE_DEFAULT is a normal query string style
     * - URL_STYLE_REWRITE is the style of host/param0/val0/param1/val1
     */
    const URL_STYLE_DEFAULT = 0;
    const URL_STYLE_REWRITE = 1;
    
    /**
     * Base url used for links
     * 
     * @var string
     */
    protected $baseUrl = '';
    
    /**
     * Total items count
     * 
     * @var integer
     */
    protected $total = 0;
    
    /**
     * Items per page count
     * 
     * @var integer
     */
    protected $perPage = 0;
    
    /**
     * Default link template. 
     * Possible variables are {LINK}, {TITLE}, {TEXT}
     * 
     * @var string
     */
    protected $linkTpl = '<a href="{LINK_URL}">{LINK_TEXT}</a>';
    
    /**
     * Current page link template
     * 
     * @var string
     */
    protected $currentPageLinkTpl = '{LINK_TEXT}';
    
    /**
     * Next page link template
     * 
     * @var string
     */
    protected $nextPageLinkTpl = '<a href="{LINK_URL}">&gt;</a>';
    
    /**
     * Previous page link template
     * 
     * @var string
     */
    protected $prevPageLinkTpl = '<a href="{LINK_URL}">&lt;</a>';
    
    /**
     * First page link template
     * 
     * @var string
     */
    protected $firstPageLinkTpl = '<a href="{LINK_URL}">&lt;&lt;</a>';
    
    /**
     * Last page link template
     * 
     * @var string
     */
    protected $lastPageLinkTpl = '<a href="{LINK_URL}">&gt;&gt;</a>';
    
    /**
     * Layout template
     * 
     * @var string
     */
    protected $layoutTpl = '<div>{LAYOUT_CONTENT}</div>';
    
    /**
     * Url style 
     * 
     * @var string
     */
    protected $urlStyle = self::URL_STYLE_DEFAULT;
    
    /**
     * Current page index
     * 
     * @var integer
     */
    protected $currentPage = 0;
    
    /**
     * Param name to associate the page numbers to
     * 
     * @var string
     */
    protected $pageParamName = 'page';
    
    /**
     * Constructor
     *
     * @param array $options initialization options,
     * possible options:
     * - baseUrl (default "")
     * - total
     * - perPage
     * - linkTpl
     * - currentPageLinkTpl
     * - nextPageLinkTpl
     * - prevPageLinkTpl
     * - firstPageLinkTpl
     * - lastPageLinkTpl
     * - layoutTpl
     * - urlStyle
     * - pageParamName (default "page")
     * 
     * NOTE we read in the current page from the baseUrl, so if it contains
     *      no pageParamName, first page is asumed
     * 
     * @return null
     * 
     * TODO do some checks to params (eq. arguments count for templates etc)
     */
    public function __construct($options = null)
    {
        if (isset($options['baseUrl'])) {
            $this->baseUrl = $options['baseUrl'];
        }
       
        if (isset($options['total'])) {
            $this->total = $options['total'];
        }

        if (isset($options['perPage'])) {
            $this->perPage = $options['perPage'];
        }
       
        if (isset($options['linkTpl'])) {
            $this->linkTpl = $options['linkTpl'];
        }
       
        if (isset($options['currentPageLinkTpl'])) {
            $this->currentPageLinkTpl = $options['currentPageLinkTpl'];
        }
       
        if (isset($options['nextPageLinkTpl'])) {
            $this->nextPageLinkTpl = $options['nextPageLinkTpl'];
        }
       
        if (isset($options['prevPageLinkTpl'])) {
            $this->prevPageLinkTpl = $options['prevPageLinkTpl'];
        }
       
        if (isset($options['firstPageLinkTpl'])) {
           $this->firstPageLinkTpl = $options['firstPageLinkTpl'];
        }
        
        if (isset($options['lastPageLinkTpl'])) {
           $this->lastPageLinkTpl = $options['lastPageLinkTpl'];
        }
        
        if (isset($options['layoutTpl'])) {
           $this->layoutTpl = $options['layoutTpl'];
        }
        
        if (isset($options['urlStyle'])) {
           $this->urlStyle = $options['urlStyle'];
        }
        
        if (isset($options['pageParamName'])) {
           $this->pageParamName = $options['pageParamName'];
        }
        
        /**
         * Let this call to be last cuz it 
         * needs some options to be set before
         */
        $this->currentPage = $this->getCurrentPageFromUrl($this->baseUrl);
        
    }
    
    protected function getCurrentPageFromUrl($url)
    {
        $retval = 1;
        
        if(!empty($url)) {
            
            $match = array();
            
            switch ($this->urlStyle) {
                case self::URL_STYLE_REWRITE:
                    $pattern = '$/(' . $this->pageParamName . ')/(\d+)$';
                break;
                    
                case self::URL_STYLE_DEFAULT:
                default:
                    $pattern = '$&(amp;|)' . $this->pageParamName . '=(\d+)$';
                break;
            }
            
            if(PMF_String::preg_match($pattern, $url, $match)) {
                $retval = isset($match[2]) ? $match[2] : $retval;
            }
        }

        return $retval;
    }
    
    /**
     * Render full pagination string
     * 
     * @return string
     */
    public function render()
    {
        $content = array();
        
        $page = 1;
        for ($i = 0; $i < $this->total; $i += $this->perPage, $page++) {
            $link = $this->renderUrl($this->baseUrl, $page);

            if($page == $this->currentPage) {
                $template = $this->currentPageLinkTpl;
            } else {
                $template = $this->linkTpl;
            }
            
            $content[] = $this->renderLink($template, $link, $page);
        }
        
        if(1 < $this->currentPage) {
            array_unshift($content,
                          $this->renderLink($this->prevPageLinkTpl,
                                            $this->renderUrl($this->baseUrl, $this->currentPage - 1),
                                            $this->currentPage - 1)
                          );
            array_unshift($content,
                          $this->renderLink($this->firstPageLinkTpl,
                                            $this->renderUrl($this->baseUrl, 1),
                                            1)
                          );                          
                          
        }
        
        if($page - 1 > $this->currentPage) {
            array_push($content,
                       $this->renderLink($this->nextPageLinkTpl,
                                         $this->renderUrl($this->baseUrl, $this->currentPage + 1),
                                         $this->currentPage + 1)
                       );
                          
            array_push($content,
                       $this->renderLink($this->lastPageLinkTpl,
                                         $this->renderUrl($this->baseUrl, $page - 1),
                                         $page - 1)
                       );
        }
        
        return $this->renderLayout(implode('&nbsp;', $content));
    }
    
    /**
     * Render url for a given page
     * 
     * @param string  $url  url
     * @param integer $page page number
     * 
     * @return string
     */
    protected function renderUrl($url, $page)
    {
        switch ($this->urlStyle) {
            case self::URL_STYLE_REWRITE:
                $cleanedUrl = PMF_String::preg_replace(array('$/' . $this->pageParamName . '/(\d+)$',
                                                             '$//$'),
                                                       "",
                                                       $this->baseUrl);
                $url = "$cleanedUrl/{$this->pageParamName}/$page";
            break;
                
            case self::URL_STYLE_DEFAULT:
            default:
                $cleanedUrl = PMF_String::preg_replace(array('$&(amp;|)' . $this->pageParamName . '=(\d+)$'),
                                                       "",
                                                       $this->baseUrl);
                $url = "$cleanedUrl&amp;{$this->pageParamName}=$page";
            break;
        }
        
        return $url;
    }
    
    /**
     * Render a link
     * 
     * @param string $tpl      link template
     * @param string $url      url value for template container
     * @param string $linkText text value for template container
     * 
     * @return string
     */
    protected function renderLink($tpl, $url, $linkText)
    {
        $search  = array(self::TPL_VAR_LINK_URL, self::TPL_VAR_LINK_TEXT);
        $replace = array($url, $linkText);
        
        return str_replace($search, $replace, $tpl);
    }
    
    /**
     * Render the whole pagination layout
     * 
     * @param string $content layout contents
     * 
     * @return string
     */
    protected function renderLayout($content)
    {
        return str_replace(self::TPL_VAR_LAYOUT_CONTENT, $content, $this->layoutTpl);
    }
}
 
