<?php

namespace elanpl\L3;

class Router{

    protected $routes; // The defined routes collection
    protected $parsedParameters; // Parameters parsed from Request Path
    protected $depth; // Number of nested nodes in Request Path
    protected $routeNameIndex; // An array with elements that reference to the routes ordered by a route names
    public $RouteInfo; // The RouteInfo objcet 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];
    }

}