<?php

require_once('Controller.class.php');

class Router {
	/**
	 * Array mapping URIs to controllers.
	 * A controller may register itself either by using
	 * Router::register($URI, $controller[, $function]);
	 * or by adding itself to the $ROUTER global.
	 * 
	 * The default here just gives us a 404 handler.
	 */
	private $routes = array('/*' => 'Http404');
	
	/**
	 * Instantiate a router that looks for controllers in $controllerpath.
	 */
	public function Router($controllerpath) {
		// create a $ROUTES global that can be used to set up our
		// $this->routes.
		global $ROUTES;
		$ROUTES = $this->routes;

		// Split $controllerpath into directories, and load the
		// controllers in each.
		$dirs = explode(PATH_SEPARATOR, $controllerpath);
		foreach ($dirs as $dir) {
			// Find all files in $dir with the ext `.class.php'
			$files = glob($dir.'/*.class.php');
			foreach ($files as $file) {
				// and include them
				require_once($file);
			}
		}
		
		$this->routes = $ROUTES;
		unset($ROUTES);
	}

	/**
	 * Route the page at the relative URL $page to the appropriate
	 * controller, and call the appropriate function.
	 */
	public function route($page) {
		$parts = explode('/', $page);
		$length = count($parts); // the # of segments in $controllerpart
		
		// if $page ends in "/", strip that off
		if ($parts[$length-1]=='') {
			array_pop($parts);
			$length--;
		}

		$controllerpart = implode('/', $parts);
		
		// Keep shortening $controllerpart until it matches something in
		// $this->routes.  The shortest it will ever become is '/*'.
		// If no key exists for '/*', that's an infinite loop.
		// Fortunately, the default value of $this->routes directs '/*'
		// to the Http404 controller.
		while(!isset($this->routes[$controllerpart])) {
			$some_parts = array_slice($parts, 0, $length);
			$controllerpart = implode('/', $some_parts).'/*';
			$length--;
		}
		$length++;
		
		// Figure what function to call on what controller
		// Grammar Nazi Warning: `what' or `which'?
		$controller = $this->routes[$controllerpart];
		if (strpos($controller, '->')===false) {
			// imply function
			$function = $parts[$length];
		} else {
			preg_match('/(.*)->(.*)/', $controller, $matches);
			$controller = $matches[1];
			$function = $matches[2];
		}
		
		// Default to the `index' function, provided by all controllers
		if ($function=='') {
			$function = 'index';
		}
		
		// We will pass these arrays to the function.
		$routed = array_slice($parts, 0, $length);
		$remainder = array_slice($parts, $length);
		
		// Finally, run the controller
		$obj = new $controller();
		if (in_array($function, get_class_methods($obj))) {
			call_user_func(array($obj, $function),
			               $routed, $remainder);
		} else {
			$obj->http404($routed, $remainder);
		}
	}
	
	/**
	 * This is to allow controllers to register themselves to the router.
	 * If $function=='', then the function will be determined by the segment
	 * to the right of the last segment in $path
	 */
	public static function register($path, $controller, $function='') {
		$str = $controller.(($function=='')?'':'->'.$function);
		global $ROUTES;
		$ROUTES[$path] = $str;
	}
}