<?php
	require_once('base.class.php');
	require_once('router.class.php');
	require_once('response.class.php');
	require_once('uri.class.php');
	require_once('events.interface.php');
	class App extends Base implements Events {
		private static $apps = array();
		private $domains;
		private $routers;
		private $router;
		public $_onerror;
		public function __construct($name){
			$this->name = $name;
			$this->router = new Router();
			$this->routers = array();
			$this->domains = array();
			$this->_onerror = function($res, $error){
				$o = json_encode(array(
					'message'=>$error->getMessage(),
					'code'=>$error->getCode(),
					'file'=>$error->getFile(),
					'line'=>$error->getLine(),
					'traceback'=>$error->getTrace()
				));
				if($res){
					$res->code(404);
					$res->header('Content-Type', 'application/json');
					$res->end($o);
				}else{
					http_response_code(404);
					header('Content-Type: application/json');
					print($o);
				}
			};
			static::$apps[] = $this;
		}
		public function __destruct(){
			$this->routers = array();
			$index = array_search(static::$apps, $this);
			if($index !== false){
				array_splice(static::$apps, $index, 1);
			}
			if(is_callable('parent::__destruct')){
				parent::__destruct();
			}
		}
		public static function shutdown(){
			$verb = $_SERVER['REQUEST_METHOD'];
			if(isset($_SERVER['REDIRECT_URL'])){
				$url = 'REDIRECT_URL';
			}else{
				$url = 'REQUEST_URI';
			}
			if(!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])){
				$root = $_SERVER['HTTP_X_FORWARDED_PROTO'].'://';
			}else{
				$root = !empty($_SERVER['HTTPS']) ? "https://" : "http://";
			}
			// @todo - determine if http or http and also check cloudflare
			$url = parse_url($root.$_SERVER['HTTP_HOST'].$_SERVER[$url]);
			$data = file_get_contents( 'php://input','r');
			foreach(static::$apps as $k => $app){
				if($app instanceof App){
					$app->handle($verb, $url, $data)->shutdown();
				}
			}
			if(is_callable('parent::__destruct')){
				parent::__destruct();
			}
		}
		public static function shutdown_error($error){
			foreach(static::$apps as $k => $app){
				if(is_callable($app->onerror)){
					$app->onerror(null, $error);
				}
			}
		}
		public function handle($verb, $url, $data){
			$res = new Response((string)new Uri($url));
			$self = $this;
			$onerror = function($res, $error) use($self){
				$self->onerror($res, $error);
			};
			$handled = false;
			// Domain routers
			foreach($this->domains as $host => $router){
				if($url['host'] == $host){
					$router->handle($url["path"], $res, null, $onerror);
					$handled = $handled || $router->handled;
				}
			}
			// Prefixed path routers
			foreach($this->routers as $prefix => $router){
				$router->handle($url["path"], $res, null, $onerror);
				$handled = $handled || $router->handled;
			}
			// Base router for non-prefixed paths
			$this->router->handle($url["path"], $res, function($res, $url) use($handled, $self){
				if(!$handled){
					$self->onerror($res,new Error("Not Found", 404));
				}
			}, $onerror);
			return $res;
		}
		public function error(Callable $fn){
			$this->_onerror = $fn;
			return $this;
		}
		public function route($path, Callable $fn){
			$this->router->path($path, $fn);
			return $this;
		}
		public function prefix($prefix, Callable $fn){
			if(!$this->routers[$prefix]){
				$this->routers[$prefix] = new Router($prefix);
			}
			$fn($this->routers[$prefix]);
			return $this;
		}
		public function domain($host, Callable $fn){
			if(!isset($this->domains[$host])){
				$this->domains[$host] = new Router();
			}
			$fn($this->domains[$host]);
			return $this;
		}
		public function onerror($res, $error){
			$fn = $this->_onerror;
			if(is_callable($fn)){
				$fn($res, $error);
			}
		}
	}
	error_reporting(E_ALL);
	ini_set('display_errors', 'On');
	register_shutdown_function(function(){
		App::shutdown();
	});
	set_error_handler(function($errno, $errstr, $errfile, $errline){
		App::shutdown_error(array(
			'number'=> $errno,
			'msg'=> $errstr,
			'file'=> $errfile,
			'line'=> $errline,
			'backtrace'=> debug_backtrace(),
			'included'=> get_included_files()
		));
	},E_ALL);
	register_shutdown_function(function(){
		$error = error_get_last();
		if($error['type'] == 1){
			App::shutdown_error(array(
				'number'=> $error['type'],
				'msg'=> $error['message'],
				'file'=> $error['file'],
				'line'=> $error['line'],
				'backtrace'=> array(),
				'included'=> get_included_files()
			));
		}
	});
?>