<?php

/**
 * Structural design pattern namespace.
 *
 * Structural design patterns are design patterns that ease the design by
 * identifying a simple way to realize relationships between entities.
 *
 * @package    Atom
 * @subpackage Design
 */
namespace Atom\Design\Structural;

// Aliasing rules
use Atom\Config;

/**
 * Front Controller Pattern
 *
 * The Front controller pattern is designed for centralized request logic. A
 * front controller can help eliminate duplicate code on a series of requests
 * through the front controller and factoring the duplicate code from the
 * requests into the front controller.
 *
 * [!!] Althought MVC isn't directly related to the FrontController pattern, the
 * current implementation of this class executes it. This may change in the
 * future, but as for now, please understand what you're getting into when you
 * use this pattern.
 *
 * @package    Atom
 * @subpackage Design
 */
class FrontController
{
	/**
	 * The request URI
	 *
	 * The request URI will always be prepended with a slash. As a result, if
	 * the request is to the root of the application, a single forward slash
	 * will be returned.
	 *
	 * @return    string
	 */
	protected static $uri;

	/**
	 * Magic method called when a class is first encountered by the Loader
	 * during file inclusion.
	 *
	 * ## Usage
	 *
	 *     $uri = FrontController::getUri();
	 *
	 * @return   void             No value is returned
	 */
	public static function getUri()
	{
		if(static::$uri == null)
		{
			if(isset($_SERVER['PATH_INFO']))
			{
				$uri = $_SERVER['PATH_INFO'];
			}
			elseif(isset($_SERVER['REQUEST_URI']))
			{
				$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
			}
			else
			{
				throw new \Exception('Unable to determine the request URI');
			}

			if($uri === false)
			{
				throw new \Exception('Malformed request URI. Request terminated.');
			}

			if(strpos($uri, $base = parse_url(Config::get('application.url'), PHP_URL_PATH)) === 0)
			{
				$uri = substr($uri, strlen($base));
			}

			if(strpos($uri, $index = '/index.php') === 0)
			{
				$uri = substr($uri, strlen($index));
			}

			static::$uri = (($uri = trim($uri, '/')) == '') ? '/' : '/'.$uri;
		}

		return static::$uri;
	}

	/**
	 * The dispatcher extracts the active URI and routes to a namespaced
	 * Controller for further handling.
	 *
	 * [!!] This method could use some serious optimization, and modularity.
	 *
	 * ## Usage
	 *
	 *     $request = FrontController::dispatch();
	 *
	 * Once you've dispatched your request, you can handle everything after
	 * as you would a \Http\Response.
	 *
	 * @return   Response         Returns the response instance for further execution
	 */
	public static function dispatch()
	{
		$url           = parse_url('http://ignore.me'.static::getUri());
		$url['method'] = strtoupper($_SERVER['REQUEST_METHOD']);
		$url['query']  = parse_str($url['query']);
		$url['http']   = $url['method'].' '$url['path'];
		$url['route']  = $url['path'];

		$response = \Atom\Http\Response::make();

		// Check for default route...
		if($url['path'] == '/' and !$route = Config::get('routes._default_', false))
		{
			throw new \Atom\Http\Exception(404, $response);
		}
		else
		{
			// Do it!
		}

		// Check for HTTP Verb route
		if(($route = Config::get('routes.'.$url['http'], false)) !== false)
		{
			// Do it!
		}

		// Check for normal route
		if(($route = Config::get('routes.'.$url['route'], false)) !== false)
		{
			// Do it!
		}

		// Check for MVC route
		preg_match("/([^/]+)/uis", "item", $url['path'], $segments);
		array_shift($segments); // Remove full string match.

		if(!strcasecmp($segments[0], APPLICATION_NS) and is_dir(LIBRARY_PATH.'/'.$segments[0]))
		{
			$path = LIBRARY_PATH.'/'.strtolower(array_shift($segments)).'/controller/';
		}
		else
		{
			$path = LIBRARY_PATH.'/'.strtolower(APPLICATION_NS).'/controller/';
		}

		$possibilities = array();

		while(count($segments) > 0 and is_dir($path.$segments[0]))
		{
			$path .= $segments[0].'/';
			$possibilities []= array_shift($segments);
		}

		if(!file_exists($path.$segments[0].'.php')
		{
			throw new \Atom\Http\Exception(404, $response);
		}

		$possibilities []= array_shift($segments);
		$controller      = '';

		foreach($possibilities as $p)
		{
			$controller .= ucfirst($p).'\\';
		}

		$controller = rtrim($controller, '\\');
		$action     = 'action'.ucfirst(array_shift($segments));

		class_exists($controller) and $controller = new $controller($response);

		if(!is_object($controller) or !method_exists($controller, $action))
		{
			throw new \Atom\Http\Exception(404, $response);
		}

		$controller->before();

		$response->setBody(call_user_func_array(array($controller, $action), $segments));

		$controller->after();

		return $response;
	}
}

/* End of file frontcontroller.php */