<?php

namespace Engine\Mvc\Router;

/**
 * The class manages the main Mvc Router.
 */
class Router
{
	/**
	 * The list of internal routes
	 */
	protected $routes = [];

	/**
	 * Add route to internal list
	 */
	public function addRoute($alias, $path, $controller=null, $R=null)
	{
		$this->routes[] = [
			'alias' => $alias,
			'path' => $path,
			'controller' => $controller,
			'R' => $R,
			'_type' => 'auto',
		];
		return $this;
	}

	public function addRouteComplex($alias, $path, $controller=null, $R=null)
	{
		$this->routes[] = [
			'alias' => $alias,
			'path' => $path,
			'controller' => $controller,
			'R' => $R,
			'_type' => 'complex',
		];
		return $this;
	}


	/**
	 * Get route from alias
	 */
	public function matchAlias($value)
	{
		$route = $this->matchAlias_exact($value);
		if ($route) {
			return $route;
		}

		$route = $this->matchAlias_regex($value);
		if ($route) {
			return $route;
		}

		throw new \Exception("Could not match a route with `alias`=`{$value}`.");
	}


	private function matchAlias_exact($value)
	{
		foreach ($this->routes as $route) {
			if ($route['_type']!='complex' && $route['alias']==$value) {
				return $this->buildRouteObject($route);
			}
		}
	}


	/*
	private function matchAlias_basic($value)
	{
		foreach ($this->routes as $route) {
			if (strpos($route['alias'], "{any}")!==false) {
				$alias = str_replace("{any}", $value, $route['alias']);
				$path  = str_replace("{any}", $value, $route['path']);

				if ($alias==$value) {
					return $this->buildRouteObject($route, $alias, $path);
				}
			}
		}
	}
	*/

	private function matchAlias_regex($alias)
	{
		foreach ($this->routes as $route) {
			$routeAlias = str_replace("\{any\}", "(?P<any>.*)", preg_quote($route['alias'], "/"));
			$matches = [];
			if (preg_match("/{$routeAlias}/", (string)$alias, $matches)) {
				$path = str_replace("{any}", $matches['any'], $route['path']);
				return $this->buildRouteObject($route, $alias, $path, null, $route['R']);
			}
		}
	}


	/**
	 * Get route from path
	 */
	public function matchPath($value)
	{
		$route = $this->matchPath_exact($value);
		if ($route) {
			return $route;
		}

		$route = $this->matchPath_regex($value);
		if ($route) {
			return $route;
		}

		throw new \Exception("Could not match a route with `path`=`{$value}`.");
	}


	private function matchPath_exact($value)
	{
		foreach ($this->routes as $route) {
			if ($route['_type']!='complex' && $route['path']==$value) {
				return $this->buildRouteObject($route);
			}
		}
	}

	/*private function matchPath_basic($value)
	{
		foreach ($this->routes as $route) {
			$pos = strpos($route['path'], "{any}");
			if ($pos!==false) {
				$routePathArr = explode("/", $route['path']);
				$valueArr = explode("/", $value);
				foreach ($routePathArr as $index=>$any) {
					if ($any=="{any}") {
						// extract alias & bind everything after {any}
						$alias = $valueArr[$index]
							.substr( $value, $pos+strlen($valueArr[$index]) );
						$path = $value;
						break;
					}
				}
			}

			if ($path==$value) {
				return $this->buildRouteObject($route, $alias, $value);
			}
		}
	}*/


	private function matchPath_regex($path)
	{
		foreach ($this->routes as $route) {
			// replace '{any}' with '.*' and let the regexp do the matching
			$routePath = str_replace("\{any\}", "(?P<any>.*)", preg_quote($route['path'], "/"));
			$matches = [];
			if (preg_match("/{$routePath}/", $path, $matches)) {
				$alias = str_replace("{any}", $matches['any'], $route['alias']);
				return $this->buildRouteObject($route, $alias, $path, null, $route['R']);
			}
		}
	}


	/**
	 * Build route object
	 * @param Array $route
	 * @return \Engine\Mvc\Router\Route
	 */
	private function buildRouteObject($route, $alias=null, $path=null, $controller=null, $R=null)
	{
		$routeObj = new \Engine\Mvc\Router\Route($route);

		$routeObj->buildAlias = $alias;
		$routeObj->buildPath = $path;
		$routeObj->buildController = $controller;
		$routeObj->R = $R;

		return $routeObj;
	}
}
