<?php
/**
 * Copyright (C) 2017 Karmabunny Pty Ltd.
 *
 * This file is a part of SproutCMS.
 *
 * SproutCMS is free software: you can redistribute it and/or modify it under the terms
 * of the GNU General Public License as published by the Free Software Foundation, either
 * version 2 of the License, or (at your option) any later version.
 *
 * For more information, visit <http://getsproutcms.com>.
 *
 * This class was originally from Kohana 2.3.4
 * Copyright 2007-2008 Kohana Team
 */
namespace Sprout\Helpers;
 
use BootstrapConfig;
use Exception;
 
use Kohana;
use Kohana_Exception;
 
use karmabunny\router\Router as KbRouter;
 
/**
 * Router
 */
class Router
{
    /** The original URI requested e.g. by a HTTP user agent or via CLI */
    public static $current_uri = '';
 
    /** Original query string requested by HTTP user agent */
    public static $query_string = '';
 
    /** Original URI and query string combined. */
    public static $complete_uri = '';
 
    /** Controller/method URI to use, from the configured routes */
    public static $routed_uri = '';
 
    /** Optional fake file extension that will be added to all generated URLs, e.g. '.html' */
    public static $url_suffix = '';
 
    /** Controller to use */
    public static $controller;
 
    /** Method to call on controller */
    public static $method = 'index';
 
    /** Arguments to pass to controller method */
    public static $arguments = [];
 
    /** @var KbRouter */
    protected static $router;
 
    public static $MODE = KbRouter::MODE_REGEX;
 
 
    /**
     * Router setup routine; determines controller/method from URI.
     * Automatically called during Kohana setup process.
     *
     * @return  void
     */
    public static function setup()
    {
        // Load configured routes
        $routes = Kohana::config('routes');
 
        // Use the default route when no segments exist
        $uri = self::$current_uri;
        if ($uri === '') {
            if (!isset($routes['_default'])) {                 throw new Kohana_Exception('core.no_default_route');
            }
 
            $uri = '_default';
        }
 
        self::$router = KbRouter::create([
            'extract' => KbRouter::EXTRACT_ATTRIBUTES | KbRouter::EXTRACT_CONVERT_REGEX,
            'mode' => KbRouter::MODE_REGEX,
        ]);
        self::$router->load($routes);
 
        // Find matching configured route
        $routed_uri = Router::routedUri(Router::$current_uri);
        if ($routed_uri === false) return;
 
        // The routed URI is now complete
        Router::$routed_uri = $routed_uri;
 
        // Find the controller from the registered route. If no namespace specified, assume Sprout\Controllers\...
        $segments = explode('/', trim(Router
::$routed_uri, '/'));         if (strpos($controller, '\\') === false) $controller = 'Sprout\\Controllers\\' . $controller;             Router::$controller = $controller;
            if (count($segments) > 0) {                 Router::$arguments = $segments;
            } else {
                Router::$arguments = [];
            }
        }
    }
 
 
    /**
     * Get the routes tables.
     *
     * @return array
     */
    public static function getRoutes()
    {
        return self::$router->routes;
    }
 
 
    /**
     * Attempts to determine the current URI using CLI, GET, PATH_INFO, ORIG_PATH_INFO, or PHP_SELF.
     *
     * @return  void
     */
    public static function findUri()
    {
        if (isset($_GET['_apache_error']))         {
            $e = array(400 => '400 Bad Request', 401 => '401 Unauthorized', 403 => '403 Forbidden', 500 => '500 Internal Server Error');             $e = $e[$_GET['_apache_error']];
 
            if (!$e) {
                if ($_GET['_apache_error'][0] == '4') {
                    $e = '403 Forbidden';
                } else {
                    $e = '500 Internal Server Error';
                }
            }
 
            throw new Exception($e);
        }
 
        if (PHP_SAPI === 'cli')
        {
            // Command line requires a bit of hacking
            if (isset($_SERVER['argv'][1]))             {
                Router::$current_uri = $_SERVER['argv'][1];
 
                // Remove GET string from segments
                if (($query = strpos(Router
::$current_uri, '?')) !== FALSE)                 {
                    list (Router
::$current_uri, $query) = explode('?', Router
::$current_uri, 2);  
                    // Parse the query string into $_GET
 
                    // Convert $_GET to UTF-8
                    $_GET = utf8::clean($_GET);
                }
            }
        }
        elseif (isset($_GET['kohana_uri']))         {
            // Use the URI defined in the query string
            Router::$current_uri = $_GET['kohana_uri'];
 
            // Remove the URI from $_GET
            unset($_GET['kohana_uri']);  
            // Remove the URI from $_SERVER['QUERY_STRING']
            $_SERVER['QUERY_STRING'] = preg_replace('~\bkohana_uri\b[^&]*+&?~', '', $_SERVER['QUERY_STRING']);         }
        elseif (isset($_SERVER['PATH_INFO']) AND 
$_SERVER['PATH_INFO'])         {
            Router::$current_uri = $_SERVER['PATH_INFO'];
        }
        elseif (isset($_SERVER['ORIG_PATH_INFO']) AND 
$_SERVER['ORIG_PATH_INFO'])         {
            Router::$current_uri = $_SERVER['ORIG_PATH_INFO'];
        }
        elseif (isset($_SERVER['PHP_SELF']) AND 
$_SERVER['PHP_SELF'])         {
            Router::$current_uri = $_SERVER['PHP_SELF'];
        }
 
        if (($strpos_fc = strpos(Router
::$current_uri, KOHANA
)) !== FALSE)         {
            // Remove the front controller from the current uri
            Router
::$current_uri = (string
) substr(Router
::$current_uri, $strpos_fc + strlen(KOHANA
));        }
 
        // Remove slashes from the start and end of the URI
        Router
::$current_uri = trim(Router
::$current_uri, '/'); 
        if (Router::$current_uri !== '')
        {
            if ($suffix = Kohana
::config('core.url_suffix') AND 
strpos(Router
::$current_uri, $suffix) !== FALSE)             {
                // Remove the URL suffix
 
                // Set the URL suffix
                Router::$url_suffix = $suffix;
            }
 
            // Reduce multiple slashes into single slashes
            Router
::$current_uri = preg_replace('#//+#', '/', Router
::$current_uri);        }
 
        // Set the query string to the current query string
        if (!empty($_SERVER['QUERY_STRING'])) {             Router
::$query_string = '?'.trim($_SERVER['QUERY_STRING'], '&/');        }
 
        // Remove all dot-paths from the URI, they are not valid
        Router
::$current_uri = preg_replace('#\.[\s./]*/#', '', Router
::$current_uri);        Router
::$current_uri = trim(Router
::$current_uri, '/'); 
        // Remember the complete URI for some reason
        Router::$complete_uri = Router::$current_uri . Router::$query_string;
    }
 
    /**
     * Redirect to alternate hostname and/or protocol if requred
     *
     * The actual business rules for the desired protocol/hostname is defined in
     * the {@see BootstrapConfig} class which is located at config/_bootstrap_config.php
     *
     * @return void Redirects (301) if protocol and/or hostname should change
     */
    public static function originCleanup()
    {
        if (PHP_SAPI === 'cli') return;
 
        $old_proto = Request::protocol();
        $old_hostname = $_SERVER['HTTP_HOST'];
 
        list($new_proto, $new_hostname) = BootstrapConfig
::originCleanup($old_proto, $old_hostname);  
        if (BootstrapConfig::ORIGIN_CLEANUP_DEBUG) {
            self::originCleanupDebug($old_proto, $old_hostname, $new_proto, $new_hostname);
        }
 
        if ($new_proto !== $old_proto or $new_hostname !== $old_hostname) {
            $url = $new_proto . '://' . $new_hostname . '/' . Router::$complete_uri;
            Url::redirect($url, '301');
        }
    }
 
    /**
     * Output information about origin cleanup, and then exit
     * This is turned on by the BootstrapConfig::ORIGIN_CLEANUP_DEBUG constant
     *
     * @param string $old_proto
     * @param string $old_hostname
     * @param string $new_proto
     * @param string $new_hostname
     * @return void Terminates script execution
     */
    private static function originCleanupDebug($old_proto, $old_hostname, $new_proto, $new_hostname)
    {
        header('Content-type: text/plain');  
        echo "Old proto:     {$old_proto}\n";
        echo "New proto:     {$new_proto}\n";
        echo "Old hostname:  {$old_hostname}\n";
        echo "New hostname:  {$new_hostname}\n\n";
 
        if ($new_proto !== $old_proto or $new_hostname !== $old_hostname) {
            $url = $new_proto . '://' . $new_hostname . '/' . Router::$complete_uri;
            echo "Redirect:\n{$url}";
        } else {
            echo "No redirect";
        }
 
    }
 
    /**
     * Generates routed URI (i.e. controller/method/arg1/arg2/...) from given URI.
     *
     * @param string URI to convert, e.g. 'admin/edit/page/3'
     * @return string|bool Routed URI or false, e.g. 'AdminController/edit/page/3'
     * @throws Exception if no routes configured
     */
    public static function routedUri($uri)
    {
        if (Router
::$router === NULL or 
empty(Router
::$router->routes)) {             throw new Exception('No routes loaded');
        }
 
        $routed_uri = $uri = trim($uri, '/');  
        $method = Request::method();
        $action = self::$router->find($method, $uri);
        if (!$action) return false;
 
        $target = $action->target;
 
        // Convert class::method into sprout style segments.
            [$class, $method] = $target;
            $target = "{$class}/{$method}";
 
            foreach ($action->args as $arg) {
                $target .= '/' . $arg;
            }
        }
 
        // Ok now splice the rule args into the target.
        // So my/rule/{arg1}/path/{arg2} => 'ns\\to\\class/method/{arg1}/{arg2}'
        $routed_uri = preg_replace('#^' . $action->rule . '$#u', $target, $uri);         return trim($routed_uri, '/');     }
 
} // End Router