<?php
/**
 * A class for representing the incoming request.
 *
 * This class acts as a wrapper for the server, cookie, get, post and file
 * upload information and provides convenient ways to retrieve such information.
 * It also provides a dispatching mechanism.
 */
class request
{
    /**
     * @var array The server and execution environment variables.
     */
    protected $server;
    
    /**
     * @var array The cookie variables.
     */
    protected $cookie;
    
    /**
     * @var array The get variables.
     */
    protected $get;
    
    /**
     * @var array The post variables.
     */
    protected $post;
    
    /**
     * @var array The file variables.
     */
    protected $file;
    
    /**
     * @var front The front controller.
     */
    protected $front;
    
    ////////////////////////////////////////////////////////////////////////////
    // Construct and destruct functions.

    /**
     * @param array $front (optional) The front controller parameters.
     * @param array (optional) $server The server variables.
     * @param array (optional) $cookie The cookie variables.
     * @param array (optional) $get The get variables.
     * @param array (optional) $post The post variables.
     * @param array (optional) $file The file variables.
     * @throws invalidargumentexception If the $front parameter is not an array
     * of front construct parameters exclusively.
     * @throws invalidargumentexception If the $server, $cookie, $get, $post or
     * $file parameters are not arrays.
     */
    public function __construct(array $front = array(), $server = null, $cookie = null, $get = null, $post = null, $file = null)
    {
        $front = array_merge(array('directory' => '', 'controller' => array()), $front);
        
        if (count($front) != 2)
        {
            throw new invalidargumentexception('Argument 1 passed to ' . __METHOD__ . '() must be an array of front construct parameters exclusively.');
        }
        
        $this->front = $front;
        $i = 1;
        
        foreach (array('server' => $_SERVER, 'cookie' => $_COOKIE, 'get' => $_GET, 'post' => $_POST, 'file' => $_FILES) as $k => $v)
        {
            $i++;
            
            if ($$k === null)
            {
                $this->$k = $v;
            }
            else
            {
                if (!is_array($$k))
                {
                    throw new invalidargumentexception('Argument ' . $i . ' passed to ' . __METHOD__ . '() must be an array, ' . gettype($$k) . ' given.');
                }
                
                $this->$k = $$k;
            }
        }
    }
    
    ////////////////////////////////////////////////////////////////////////////
    // Overload functions.

    /**
     * A shortcut for get_server().
     * 
     * @param string $name The variable name.
     * @return mixed The value.
     */
    public function __get($name)
    {
        return $this->get_server($name);
    }

    /**
     * A shortcut for get_*().
     * 
     * @param string $function The variable type.
     * @param array $parameter The parameters.
     * @return string The value.
     * @throws badmethodcallexception If the variable type does not exist.
     */
    public function __call($function, array $parameter)
    {
        if (!method_exists($this, 'get_' . $function))
        {
            throw new badmethodcallexception('Magic method ' . __CLASS__ . '::' . $function . '() is not available.');
        }
        
        return call_user_func_array(array($this, 'get_' . $function), $parameter);
    }

    ////////////////////////////////////////////////////////////////////////////
    // Interface functions.

    /**
     * Executes a request path.
     *
     * @param string $path The path.
     * @throws runtimeexception If the path is not legal.
     * @return response The response.
     */
    public function execute($path)
    {
        $path = trim($path, ' /');
        
        if (!preg_match('~^([-_a-zA-Z0-9]+(/[-_a-zA-Z0-9]+)*((\.)[a-zA-Z0-9]+)?)?$~', $path))
        {
            throw new runtimeexception('Request path ' . var_export($path, true) . ' is not legal.');
        }
        
        if (!$this->front instanceof front)
        {
            $this->front = new front($this, $this->front['directory'], $this->front['controller']);
        }
        
        return $this->front->execute($path);
    }

    /**
     * Gets a server variable.
     * 
     * @param string (optional) $name The variable name.
     * @param mixed (optional) $alternative The alternative value.
     * @return mixed The value if it exists, else the alternative value.
     */
    public function get_server($name = null, $alternative = null)
    {
        if ($name === null)
        {
            return $this->server;
        }
        
        if (array_key_exists($name, $this->server))
        {
            return $this->server[$name];
        }
        
        return $alternative;
    }

    /**
     * Gets a cookie variable.
     * 
     * @param string (optional) $name The variable name.
     * @param mixed (optional) $alternative The alternative value.
     * @return mixed The value if it exists, else the alternative value.
     */
    public function get_cookie($name = null, $alternative = null)
    {
        if ($name === null)
        {
            return $this->cookie;
        }
        
        if (array_key_exists($name, $this->cookie))
        {
            return $this->cookie[$name];
        }
        
        return $alternative;
    }

    /**
     * Gets a get variable.
     * 
     * @param string (optional) $name The variable name.
     * @param mixed (optional) $alternative The alternative value.
     * @return mixed The value if it exists, else the alternative value.
     */
    public function get_get($name = null, $alternative = null)
    {
        if ($name === null)
        {
            return $this->get;
        }
        
        if (array_key_exists($name, $this->get))
        {
            return $this->get[$name];
        }
        
        return $alternative;
    }

    /**
     * Gets a post variable.
     * 
     * @param string (optional) $name The variable name.
     * @param mixed (optional) $alternative The alternative value.
     * @return mixed The value if it exists, else the alternative value.
     */
    public function get_post($name = null, $alternative = null)
    {
        if ($name === null)
        {
            return $this->post;
        }
        
        if (array_key_exists($name, $this->post))
        {
            return $this->post[$name];
        }
        
        return $alternative;
    }

    /**
     * Gets a file variable.
     * 
     * @param string (optional) $name The variable name.
     * @param mixed (optional) $alternative The alternative value.
     * @return mixed The value if it exists, else the alternative value.
     */
    public function get_file($name = null, $alternative = null)
    {
        if ($name === null)
        {
            return $this->file;
        }
        
        if (array_key_exists($name, $this->file))
        {
            return $this->file[$name];
        }
        
        return $alternative;
    }

    /**
     * Gets the base url.
     * 
     * @return string The url.
     */
    public function get_base()
    {
        $schema = ($this->HTTPS) ? 'https' : 'http';
        $name = $this->SERVER_NAME;
        $port = $this->SERVER_PORT;
        $base = trim(dirname($this->SCRIPT_NAME), ' \\/');
        return rtrim($schema . '://' . $name . (($port && $port != '80') ? ':' . $port : '') . '/' . $base, '/') . '/';
    }
    
    /**
     * Checks if the request is of type options or not.
     * 
     * @return boolean True if options, else false.
     */
    public function is_options()
    {
        return $this->REQUEST_METHOD === 'OPTIONS';
    }
    
    /**
     * Checks if the request is of type get or not.
     * 
     * @return boolean True if get, else false.
     */
    public function is_get()
    {
        return $this->REQUEST_METHOD === 'GET';
    }

    /**
     * Checks if the request is of type head or not.
     * 
     * @return boolean True if head, else false.
     */
    public function is_head()
    {
        return $this->REQUEST_METHOD === 'HEAD';
    }
    
    /**
     * Checks if the request is of type post or not.
     * 
     * @return boolean True if post, else false.
     */
    public function is_post()
    {
        return $this->REQUEST_METHOD === 'POST';
    }

    /**
     * Checks if the request is of type put or not.
     * 
     * @return boolean True if put, else false.
     */
    public function is_put()
    {
        return $this->REQUEST_METHOD === 'PUT';
    }
    
    /**
     * Checks if the request is of type delete or not.
     * 
     * @return boolean True if delete, else false.
     */
    public function is_delete()
    {
        return $this->REQUEST_METHOD === 'DELETE';
    }
    
    /**
     * Checks if the request is of type trace or not.
     * 
     * @return boolean True if trace, else false.
     */
    public function is_trace()
    {
        return $this->REQUEST_METHOD === 'TRACE';
    }
    
    /**
     * Checks if the request is of type connect or not.
     * 
     * @return boolean True if connect, else false.
     */
    public function is_connect()
    {
        return $this->REQUEST_METHOD === 'CONNECT';
    }
    
    /**
     * Checks if the request is secure (ssl) or not.
     * 
     * @return boolean True if secure, else false.
     */
    public function is_secure()
    {
        return $this->HTTPS === 'on';
    }

    ////////////////////////////////////////////////////////////////////////////
    // Internal functions.
}
