<?php

/**
 * iCMS - i Content Management System
 * Copyright (c) 2007-2017 iCMSdev.com. All rights reserved.
 *
 * @author icmsdev <master@icmsdev.com>
 * @site https://www.icmsdev.com
 * @licence https://www.icmsdev.com/LICENSE.html
 */

class Rewrite
{
    public static $URI = null;

    public static $EXTS = array(
        "png", "jpg", "jpeg", "gif", "bmp", "webp", "psd", "tif",
        "flv", "swf", "mkv", "avi", "rm", "rmvb", "mpeg", "mpg", "mp4",
        "ogg", "ogv", "mov", "wmv", "webm", "mp3", "wav", "mid", "amr",
        "rar", "zip", "tar", "gz", "7z", "bz2", "cab", "iso",
        "doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf", "txt", "md", "xml",
        "apk", "ipa",
        "css", "js",
    );
    public static function run($REQ=null)
    {
        is_null($REQ) && $REQ = parse_url(iPHP_REQUEST_URI);
        $path = $REQ['path'];
        $ext  = pathinfo($path, PATHINFO_EXTENSION);
        $ext  = strtolower($ext);
        $C = iCMS::$config;

        if (in_array($ext, self::$EXTS)) {
            if (preg_match('@.*?/avatar/\d+/\d+/\d+.jpg@i', $path)) {
                Helper::redirect(iCMS_PUBLIC_URL . '/img/avatar.jpg');
            }
            if (preg_match('@.*?/coverpic/\d+/\d+/\d+.jpg@i', $path)) {
                Helper::redirect(iCMS_PUBLIC_URL . '/img/coverpic.jpg');
            }
            Request::status(404, $path . ',rewrite 404');
        } else {
            $route = Config::get('route');
            $route['rewrite'] && self::routing($path);
            empty(self::$URI) && $flag = self::rules($route['dir'], $route['ext'], $path); //应用路由匹配
            empty(self::$URI) && $flag = self::clink($route['dir'], $route['ext'], $path); //文章类自定义链接
            empty(self::$URI) && self::indexPage($route['ext'], $path);

            if(!($route['rewrite']||$flag)) return;

            if (self::$URI) {
                $name  = basename(parse_url(self::$URI, PHP_URL_PATH), '.php');
                self::make($REQ);
                $name == 'api' ?
                    iCMS::API() :
                    iCMS::run($name);
                exit;
            } else {
                if (iPHP_DEBUG && iPHP_TPL_DEBUG) {
                    throw new sException(sprintf(
                        "未找到与链接<b>%s</b>相匹配的规则.",
                        $path
                    ));
                } else {
                    Request::status(404, $path . ',rewrite 404');
                }
            }
        }
    }
    public static function routing($path){
        $routing = Config::get('routing');
        $routing0 = $routing1 = array();
        foreach ($routing as $key => $value) {
            if (strpos($value[0], '{') === false && strpos($value[0], '}') === false) {
                $routing0[$value[0]] = $value[1];
            } else {
                if (stripos($key, 'uid:') === 0) {
                    $url = rtrim(Route::$config['user'], '/') . $value[0];
                    $value[0] = parse_url($url, PHP_URL_PATH);
                }
                $routing1[$key] = $value;
            }
        }
        //一般路由匹配
        empty(self::$URI) && self::route0($routing0, $path);
        empty(self::$URI) && self::route1($routing1, $path); //带{}类路由匹配
    }
    public static function rules($dir, $ext, $path)
    {
        $rs    = NodeCache::get('rules');
        $rules = array();
        $result = array();
        foreach ((array)$rs as $rule) {
            if ($rule) foreach ($rule as $k => $v) {
                $v = str_replace('{EXT}', $ext, $v);
                $v = rtrim($dir, '/') . '/' . ltrim($v, '/');

                if ($k == 'index' || $k == 'list') {
                    $k = 'node';
                } else {
                    $pi = pathinfo($v);
                    if (substr($v, -1) == '/' || empty($pi['extension'])) {
                        $pi['extension'] = ltrim($ext, '.');
                        $pi['basename'] .= '/index_{P}' . $ext;
                    } else {
                        $pi['basename'] = $pi['filename'] . '_{P}.' . $pi['extension'];
                    }
                    $pv = $pi['dirname'] . '/' . $pi['basename'];
                    $rules[$k][$pv] = strlen($pv);
                }
                $rules[$k][$v] = strlen($v);
            }
        }
        if (!$rules) return false;

        if ($rules) foreach ($rules as $key => $rvalue) {
            foreach ($rvalue as $k => $v) {
                $result[] = self::builder($key, $k);
            }
        }
        if (!$result) return false;

        usort($result, function ($a, $b) {
            $al = strlen($a[0]);
            $bl = strlen($b[0]);
            if ($al  ==  $bl) {
                return  0;
            }
            return ($al  <  $bl) ? -1  :  1;
        });
        krsort($result);

        foreach ($result as $key => $value) {
            preg_match_all('@' . $value[0] . '@i', $path, $matches);
            if ($matches[0]) {
                self::$URI = preg_replace('@' . $value[0] . '@i', $value[1], $path);
                return true;
            }
        }
    }
    public static function clink($dir, $ext, $path)
    {
        if (preg_match('@' . $dir . '.*?' . preg_quote($ext) . '@', $path)) {
            $clink = '[' . ltrim($path, '/') . ']';
            //如果太多请求 可以把检测移移出以免影响性能
            $check = Article::check($clink, 0, 'clink');
            $check && self::$URI = Route::make(compact('clink'), 'article.php');
            if(self::$URI) return true;
        }
    }
    public static function indexPage($ext, $path)
    {
        if (preg_match('@^/index(_(\d+))*' . preg_quote($ext) . '@', $path, $match)) {
            self::$URI = 'index.php?page=' . intval($match[2]);
        }
    }
    public static function route0($routeArray, $path)
    {
        if (self::$URI = $routeArray[$path]) {
            return;
        } else {
            //匹配 /user/aa/bb
            if (preg_match('@^/\w+/\w+/\w+$@', $path)) {
                list($app, $do, $s) = explode('/', ltrim($path, '/'));
                $_app = iApp::app($app, $sub);
                if (iCMS::$config['apps'][$_app]) {
                    $query = compact('app', 'do');
                    $s && $query['s'] = $s;
                    self::$URI = Route::make($query, 'api.php');
                }
            }
        }
    }
    public static function route1($routeArray, $path)
    {
        foreach ($routeArray as $key => $value) {
            $uri = $value[0];
            $replacement = '(?<\\1>\d+)';
            if (strpos($value[0], 'id}') === false) {
                $replacement = '(?<\\1>\w+)';
            }
            $pattern = preg_replace('/\{(\w+)\}/i', $replacement, $uri);
            preg_quote($pattern, '@');
            preg_match_all('@' . $pattern . '@i', $path, $matches);
            if ($matches[1][0]) {
                self::$URI =  $value[1];
                foreach ($matches as $mkey => $mval) {
                    // var_dump($mkey,$mval);
                    self::$URI = str_replace('{' . $mkey . '}', $mval[0], self::$URI);
                }
            }
        }
    }
    public static function make($REQ)
    {
        $rq = parse_url(self::$URI, PHP_URL_QUERY);
        parse_str($rq, $request);
        parse_str($REQ['query'], $query);
        $_GET = array_merge($request, $query);
        Security::addslash($_GET);
        Waf::check($_GET);
    }

    public static function builder($key, $value)
    {
        preg_match_all("/\{(.*?)\}/", $value, $matches);
        $url = preg_replace("/\{.*?\}/", "%s", $value);
        $urlRule = Route::getRuleEtc();
        $regular   = $value;
        $query = array();
        $app    = $key;
        if (strpos($key, ':') !== false) {
            list($app, $do) = explode(':', $key);
            $do && $query[] = 'do=' . $do;
        }
        $i = 1;
        $param[] = $url;
        foreach ($matches[1] as $k => $v) {
            $reg = $urlRule[$v]['regular'];
            $reg = str_replace("/", "@@", $reg);
            $param[] = $reg;
            if ($urlRule[$v]['query']) {
                $query[] = sprintf('%s=$%d', $urlRule[$v]['query'], $i);
                $i++;
            }
        }
        $regular = call_user_func_array('sprintf', $param);
        $rewrite = $app . '.php?' . implode('&', $query);
        if ($p = Route::$config['iurl'][$key]['page']) {
            $rewrite = str_replace('page=', $p . '=', $rewrite);
        }
        return array($regular, $rewrite);
    }
}
