<?php

namespace xmvc;

use xmvc\Cache;

/**
* 视图管理类
*
* @version 2.1.0
*/

class Viewer
{
    public array $list = []; //视图集

    public static ?View $mainView = null; //起始主视图


    /**
     * 视图缓存文件是否有效
     * - 如果缓存文件不存 false在
     * - 如果配置文件设置了 tplCheckFileTime, 模板文件时间大于缓存文件时间 false
     * - 如果是 DEBUG 模式, 模板文件时间大于缓存文件时间 false
     * - 如果是 IS_DEVELOP 模式, 恒为 false
     * @param string $viewName
     * @param string $cacheFilename
     * @return bool
     */
    public static function isViewCacheValid(string $viewName, string $cacheFilename): bool
    {
        return false;
        if (IS_DEVELOP) {
            return false;
        }
        if (MVC::$cfg['cache']['tplCheckFileTime'] || DEBUG) {
            if (!is_file($cacheFilename)) {
                return false;
            }
            $cache = Cache::create(cfg('cache>group>viewtimes>type'), 'viewtimes', cfg('cache>group>viewtimes>configKey'));
            $files = $cache->get($viewName);
            if ($files == null) {
                $files[self::getFilenameByViewName($viewName)] = filemtime($cacheFilename);
            }
            foreach ((array)$files as $filename => $time) {
                if (filemtime($filename) > $time) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 生成所有缓存文件
     */
    public static function createAllCacheFile(string $site)
    {
        rmdirs(DIR_CACHE . '/tpl');
        rmdirs(DIR_CACHE . '/xview');
        // 清空缓存中的文件修改时间
        $cache = Cache::create(cfg('cache>group>viewtimes>type'), 'viewtimes', cfg('cache>group>viewtimes>configKey'));
        $cache->clear();
        $cache->save();
        // 删除自动 js,css
        $findFiles = glob(DIR_STATIC . '/auto/js/' . $site . '.*');
        foreach ($findFiles as $filename) {
            unlink($filename);
        }
        $findFiles = glob(DIR_STATIC . '/auto/css/' . $site . '.*');
        foreach ($findFiles as $filename) {
            unlink($filename);
        }
        // 生成视图
        foreach ([DIR_VIEW . '/' . $site, DIR_MVC . '/xview/tpl'] as $path) {
            forEachDir($path, function ($filename, $basename, $type, $level) {
                if ($type == 'file') {
                    view($filename, null, null, false);
                }
            });
        }
    }

    /**
     * 获取视图名称
     * @param string $filename
     */
    public static function getViewName(string $filename)
    {
        if (strpos($filename, DIR_MVC . '/xview') === false) {
            $absFilename = str_replace(DIR_VIEW . '/', '', $filename);
            $basename = basename($absFilename);
            $basename = substr($basename, 0, strpos($basename, '.'));
            $name = dirname($absFilename) . '/' . $basename;
        } else {
            $absFilename = str_replace(DIR_MVC . '/xview' . '/tpl/', '', $filename);
            $basename = basename($absFilename);
            $basename = substr($basename, 0, strpos($basename, '.'));
            $name = 'x.' . str_replace('/', '.', $basename);
        }
        $name = str_replace('/', '.', $name);
        return $name;
    }

    /**
     * 获取模板文件对应的缓存文件绝对路径
     *
     * @param string $filename
     * @return string
     */
    public static function getViewCacheFilename(string $filename): string
    {
        if (strpos($filename, DIR_MVC . '/xview') !== false) {
            return str_replace(DIR_MVC, DIR_CACHE, $filename);
        } else {
            return str_replace(DIR_VIEW, DIR_CACHE . '/tpl', $filename);
        }
    }

    /**
     * 加载视图
     *
     * @param string $filename 模板文件名
     */
    public function load(string $filename, bool $isMain, string $viewname, bool $isLoop = false)
    {
        if (!isset($this->list[$viewname])) {
            $view = new View($filename, $viewname);
            $view->isLoop = $isLoop;
            $view->viewer = $this;
            if (is_null(self::$mainView)) {
                $view->isMainView = true;
                self::$mainView = $view;
            }
            $view->parse();
            foreach ($view->tagViews as $tag) {
                self::load($tag->filename, false, $tag->name);
            }
            $this->list[$view->name] = $view;

            if ($isMain) {
                $view->createCache(); //生成主视图缓存文件
            }
        }
    }

    /**
     * 通过视图名称取模板文件名
     */
    public static function getFilenameByViewName($viewname)
    {
        $firstDotPos = strpos($viewname, '.');
        if (substr($viewname, 0, $firstDotPos) === 'x') {
            return  DIR_MVC . '/xview/tpl/' . str_replace('.', '/', substr($viewname, $firstDotPos + 1)) . '.tpl.php';
        } elseif ($firstDotPos === 0) {
            // ../../处理
            if (self::$mainView) {
                if (preg_match('/^([\.\/]+)(.*)/', $viewname, $m)) {
                    if ($m[1] == '.' || $m[1] == '..') {
                        $m[1] .= '/';
                    }
                    $m[2] = str_replace('.', '/', $m[2]);
                    $path = fdir(realpath(dirname(dirname(self::$mainView->filename) . '/' . $m[1] . $m[2])));
                    if ($path == '') {
                        error('目录不存在 ' . dirname(dirname(self::$mainView->filename) . '/' . $m[1] . $m[2]));
                    } else {
                        return $path . '/' . basename($m[2]) . '.tpl.php';
                    }
                }
            }
            return  DIR_VIEW . '/' . str_replace('.', '/', substr($viewname, 1)) . '.tpl.php';
        } else {
            return  DIR_VIEW . '/' . str_replace('.', '/', $viewname) . '.tpl.php';
        }
    }

    /**
     * 逐行检查代码在文件中的哪行
     * @param  string $findStr
     * @return int|false
     */
    public static function findStrInFileLine(View $view, string $findStr)
    {
        if ($findStr == '') {
            return -1;
        }
        $lines = explode("\r", $view->contents);
        $count = count($lines);
        for ($i = 0; $i < $count; $i++) {
            $line = $lines[$i];
            if (strpos($line, $findStr) !== false) {
                return $i + 1;
            }
        }
        return -1;
    }

    /**
     * 抛出有问题的代码错误
     */
    public static function error(View $view, string $findStr, string $message)
    {
        $line = self::findStrInFileLine($view, $findStr);
        $backtrace = debug_backtrace(DEBUG_BACKTRACE);
        array_unshift($backtrace, [
            'file' => $view->filename,
            'line' => $line,
            'class' => '模板文件',
            'function' => '',
            'type' => '',
            'args' => []
        ]);
        $error = [
            'type' => E_ERROR,
            'file' => $view->filename,
            'line' => $line,
            'message' => $message,
            'backtrace' => $backtrace
        ];
        ErrorClass::errorMake($error);
        exit();
    }
}
