<?php

namespace common\libraries;

/**
 * Theme manager. Used to create, copy, update themes.
 *
 * To create a theme based on differences only:
 *
 *      Create a "diff" theme containing only differences by calling create_empty_theme. Add differences to it - css, images, templates.
 *      Call make(base_theme, diff_theme, target_theme) where base theme is the base theme you want to copy, diff_theme is the theme that contains your differences and target_theme is the final them to use in Chamilo.
 *      Reaply make if base_theme or diff_them have changed.
 *
 * To update changes from a base theme to an exsiting theme.
 *
 *      Call update(from_them, to_them)
 *
 * This will overwrite existing files based on the file change time stamp. Note that css files will not be merged! To do that create first a diff_theme containing differences and do a make.
 *
 * Note: at this point there is no differences between a diff theme and a normal theme. 
 * Creating diff theme is simply creating a theme that contains only differences from a base theme.
 * As a result diff themes will be listed in the user interface and selected as the main theme. Which should not be done since they will not contain enough information.
 *
 * @copyright (c) 2011 University of Geneva
 * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
 * @author laurent.opprecht@unige.ch
 */
class ThemeManager {

    /**
     * Return the list of resource types to copy from a theme to another. That is CSS, IMAGES and TEMPLATES.
     * 
     * @return array
     */
    public static function get_resource_types() {
        $result = array('css', 'images', 'templates');
        return $result;
    }

    /*
     * List of system themes. That is themes that are necessary for the system to work.
     */
    public static function get_system_themes() {
        $result = array('aqua');
        return $result;
    }

    /**
     * List of existing themes.
     * 
     * @return array
     */
    public static function get_themes() {
        return Theme::get_themes();
    }

    /**
     * Make $target_theme by copying $base_theme and adding the differences contained in $diff_theme.
     * That is CSS files contained in $diff_theme are appended at the end. Taking precendence.
     * Image and Template files contained in $diff_theme overwrite existing files.
     *
     * Note that files already present in $target_theme are deleted!
     *
     * @param string $base_theme
     * @param string $diff_theme
     * @param string $target_theme
     */
    public static function make($base_theme, $diff_theme, $target_theme){
        set_time_limit(60*5);
        ThemeManager::delete($target_theme);
        ThemeManager::update($base_theme, $target_theme);
        ThemeManager::update($diff_theme, $target_theme, true, true);
    }

    /**
     * Create an empty theme. That is the directory theme directory structure but with no file. Can be called several times.
     * 
     * @param string $theme
     * @param string $root
     */
    public static function create_empty_theme($theme, $root = '') {
        $root = $root ? $root : Path::get('SYS_PATH');
        $files = Filesystem :: get_directory_content($root, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
        foreach ($files as $file) {
            $path = $root . '/' . $file;
            if (StringUtilities::start_with($file, '.')) {
                //do nothing those are sys files i.e. mercurial or apache, etc
            } else if ($file == 'resources') {
                $resource_types = self::get_resource_types();
                foreach ($resource_types as $type) {
                    $resource_dir = $path . '/' . $type . '/' . $theme;
                    Filesystem::create_dir($resource_dir);
                }
            } else if (is_dir($path)) {
                self::create_empty_theme($theme, $path);
            } else {
                //do nothing
            }
        }
    }

    /**
     * Delete a theme. Will not delete system themes.
     * Wrapper function to hide the $root parameter.
     *
     * @param string $theme
     * @param string $root
     */
    public static function delete($theme) {
        self::delete_exec($theme);
    }

    /**
     * Copy files from FROM theme to TO theme. Create TO theme if it doesn't exist.
     *
     * @param string $from_theme The theme to copy resources from
     * @param string $to_theme  The theme to copy resources to
     * @param bool $merge If true attemp to merge the FROM files with the TO files. That is for css append the css at the end of the css if it exists.
     * @param string $force_overwrite If true overwrite existing files. By default overwite older files with new ones based on the "change" file timestamp.
     * @param string $root root directory to update from. Leave blank to update everything.
     */
    public static function update($from_theme, $to_theme, $merge = false, $force_overwrite = false, $root = '') {
        $root = $root ? $root : Path::get('SYS_PATH');
        $files = Filesystem :: get_directory_content($root, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
        foreach ($files as $file) {
            $path = $root . '/' . $file;
            if ($comp = StringUtilities::start_with($file, '.')) {
                //do nothing those are sys files i.e. mercurial or apache, etc
            } else if ($file == 'resources') {
                $resource_types = self::get_resource_types();
                foreach ($resource_types as $type) {
                    $f = $path . '/' . $type . '/' . $from_theme;
                    $t = $path . '/' . $type . '/' . $to_theme;
                    self::copy_ressources($f, $t, $type, $merge, $force_overwrite);
                }
            } else if (is_dir($path)) {
                self::update($from_theme, $to_theme, $merge, $force_overwrite, $path);
            } else {
                //do nothing
            }
        }
    }

    /**
     * Delete a theme. Will not delete system themes.
     *
     * @param string $theme
     * @param string $root
     */
    private static function delete_exec($theme, $root = '') {

        //protection against possible attacks
        $theme = str_replace('/', '', $theme);
        $theme = str_replace('\\', '', $theme);

        //protection, system themes which are necessary for the system to work should not be deletable
        $system_themes = self::get_system_themes();
        foreach ($system_themes as $system_theme) {
            if (strtolower($system_theme) == $theme) {
                return false;
            }
        }

        $root = $root ? $root : Path::get('SYS_PATH');

        $files = Filesystem :: get_directory_content($root, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
        foreach ($files as $file) {
            $path = $root . '/' . $file;
            if (StringUtilities::start_with($file, '.')) {
                //do nothing those are sys files i.e. mercurial or apache, etc
            } else if ($file == 'resources') {
                $resource_types = self::get_resource_types();
                foreach ($resource_types as $type) {
                    $resource_dir = $path . '/' . $type . '/' . $theme;
                    Filesystem::remove($resource_dir);
                }
            } else if (is_dir($path)) {
                self::delete_exec($theme, $path);
            } else {
                //do nothing
            }
        }
    }

    private static function copy_ressources($from, $to, $type, $merge, $force_overwrite) {
        self::recurse_copy($from, $to, $type, $merge, $force_overwrite);
    }

    /**
     * 
     */
    private static function recurse_copy($source, $destination, $type, $merge, $force_overwrite) {
        if (!is_dir($source)) {
            return self :: copy_resource($source, $destination, $type, $merge, $force_overwrite);
        }
        $result = true;

        $content = Filesystem :: get_directory_content($source, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
        foreach ($content as $file) {
            $path_to_file = $source . '/' . $file;
            $path_to_new_file = $destination . '/' . $file;
            if (!is_dir($path_to_file)) {
                $result &= self :: copy_resource($path_to_file, $path_to_new_file, $type, $merge, $force_overwrite);
            } else {
                Filesystem :: create_dir($path_to_new_file);
                $result &= self :: recurse_copy($path_to_file, $path_to_new_file, $type, $merge, $force_overwrite);
            }
        }

        return $result;
    }

    /**
     * Copy file to destination if souce is newer OR we do a force overwrite. I.e. we don't ovewrite changes.
     */
    private static function copy_resource($source, $destination, $type, $merge, $force_overwrite) {
        $exists = file_exists($destination);
        if ($exists && $merge && $type == 'css') {
            $data = "\n" . file_get_contents($source);
            return file_put_contents($destination, $data, FILE_APPEND);
        } else if ($exists && !$force_overwrite) {
            $d_time = filectime($destination);
            $s_time = filectime($source);
            if ($s_time < $d_time) {
                return false;
            }
        }

        $destination_dir = dirname($destination);
        if (file_exists($source) && Filesystem :: create_dir($destination_dir)) {
            copy($source, $destination);
        }
    }

}

?>
