<?php
/**
 * Configuration Class
 *
 * @author  Chris Smith <chris@jalakai.co.uk>
 * @author  Ben Coburn <btcoburn@silicodon.net>
 */

namespace dokuwiki\plugin\config\core;

/**
 * Class configuration
 */
class Configuration {

    const KEYMARKER = '____';

    protected $_name = 'conf';     // name of the config variable found in the files (overridden by $config['varname'])
    protected $_format = 'php';    // format of the config file, supported formats - php (overridden by $config['format'])
    protected $_heading = '';      // heading string written at top of config file - don't include comment indicators

    /** @var ConfigParser */
    protected $parser;

    protected $_loaded = false;    // set to true after configuration files are loaded
    protected $_metadata = array();// holds metadata describing the settings

    public $locked = false;     // configuration is considered locked if it can't be updated
    public $show_disabled_plugins = false;

    // configuration filenames
    protected $_default_files = array();
    protected $_local_files = array();      // updated configuration is written to the first file
    protected $_protected_files = array();

    protected $_plugin_list = null;

    /** @var ConfigSettings FIXME better name? */
    protected $confset;


    /**
     * constructor
     *
     * @param string $datafile path to config metadata file
     */
    public function __construct($datafile) {
        global $conf, $config_cascade;

        if(!file_exists($datafile)) {
            msg('No configuration metadata found at - ' . htmlspecialchars($datafile), -1);
            return;
        }

        /** @var array $config gets loaded via include here */
        /* FIXME I think we can remove this
        include($datafile);
        if(isset($config['varname'])) $this->_name = $config['varname'];
        if(isset($config['format'])) $this->_format = $config['format'];
        if(isset($config['heading'])) $this->_heading = $config['heading'];
        */

        $this->_local_files = $config_cascade['main']['local']; // FIXME simplify and remove this later
        $this->locked = $this->_is_locked();

        $this->confset = new ConfigSettings();
    }

    /**
     * Stores setting[] array to file
     *
     * @param string $id Name of plugin, which saves the settings
     * @param string $header Text at the top of the rewritten settings file
     * @param bool $backup backup current file? (remove any existing backup)
     * @return bool succesful?
     */
    public function save_settings($id, $header = '', $backup = true) {
        global $conf;

        if($this->locked) return false;

        // write back to the last file in the local config cascade
        $file = end($this->_local_files);

        // backup current file (remove any existing backup)
        if(file_exists($file) && $backup) {
            if(file_exists($file . '.bak')) @unlink($file . '.bak');
            if(!io_rename($file, $file . '.bak')) return false;
        }

        if(!$fh = @fopen($file, 'wb')) {
            io_rename($file . '.bak', $file);     // problem opening, restore the backup
            return false;
        }

        if(empty($header)) $header = $this->_heading;

        $out = $this->_out_header($id, $header);

        foreach($this->confset->getSettings() as $setting) {
            $out .= $setting->out($this->_name, $this->_format);
        }

        $out .= $this->_out_footer();

        @fwrite($fh, $out);
        fclose($fh);
        if($conf['fperm']) chmod($file, $conf['fperm']);
        return true;
    }

    /**
     * Update last modified time stamp of the config file
     *
     * @return bool
     */
    public function touch_settings() {
        if($this->locked) return false;
        $file = end($this->_local_files);
        return @touch($file);
    }

    /**
     * Returns header of rewritten settings file
     *
     * @param string $id plugin name of which generated this output
     * @param string $header additional text for at top of the file
     * @return string text of header
     */
    protected function _out_header($id, $header) {
        $out = '';
        if($this->_format == 'php') {
            $out .= '<' . '?php' . "\n" .
                "/*\n" .
                " * " . $header . "\n" .
                " * Auto-generated by " . $id . " plugin\n" .
                " * Run for user: " . $_SERVER['REMOTE_USER'] . "\n" .
                " * Date: " . date('r') . "\n" .
                " */\n\n";
        }

        return $out;
    }

    /**
     * Returns footer of rewritten settings file
     *
     * @return string text of footer
     */
    protected function _out_footer() {
        $out = '';
        if($this->_format == 'php') {
            $out .= "\n// end auto-generated content\n";
        }

        return $out;
    }

    /**
     * Configuration is considered locked if there is no local settings filename
     * or the directory its in is not writable or the file exists and is not writable
     *
     * @return bool true: locked, false: writable
     */
    protected function _is_locked() {
        if(!$this->_local_files) return true;

        $local = $this->_local_files[0];

        if(!is_writable(dirname($local))) return true;
        if(file_exists($local) && !is_writable($local)) return true;

        return false;
    }

}

