<?php
/**
 * Zend Framework (http://framework.zend.com/)
 *
 * @link      http://github.com/zendframework/zf2 for the canonical source repository
 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */

namespace Zend\Paginator\Adapter\Exception;

use 
Zend\Paginator\Exception\ExceptionInterface as Exception;

interface 
ExceptionInterface extends Exception
{}
page cannot have itself as a parent'
            );
        }

        // return if the given parent already is parent
        if ($parent === $this->parent) {
            return $this;
        }

        // remove from old parent
        if (null !== $this->parent) {
            $this->parent->removePage($this);
        }

        // set new parent
        $this->parent = $parent;

        // add to parent if page and not already a child
        if (null !== $this->parent && !$this->parent->hasPage($this, false)) {
            $this->parent->addPage($this);
        }

        return $this;
    }

    /**
     * Returns parent container
     *
     * @return AbstractContainer|null  parent container or null
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * Sets the given property
     *
     * If the given property is native (id, class, title, etc), the matching
     * set method will be used. Otherwise, it will be set as a custom property.
     *
     * @param  string $property property name
     * @param  mixed  $value    value to set
     * @return AbstractPage fluent interface, returns self
     * @throws Exception\InvalidArgumentException if property name is invalid
     */
    public function set($property, $value)
    {
        if (!is_string($property) || empty($property)) {
            throw new Exception\InvalidArgumentException(
                '
Invalid argument$property must be a non-empty string'
            );
        }

        $method = '
set' . static::normalizePropertyName($property);

        if ($method != '
setOptions' && method_exists($this, $method)
        ) {
            $this->$method($value);
        } else {
            $this->properties[$property] = $value;
        }

        return $this;
    }

    /**
     * Returns the value of the given property
     *
     * If the given property is native (id, class, title, etc), the matching
     * get method will be used. Otherwise, it will return the matching custom
     * property, or null if not found.
     *
     * @param  string $property property name
     * @return mixed            the property'
s value or null
     
* @throws Exception\InvalidArgumentException if property name is invalid
     
*/
    public function 
get($property)
    {
        if (!
is_string($property) || empty($property)) {
            throw new 
Exception\InvalidArgumentException(
                
'Invalid argument: $property must be a non-empty string'
            
);
        }

        
$method 'get' . static::normalizePropertyName($property);

        if (
method_exists($this$method)) {
            return 
$this->$method();
        } elseif (isset(
$this->properties[$property])) {
            return 
$this->properties[$property];
        }

        return 
null;
    }

    
// Magic overloads:

    /**
     * Sets a custom property
     *
     * Magic overload for enabling <code>$page->propname = $value</code>.
     *
     * @param  string $name  property name
     * @param  mixed  $value value to set
     * @return void
     * @throws Exception\InvalidArgumentException if property name is invalid
     */
    
public function __set($name$value)
    {
        
$this->set($name$value);
    }

    
/**
     * Returns a property, or null if it doesn't exist
     *
     * Magic overload for enabling <code>$page->propname</code>.
     *
     * @param  string $name property name
     * @return mixed        property value or null
     * @throws Exception\InvalidArgumentException if property name is invalid
     */
    
public function __get($name)
    {
        return 
$this->get($name);
    }

    
/**
     * Checks if a property is set
     *
     * Magic overload for enabling <code>isset($page->propname)</code>.
     *
     * Returns true if the property is native (id, class, title, etc), and
     * true or false if it's a custom property (depending on whether the
     * property actually is set).
     *
     * @param  string $name property name
     * @return bool whether the given property exists
     */
    
public function __isset($name)
    {
        
$method 'get' . static::normalizePropertyName($name);
        if (
method_exists($this$method)) {
            return 
true;
        }

        return isset(
$this->properties[$name]);
    }

    
/**
     * Unsets the given custom property
     *
     * Magic overload for enabling <code>unset($page->propname)</code>.
     *
     * @param  string $name property name
     * @return void
     * @throws Exception\InvalidArgumentException  if the property is native
     */
    
public function __unset($name)
    {
        
$method 'set' . static::normalizePropertyName($name);
        if (
method_exists($this$method)) {
            throw new 
Exception\InvalidArgumentException(
                
sprintf(
                    
'Unsetting native property "%s" is not allowed',
                    
$name
                
)
            );
        }

        if (isset(
$this->properties[$name])) {
            unset(
$this->properties[$name]);
        }
    }

    
/**
     * Returns page label
     *
     * Magic overload for enabling <code>echo $page</code>.
     *
     * @return string  page label
     */
    
public function __toString()
    {
        return 
$this->label;
    }

    
// Public methods:

    /**
     * Adds a forward relation to the page
     *
     * @param  string $relation relation name (e.g. alternate, glossary,
     *                          canonical, etc)
     * @param  mixed  $value    value to set for relation
     * @return AbstractPage  fluent interface, returns self
     */
    
public function addRel($relation$value)
    {
        if (
is_string($relation)) {
            
$this->rel[$relation] = $value;
        }
        return 
$this;
    }

    
/**
     * Adds a reverse relation to the page
     *
     * @param  string $relation relation name (e.g. alternate, glossary,
     *                          canonical, etc)
     * @param  mixed  $value    value to set for relation
     * @return AbstractPage fluent interface, returns self
     */
    
public function addRev($relation$value)
    {
        if (
is_string($relation)) {
            
$this->rev[$relation] = $value;
        }
        return 
$this;
    }

    
/**
     * Removes a forward relation from the page
     *
     * @param  string $relation name of relation to remove
     * @return AbstractPage fluent interface, returns self
     */
    
public function removeRel($relation)
    {
        if (isset(
$this->rel[$relation])) {
            unset(
$this->rel[$relation]);
        }

        return 
$this;
    }

    
/**
     * Removes a reverse relation from the page
     *
     * @param  string $relation name of relation to remove
     * @return AbstractPage  fluent interface, returns self
     */
    
public function removeRev($relation)
    {
        if (isset(
$this->rev[$relation])) {
            unset(
$this->rev[$relation]);
        }

        return 
$this;
    }

    
/**
     * Returns an array containing the defined forward relations
     *
     * @return array  defined forward relations
     */
    
public function getDefinedRel()
    {
        return 
array_keys($this->rel);
    }

    
/**
     * Returns an array containing the defined reverse relations
     *
     * @return array  defined reverse relations
     */
    
public function getDefinedRev()
    {
        return 
array_keys($this->rev);
    }

    
/**
     * Returns custom properties as an array
     *
     * @return array  an array containing custom properties
     */
    
public function getCustomProperties()
    {
        return 
$this->properties;
    }

    
/**
     * Returns a hash code value for the page
     *
     * @return string  a hash code value for this page
     */
    
final public function hashCode()
    {
        return 
spl_object_hash($this);
    }

    
/**
     * Returns an array representation of the page
     *
     * @return array  associative array containing all page properties
     */
    
public function toArray()
    {
        return 
array_merge($this->getCustomProperties(), array(
            
'label'     => $this->getLabel(),
            
'fragment'  => $this->getFragment(),
            
'id'        => $this->getId(),
            
'class'     => $this->getClass(),
            
'title'     => $this->getTitle(),
            
'target'    => $this->getTarget(),
            
'rel'       => $this->getRel(),
            
'rev'       => $this->getRev(),
            
'order'     => $this->getOrder(),
            
'resource'  => $this->getResource(),
            
'privilege' => $this->getPrivilege(),
            
'active'    => $this->isActive(),
            
'visible'   => $this->isVisible(),
            
'type'      => get_called_class(),
            
'pages'     => parent::toArray(),
        ));
    }

    
// Internal methods:

    /**
     * Normalizes a property name
     *
     * @param  string $property  property name to normalize
     * @return string            normalized property name
     */
    
protected static function normalizePropertyName($property)
    {
        return 
str_replace(' '''ucwords(str_replace('_'' '$property)));
    }

    
// Abstract methods:

    /**
     * Returns href for this page
     *
     * @return string  the page's href
     */
    
abstract public function getHref();
}