<?php

namespace elanpl\L3;

class Router{

    protected $routes; // The defined routes collection
    protected $routeNameIndex; // An array with elements that reference to the routes ordered by a route names
    public $parsedParameters; // Parameters parsed from Request Path
    public $depth; // Number of nested nodes in Request Path
    public $routeInfo; // The RouteInfo object if the route was matched

    public function __construct($routing)
    {
        $this->routes = array(); 
        $this->routeNameIndex = array();
        //Set the routing from configuration object
        $routing->set($this);
    }

    public function add($method, $path, $result, $name = ''){
        $this->routes[] = new RouteInfo($method, $path, $result, $name);
        if(isset($name)&&$name!='') $this->routeNameIndex[$name] = &$this->routes[count($this->routes)-1];
        return $this;
    }

    public function get($path, $result, $name = ''){
        return $this->add('GET', $path, $result, $name);
    }

    public function post($path, $result, $name = ''){
        return $this->add('POST', $path, $result, $name);
    }

    public function put($path, $result, $name = ''){
        return $this->add('PUT', $path, $result, $name);
    }

    public function patch($path, $result, $name = ''){
        return $this->add('PATCH', $path, $result, $name);
    }

    public function delete($path, $result, $name = ''){
        return $this->add('DELETE', $path, $result, $name);
    }

    public function any($path, $result, $name = ''){
        return $this->add('ANY', $path, $result, $name);
    }

    public function addBeforeAction($event_handler){
        $this->routes[count($this->routes)-1]->addBeforeAction($event_handler);
        return $this;
    }

    public function addAfterAction($event_handler){
        $this->routes[count($this->routes)-1]->addAfterAction($event_handler);
        return $this;
    }

    public function addAfterResult($event_handler){
        $this->routes[count($this->routes)-1]->addAfterAction($event_handler);
        return $this;
    }

    public function match($request){

        $auri = explode('/', trim($request->path, "/ \t\n\r\0\x0B"));
        $curi = count($auri);
			
        foreach ($this->routes as $routeInfo) {
            
            $route = $routeInfo->path;
            $method = $routeInfo->method;
            if($method=='ANY' || strpos($request->method,$method)!==false){
                $aroute = explode('/', trim($route, "/ \t\n\r\0\x0B"));
                //print_r($aroute);
                if($curi==count($aroute)){ //compare path element count
                    //optimistic assumption :)
                    $matchResult = true;
                    for($i = 0; $i<$curi; $i++){
                        $pathPartName = trim($aroute[$i],'{}');
                        if($aroute[$i]==$pathPartName){
                            if($auri[$i]!=$pathPartName){
                                //echo "diffrence found";
                                $matchResult = false;
                                break;
                            }
                        }
                        else{ // {...} found -> catch $uri variable
                            $value = $auri[$i];
                            $valueKey = explode(':', $pathPartName);
                            //validation
                            if(isset($valueKey[1]) && $valueKey[1]=='int'){
                                $value = intval($value);
                            }
                            //value store...
                            $this->parsedParameters[$valueKey[0]] = $value;
                        }
                    }
                    if($matchResult){ // match found
                        $this->depth = $curi;
                        $this->routeInfo = $routeInfo;
                        return $routeInfo->result;
                    }
                }
            }
        }
        return false;
    }

    public function link($name, $parameters){
        $route = $this->routeNameIndex[$name];
        $fields = array_keys($parameters);
        $values = array_values($parameters);
        array_walk($fields, function (&$item, $key){
            $item = "/\{".$item."\}/";
        });
        return preg_replace($fields, $values, $route->Path);
    }

    public function getParameter($name){
        return $this->parsedParameters[$name];
    }

}