<?php

namespace pictpostpersonal;

/**
 * 男前テンプレートエンジンのPPPオリジナル拡張版
 */
class NoSixTemplate2
{
    protected $template_dir = null;
    protected $cache_dir = null;
    protected $template = null;
    protected $context = array();
    protected $container = [];

    function __construct($filename = null, $directorys = [], $cachedir = '', $container = [])
    {
        $this->set_template($filename);
        $this->set_cache_dir($cachedir);
        $this->set_dir($directorys);
        $this->container = $container;
    }

    function set_template($filename)
    {
        $this->template = $filename;
    }

    function set_dir($directorys)
    {
        foreach ($directorys as $index => $dir) {
            if (substr($dir, -1) != '/') $directorys[$index] .= '/';
        }
        $this->template_dir = $directorys;
    }

    function set_cache_dir($dir)
    {
        if (substr($dir, -1) != '/') $dir .= '/';
        $this->cache_dir = $dir;
    }

    function set_data($context_key, $context_data, $overwrite = false)
    {
        if (empty($this->context[$context_key]) || $overwrite) {
            $this->context[$context_key] = $context_data;
        } else {
            if (is_array($context_data) && is_array($this->context[$context_key])) {
                $this->context[$context_key] = array_merge($this->context[$context_key], $context_data);
            } else {
                $this->context[$context_key] .= $context_data;
            }
        }
    }
    function set_data_h($context_key, $context_data, $overwrite = false)
    {
        if (is_string($context_data)) {
            $context_data = htmlspecialchars($context_data ?? '');
        }
        if (empty($this->context[$context_key]) || $overwrite) {
            $this->context[$context_key] = $context_data;
        } else {
            if (is_array($context_data) && is_array($this->context[$context_key])) {
                $this->context[$context_key] = array_merge($this->context[$context_key], $context_data);
            } else {
                $this->context[$context_key] .= $context_data;
            }
        }
    }

    function reset($context_key = null)
    {
        if (is_null($context_key)) {
            $this->context = array();
        } else {
            $this->context[$context_key] = null;
        }
    }

    //ファイルの存在チェック
    function template_exists()
    {
        $exists = false;
        foreach ($this->template_dir as $dir) {
            if ($dir == '') {
                continue;
            }
            $filename = $dir . $this->template;
            if (file_exists($filename)) {
                $exists = true;
                break;
            }
        }
        return $exists;
    }

    function convert_template()
    {
        //指定されたディレクトリの中から順にテンプレートを検索する
        $filename = '';
        foreach ($this->template_dir as $dir) {
            if ($dir == '') {
                continue;
            }
            $filename = $dir . $this->template;
            if (file_exists($filename)) {
                $template_dir = $dir;
                break;
            }
        }

        $cachename = $this->cache_dir . $this->template . '.cache';

        //キャッシュがない、またはテンプレートが更新されていたら再生成
        if (!file_exists($cachename) || filemtime($cachename) < filemtime($filename)) {

            if (!file_exists($filename)) {
                //テンプレートファイルが存在しない場合はエラー
                echo 'テンプレートファイルが見つかりません';
                return false;
            }

            $s = file_get_contents($filename);
            $s = $this->convert_string($s, $template_dir);

            if (is_writable($cachename) || is_writable($template_dir)) {
                file_put_contents($cachename, $s);
            }
        }
        return $cachename;
    }

    function convert_string($s, $current_dir)
    {
        // <PPP～>記述を{PPP:}記述に変換
        $s = preg_replace('/\<PPP[ _](.*)\>/i', '{PPP $1}', $s);

        // レイアウトファイル処理
        preg_match('/\{PPP LAYOUT:([^{}\s\=!]+?)\}/i', $s, $match);
        if ($match) {
            $filename = $match[1];
            $filepath = '';
            foreach ($this->template_dir as $dir) {
                if ($dir == '') {
                    continue;
                }
                $tmppath = $dir . $filename;
                if (file_exists($tmppath)) {
                    $filepath = $tmppath;
                    break;
                }
            }
            if ($filepath != '') {
                $layout = file_get_contents($filepath);
                $s = str_replace('{PPP LAYOUT_CONTENTS}', $s, $layout);
                $s = str_replace($match[0], '', $s);
            }

            //テンプレートからレイアウトテンプレートへの固定データ投入
            preg_match_all('/\{PPP LDS:([^\}].+)\}(.+)\{\/PPP LDS\}/is', $s, $matchs, PREG_SET_ORDER);
            if ($matchs) {
                foreach ($matchs as $m) {
                    $s = preg_replace('/\{PPP LD:' . $m[1] . '\}/i', $m[2], $s);
                    $s = preg_replace('/\{PPP LDIF:' . $m[1] . '\}/i', "<?php if($m[2]) { ?>", $s);
                }
                $s = preg_replace('/\{PPP LDS:[^}].+\}(.+)\{\/PPP LDS\}/is', '', $s);
            }
        }

        // インクルード処理
        $include_callback = function ($matches) {
            $filename = $matches[1];
            $filepath = '';
            foreach ($this->template_dir as $dir) {
                if ($dir == '') {
                    continue;
                }
                $tmppath = $dir . $filename;
                if (file_exists($tmppath)) {
                    $filepath = $tmppath;
                    break;
                }
            }
            if ($filepath != '') {
                return file_get_contents($filepath);
            } else {
                return "!! include error ($filename) !!";
            }
        };
        $s = preg_replace_callback('/\{PPP[ _]INCLUDE:([^\}]+)\}/i', $include_callback, $s);

        if (!strstr($current_dir, '/templates_sys/')) {
            $s = str_replace('</head>', '<?php echo $sys_insert_header ?? "" ?></head>', $s);
            $s = preg_replace('/(<body.*?>)/', '$1<?php echo $sys_insert_body_header ?? "" ?>', $s);
            $s = str_replace('</body>', '<?php echo $sys_insert_body_footer ?? "" ?></body>', $s);
        }

        $s = preg_replace('/\{PPP[ _]SET:([^{}\s\=!]+?)\s?=\s?"?(.*?)"?\s?\}/i', '<?php \$$1 = "$2" ?>', $s);

        $s = preg_replace('/\{PPP[ _]SITE_URL\}/i', '<?php echo "$site_url" ?>', $s);

        $s = preg_replace('/\{PPP[ _](ARTICLES):"?(.*?)"?\s?,\s?"?(.*?)"?\s?,\s?"?(.*?)"?\}/i', '<?php [$articles, $total_count] = $api->getArticles("$2", "$3", $4);foreach(\$articles as \$$1_item){ ?>', $s);

        $s = preg_replace('/\{PPP[ _]IF:([^{}\s\=!]+?)\.([^{}\s\=!]+?)\s?==\s?"?(.*?)"?\s?\}/i', '<?php if (isset(\$$1_item["$2"]) && \$$1_item["$2"] == "$2") { ?>', $s);
        $s = preg_replace('/\{PPP[ _]IF:([^{}\s\=!]+?)\s?==\s?"?(.*?)"?\s?\}/i', '<?php if (isset(\$$1) && $$1 == "$2") { ?>', $s);
        $s = preg_replace('/\{PPP[ _]IF:([^{}\s\=!]+?)\.([^{}\s\=!]+?)\s?!=\s?"?(.*?)"?\s?\}/i', '<?php if (isset(\$$1_item["$2"]) && $$1_item["$2"] != "$2") { ?>', $s);
        $s = preg_replace('/\{PPP[ _]IF:([^{}\s\=!]+?)\s?!=\s?"?(.*?)"?\s?\}/i',        '<?php if (isset(\$$1) && $$1 != "$2") { ?>', $s);
        $s = preg_replace('/\{PPP[ _]IF:([^{}\s\=!<>]+?)\.([^{}\s\=!<>]+?)\s?<\s?"?(.*?)"?\s?\}/i', '<?php if (!isset(\$$1_item["$2"]) || $$1_item["$2"] < "$2") { ?>', $s);
        $s = preg_replace('/\{PPP[ _]IF:([^{}\s\=!<>]+?)\s?<\s?"?(.*?)"?\s?\}/i',        '<?php if (!isset(\$$1) || $$1 < "$2") { ?>', $s);
        $s = preg_replace('/\{PPP[ _]IF:([^{}\s\=!<>]+?)\.([^{}\s\=!<>]+?)\s?>\s?"?(.*?)"?\s?\}/i', '<?php if (isset(\$$1_item["$2"]) && $$1_item["$2"] < "$2") { ?>', $s);
        $s = preg_replace('/\{PPP[ _]IF:([^{}\s\=!<>]+?)\s?>\s?"?(.*?)"?\s?\}/i',        '<?php if (isset(\$$1) && $$1 < "$2") { ?>', $s);


        $s = preg_replace('/\{PPP[ _]IF:([^{}\s\=!]+?)\.([^{}\s\=!]+?)\}/i', '<?php if (isset(\$$1_item["$2"]) && \$$1_item["$2"] ) { ?>', $s);
        $s = preg_replace('/\{PPP[ _]ELSE:([^{}\s\=!]+?)\.([^{}\s\=!]+?)\}/i', '<?php } else if (isset(\$$1_item["$2"]) && \$$1_item["$2"]) { ?>', $s);
        $s = preg_replace('/\{PPP[ _]IF:([^{}\s\=!]+?)\}/i', '<?php if (isset(\$$1) && (\$$1)) { ?>', $s);
        $s = preg_replace('/\{PPP[ _]ELSE:([^{}\s\=!]+?)\}/i', '<?php } else if (isset(\$$1) && \$$1){ ?>', $s);
        $s = preg_replace('/\{PPP[ _]ELSE\}/i', '<?php } else { ?>', $s);
        $s = preg_replace('/\{PPP[ _]ENDIF\}/i', '<?php } ?>', $s);
        $s = preg_replace('/\{\/PPP[ _]IF\}/i', '<?php } ?>', $s);


        $s = preg_replace('/\{PPP[ _]LOOP:([^{}\s\=!]+?)\.([^{}\s\=!]+?)\}/i', '<?php foreach(\$$1_item["$2"] as \$$2_item){ ?>', $s);
        $s = preg_replace('/\{PPP[ _]LOOP:([^{}\s\=!]+?)\}/i', '<?php foreach(\$$1 as \$$1_item){ ?>', $s);
        $s = preg_replace('/\{\/PPP[ _](LOOP|ENDIF|ARTICLES)?\}/i', '<?php } ?>', $s);

        $s = preg_replace('/\{PPP:([^{}\s\=!]+?)\.([^{}\s\=!\[\]\[\]]+?_at)\[([^{}\s\=!]+?)\]\}/i', '<?php $tmpdate = new \DateTime(\$$1_item["$2"]);echo $tmpdate->format(\'$3\'); ?>', $s);
        $s = preg_replace('/\{PPP:(([^{}\s\=!\[\]]+?_at))\[([^{}\s\=!]+?)\]\}/i', '<?php $tmpdate = new \DateTime(\$$1_item);echo $tmpdate->format(\'$2\'); ?>', $s);

        $s = preg_replace('/\{PPP:([^{}\s\=!]+?)\.([^{}\s\=!]+?)\}/i', '<?php echo \$$1_item["$2"]; ?>', $s);
        $s = preg_replace('/\{PPP:([^{}\s\=!]+?)\}/i', '<?php echo \$$1; ?>', $s);
        $s = preg_replace('/\{PPP[ _]#:([^{}\s\=!]+?)\.([^{}\s\=!]+?)\}/i', '<?php echo h(\$$1_item["$2"]); ?>', $s);
        $s = preg_replace('/\{PPP[ _]#:([^{}\s\=!]+?)\}/i', '<?php echo h(\$$1); ?>', $s);

        $s = preg_replace('/\{PPP[ _]THEME[ _]IF:([^{}\s\=!]+?)\}/i', '<?php if (isset($theme_item["$1"]["value"]) && ($theme_item["$1"]["value"])) { ?>', $s);
        $s = preg_replace('/\{PPP[ _]THEME:([^{}\s\=!]+?)\}/i', '<?php echo $theme_item["$1"]["value"] ?? ""; ?>', $s);
        $s = preg_replace('/\{PPP[ _]THEME_URL\}/i', '<?php if($settings["theme"] == ""){echo "$site_url/system_resource/def_theme"; } else {echo "$site_url/themes/{$settings[\'theme\']}";} ?>', $s);

        $s = preg_replace('/\{PPP[ _]SETTING:([^{}\s\=!]+?)\}/i', '<?php echo s(\$settings["$1"]); ?>', $s);
        $s = preg_replace('/\{PPP[ _]SETTING C:([^{}\s\=!]+?)\}/i', '<?php if($settings["$1"]){echo "checked";}; ?>', $s);
        $s = preg_replace('/\{PPP[ _]SETTING S:([^{}\s\=!]+?)\s?,\s?(.*?)\.(.*?)?\s?\}/i', '<?php if($settings["$1"] &&  $settings["$1"] == $$2_item["$3"]){echo "selected";}; ?>', $s);
        $s = preg_replace('/\{PPP[ _]SETTING S:([^{}\s\=!]+?)\s?,\s?(.*?)?\s?\}/i', '<?php if($settings["$1"] &&  $settings["$1"] == $2){echo "selected";}; ?>', $s);
        $s = preg_replace('/\{PPP[ _]SETTING IF:([^{}\s\=!]+?)\}/i', '<?php if (isset(\$settings["$1"]) && (\$settings["$1"])) { ?>', $s);

        $s = preg_replace('/\{PPP[ _]CHECKED:([^{}\s\=!]+?)\}/i', '<?php if (isset(\$$1) && $$1) { echo "checked"; }?>', $s);

        $s = preg_replace('/\{PPP[ _]DIR}/', "<?php echo \"\$site_url/$current_dir\" ?>", $s);

        $s = preg_replace('/^<\?xml/', '<<?php ?>?xml', $s);
        $s = preg_replace('/#\{([^{}\s\=!]+?)\}/', '<?php echo $1; ?>', $s);
        $s = preg_replace('/%\{([^{}\s\=!]+?)\}/', '<?php echo htmlspecialchars($1,ENT_QUOTES); ?>', $s);

        return $s;
    }

    function display()
    {
        $container = $this->container; //汎用データコンテナ

        $cache = $this->convert_template();
        if ($cache === false) {
            //テンプレートの展開に失敗
            return;
        }
        extract($this->context);
        include($cache);
    }

    public function getString()
    {
        ob_start();
        $this->display();
        return ob_get_clean();
    }


    /**
     * 指定ファイルに PHP コードが含まれているか判定する
     *
     * @param string $filePath
     * @return bool
     */
    public function containsPhpCode(string $filePath): bool
    {
        if (!is_readable($filePath)) {
            throw new \RuntimeException('File not readable.');
        }

        $content = file_get_contents($filePath);

        // PHP開始タグが存在しない場合は即 false
        if (!preg_match('/<\?(php|=)?/i', $content)) {
            return false;
        }

        // PHPコードとしてトークン解析
        $tokens = @token_get_all($content);

        foreach ($tokens as $token) {
            if (is_array($token)) {
                // PHPタグを検出
                if (in_array($token[0], [T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO], true)) {
                    return true;
                }
            }
        }

        return false;
    }
}
