<?php
/**
 * @brief themeEditor, a plugin for Dotclear 2
 *
 * @package Dotclear
 * @subpackage Plugins
 *
 * @copyright Olivier Meunier & Association Dotclear
 * @copyright GPL-2.0-only
 */
if (!defined('DC_RC_PATH')) {
    return;
}

class dcThemeEditor
{
    protected $core;

    protected $user_theme;
    protected $parent_theme;
    protected $tplset_theme;

    protected $parent_name;
    protected $tplset_name;

    protected $tpl_model;

    public $tpl = [];
    public $css = [];
    public $js  = [];
    public $po  = [];
    public $php = [];

    public function __construct($core)
    {
        $this->core         = &$core;
        $this->user_theme   = path::real($this->core->blog->themes_path . '/' . $this->core->blog->settings->system->theme);
        $this->tplset_theme = DC_ROOT . '/inc/public/default-templates/' . DC_DEFAULT_TPLSET;
        $this->tplset_name  = DC_DEFAULT_TPLSET;
        if (null !== $this->core->themes) {
            $parent_theme = $this->core->themes->moduleInfo($this->core->blog->settings->system->theme, 'parent');
            if ($parent_theme) {
                $this->parent_theme = path::real($this->core->blog->themes_path . '/' . $parent_theme);
                $this->parent_name  = $parent_theme;
            }
            $tplset = $this->core->themes->moduleInfo($this->core->blog->settings->system->theme, 'tplset');
            if ($tplset) {
                $this->tplset_theme = DC_ROOT . '/inc/public/default-templates/' . $tplset;
                $this->tplset_name  = $tplset;
            }
        }
        $this->findTemplates();
        $this->findStyles();
        $this->findScripts();
        $this->findLocales();
        $this->findCodes();
    }

    public function filesList($type, $item = '%1$s', $split = true)
    {
        $files = $this->getFilesFromType($type);

        if (empty($files)) {
            return '<p>' . __('No file') . '</p>';
        }

        $list = '';
        if ($split) {
            $list_theme  = ''; // Files from current theme
            $list_parent = ''; // Files from parent of current theme
            $list_tpl    = ''; // Files from template set used by current theme
            foreach ($files as $k => $v) {
                if (strpos($v, $this->user_theme) === 0) {
                    $li = sprintf('<li class="default-file">%s</li>', $item);
                    $list_theme .= sprintf($li, $k, html::escapeHTML($k));
                } elseif ($this->parent_theme && strpos($v, $this->parent_theme) === 0) {
                    $li = sprintf('<li class="parent-file">%s</li>', $item);
                    $list_parent .= sprintf($li, $k, html::escapeHTML($k));
                } else {
                    $li = sprintf('<li>%s</li>', $item);
                    $list_tpl .= sprintf($li, $k, html::escapeHTML($k));
                }
            }
            $list .= ($list_theme != '' ? sprintf('<li class="group-file">' . __('From theme:') . '<ul>%s</ul></li>', $list_theme) : '');
            $list .= ($list_parent != '' ? sprintf('<li class="group-file">' . __('From parent:') . ' %s<ul>%s</ul></li>',
                $this->parent_name, $list_parent) : '');
            $list .= ($list_tpl != '' ? sprintf('<li class="group-file">' . __('From template set:') . ' %s<ul>%s</ul></li>',
                $this->tplset_name, $list_tpl) : '');
        } else {
            foreach ($files as $k => $v) {
                if (strpos($v, $this->user_theme) === 0) {
                    $li = sprintf('<li class="default-file">%s</li>', $item);
                } elseif ($this->parent_theme && strpos($v, $this->parent_theme) === 0) {
                    $li = sprintf('<li class="parent-file">%s</li>', $item);
                } else {
                    $li = sprintf('<li>%s</li>', $item);
                }
                $list .= sprintf($li, $k, html::escapeHTML($k));
            }
        }

        return sprintf('<ul>%s</ul>', $list);
    }

    public function getFileContent($type, $f)
    {
        $files = $this->getFilesFromType($type);

        if (!isset($files[$f])) {
            throw new Exception(__('File does not exist.'));
        }

        $F = $files[$f];
        if (!is_readable($F)) {
            throw new Exception(sprintf(__('File %s is not readable'), $f));
        }

        return [
            'c'    => file_get_contents($F),
            'w'    => $this->getDestinationFile($type, $f) !== false,
            'type' => $type,
            'f'    => $f
        ];
    }

    public function writeFile($type, $f, $content)
    {
        $files = $this->getFilesFromType($type);

        if (!isset($files[$f])) {
            throw new Exception(__('File does not exist.'));
        }

        try {
            $dest = $this->getDestinationFile($type, $f);

            if ($dest == false) {
                throw new Exception();
            }

            if ($type == 'tpl' && !is_dir(dirname($dest))) {
                files::makeDir(dirname($dest));
            }

            if ($type == 'po' && !is_dir(dirname($dest))) {
                files::makeDir(dirname($dest));
            }

            $fp = @fopen($dest, 'wb');
            if (!$fp) {
                throw new Exception('tocatch');
            }

            $content = preg_replace('/(\r?\n)/m', "\n", $content);
            $content = preg_replace('/\r/m', "\n", $content);

            fwrite($fp, $content);
            fclose($fp);

            # Updating inner files list
            $this->updateFileInList($type, $f, $dest);
        } catch (Exception $e) {
            throw new Exception(sprintf(__('Unable to write file %s. Please check your theme files and folders permissions.'), $f));
        }
    }

    public function deletableFile($type, $f)
    {
        if ($type != 'tpl') {
            // Only tpl files may be deleted
            return false;
        }

        $files = $this->getFilesFromType($type);
        if (isset($files[$f])) {
            $dest = $this->getDestinationFile($type, $f);
            if ($dest) {
                if (file_exists($dest) && is_writable($dest)) {
                    // Is there a model (parent theme or template set) ?
                    if (isset($this->tpl_model[$f])) {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    public function deleteFile($type, $f)
    {
        if ($type != 'tpl') {
            // Only tpl files may be deleted
            return;
        }

        $files = $this->getFilesFromType($type);
        if (!isset($files[$f])) {
            throw new Exception(__('File does not exist.'));
        }

        try {
            $dest = $this->getDestinationFile($type, $f);
            if ($dest) {
                // File exists and may be deleted
                unlink($dest);

                // Updating template files list
                $this->findTemplates();
            }
        } catch (Exception $e) {
            throw new Exception(sprintf(__('Unable to delete file %s. Please check your theme files and folders permissions.'), $f));
        }
    }

    protected function getDestinationFile($type, $f)
    {
        if ($type == 'tpl') {
            $dest = $this->user_theme . '/tpl/' . $f;
        } elseif ($type == 'po') {
            $dest = $this->user_theme . '/locales/' . $f;
        } else {
            $dest = $this->user_theme . '/' . $f;
        }

        if (file_exists($dest) && is_writable($dest)) {
            return $dest;
        }

        if ($type == 'tpl' && !is_dir(dirname($dest))) {
            if (is_writable($this->user_theme)) {
                return $dest;
            }
        }

        if ($type == 'po' && !is_dir(dirname($dest))) {
            if (is_writable($this->user_theme)) {
                return $dest;
            }
        }

        if (is_writable(dirname($dest))) {
            return $dest;
        }

        return false;
    }

    protected function getFilesFromType($type)
    {
        switch ($type) {
            case 'tpl':
                return $this->tpl;
            case 'css':
                return $this->css;
            case 'js':
                return $this->js;
            case 'po':
                return $this->po;
            case 'php':
                return $this->php;
            default:
                return [];
        }
    }

    protected function updateFileInList($type, $f, $file)
    {
        switch ($type) {
            case 'tpl':
                $list = &$this->tpl;

                break;
            case 'css':
                $list = &$this->css;

                break;
            case 'js':
                $list = &$this->js;

                break;
            case 'po':
                $list = &$this->po;

                break;
            case 'php':
                $list = &$this->php;

                break;
            default:
                return;
        }

        $list[$f] = $file;
    }

    protected function findTemplates()
    {
        $this->tpl = array_merge(
            $this->getFilesInDir($this->tplset_theme),
            $this->getFilesInDir($this->parent_theme . '/tpl')
        );
        $this->tpl_model = $this->tpl;

        $this->tpl = array_merge($this->tpl, $this->getFilesInDir($this->user_theme . '/tpl'));

        # Then we look in 'default-templates' plugins directory
        $plugins = $this->core->plugins->getModules();
        foreach ($plugins as $p) {
            // Looking in default-templates directory
            $this->tpl       = array_merge($this->getFilesInDir($p['root'] . '/default-templates'), $this->tpl);
            $this->tpl_model = array_merge($this->getFilesInDir($p['root'] . '/default-templates'), $this->tpl_model);
            // Looking in default-templates/tplset directory
            $this->tpl       = array_merge($this->getFilesInDir($p['root'] . '/default-templates/' . $this->tplset_name), $this->tpl);
            $this->tpl_model = array_merge($this->getFilesInDir($p['root'] . '/default-templates/' . $this->tplset_name), $this->tpl_model);
        }

        uksort($this->tpl, [$this, 'sortFilesHelper']);
    }

    protected function findStyles()
    {
        $this->css = $this->getFilesInDir($this->user_theme, 'css');
        $this->css = array_merge($this->css, $this->getFilesInDir($this->user_theme . '/style', 'css', 'style/'));
        $this->css = array_merge($this->css, $this->getFilesInDir($this->user_theme . '/css', 'css', 'css/'));
    }

    protected function findScripts()
    {
        $this->js = $this->getFilesInDir($this->user_theme, 'js');
        $this->js = array_merge($this->js, $this->getFilesInDir($this->user_theme . '/js', 'js', 'js/'));
    }

    protected function findLocales()
    {
        $langs = l10n::getISOcodes(true, true);
        foreach ($langs as $k => $v) {
            if ($this->parent_theme) {
                $this->po = array_merge($this->po, $this->getFilesInDir($this->parent_theme . '/locales/' . $v, 'po', $v . '/'));
            }
            $this->po = array_merge($this->po, $this->getFilesInDir($this->user_theme . '/locales/' . $v, 'po', $v . '/'));
        }
    }

    protected function findCodes()
    {
        $this->php = $this->getFilesInDir($this->user_theme, 'php');
    }

    protected function getFilesInDir($dir, $ext = null, $prefix = '', $model = null)
    {
        $dir = path::real($dir);
        if (!$dir || !is_dir($dir) || !is_readable($dir)) {
            return [];
        }

        $d   = dir($dir);
        $res = [];
        while (($f = $d->read()) !== false) {
            if (is_file($dir . '/' . $f) && !preg_match('/^\./', $f) && (!$ext || preg_match('/\.' . preg_quote($ext) . '$/i', $f))) {
                if (!$model || preg_match('/^' . preg_quote($model) . '$/i', $f)) {
                    $res[$prefix . $f] = $dir . '/' . $f;
                }
            }
        }

        return $res;
    }

    protected function sortFilesHelper($a, $b)
    {
        if ($a == $b) {
            return 0;
        }

        $ext_a = files::getExtension($a);
        $ext_b = files::getExtension($b);

        return strcmp($ext_a . '.' . $a, $ext_b . '.' . $b);
    }
}
