<?php
/**
* @version	$Id$
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2013 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

class kSystemConfig {

	/**
	 * Path to config file.
	 *
	 * @var string
	 */
	protected $file = '';

	/**
	 * Parsed configuration data.
	 *
	 * @var array
	 */
	protected $data = array();

	/**
	 * Tells, that config was changed.
	 *
	 * @var boolean
	 */
	protected $isChanged = false;

	/**
	 * In strict mode an exception is thrown on any inconsistency within system config.
	 *
	 * @var boolean
	 */
	protected $strictMode = true;

	/**
	 * Creates object to access system config.
	 *
	 * @param boolean $parse_section Whatever or not to parse sub-sections.
	 * @param boolean $strict        Strict mode.
	 */
	public function __construct($parse_section = false, $strict = true)
	{
		$this->parseSections = $parse_section;
		$this->strictMode = $strict;
		$this->file = FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'config.php';
	}

	/**
	 * Returns default config values.
	 *
	 * @return array
	 */
	protected function getDefaults()
	{
		$ret = array(
			'AdminDirectory' => '/admin',
			'AdminPresetsDirectory' => '/admin',
			'ApplicationClass' => 'kApplication',
			'ApplicationPath' => '/core/kernel/application.php',
			'CacheHandler' => 'Fake',
			'CmsMenuRebuildTime' => 10,
			'DomainsParsedRebuildTime' => 2,
			'EditorPath' => '/core/ckeditor/',
			'EnableSystemLog' => '0',
			'MemcacheServers' => 'localhost:11211',
			'CompressionEngine' => '',
			'RestrictedPath' => DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . '.restricted',
			'SectionsParsedRebuildTime' => 5,
			'StructureTreeRebuildTime' => 10,
			'SystemLogMaxLevel' => 5,
			'TemplateMappingRebuildTime' => 5,
			'TrustProxy' => '0',
			'UnitCacheRebuildTime' => 10,
			'WebsiteCharset' => 'utf-8',
			'WebsitePath' => rtrim(preg_replace('/'.preg_quote(rtrim(defined('REL_PATH') ? REL_PATH : '', '/'), '/').'$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/'),
			'WriteablePath' => DIRECTORY_SEPARATOR . 'system',
		);

		return $this->parseSections ? array('Misc' => $ret) : $ret;
	}

	/**
	 * Parses "/system/config.php" file and writes the result to the data variable
	 *
	 * @return array
	 * @throws kSystemConfigException When something goes wrong.
	 */
	public function parse()
	{
		if ( !$this->exists() ) {
			if ( $this->strictMode ) {
				throw new kSystemConfigException(sprintf('System config at "%s" not found', $this->file));
			}

			return array();
		}
		elseif ( !is_readable($this->file) ) {
			throw new kSystemConfigException(sprintf('System config at "%s" could not be opened', $this->file));
		}

		$contents = file($this->file);

		if ( $contents && $contents[0] == '<' . '?' . 'php die() ?' . ">\n" ) {
			// format of "config.php" file before 5.1.0 version
			array_shift($contents);

			return parse_ini_string(implode('', $contents), $this->parseSections);
		}

		$_CONFIG = array();
		require($this->file);

		if ( $this->parseSections ) {
			if ( isset($_CONFIG['Database']['LoadBalancing']) && $_CONFIG['Database']['LoadBalancing'] ) {
				require FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'db_servers.php';
			}

			return $_CONFIG;
		}

		$ret = array();

		foreach ($_CONFIG as $section_variables) {
			$ret = array_merge($ret, $section_variables);
		}

		if ( !count($ret) && $this->strictMode ) {
			throw new kSystemConfigException(sprintf('System config at "%s" could is empty', $this->file));
		}

		return $ret;
	}

	/**
	 * Returns parsed variables from "config.php" file
	 *
	 * @return array
	 */
	public function getData()
	{
		if ( !$this->data ) {
			$this->data = array_replace_recursive($this->getDefaults(), $this->parse());
		}

		return $this->data;
	}

	/**
	 * Checks if given section is present in config.
	 *
	 * @param string $section
	 *
	 * @return boolean
	 */
	function sectionFound($section)
	{
		return $this->parseSections ? array_key_exists($section, $this->getData()) : false;
	}

	/**
	 * Returns config value
	 *
	 * @param string $key     Key name.
	 * @param string $section Section name.
	 * @param mixed  $default Default value.
	 *
	 * @return string
	 */
	public function get($key, $section = null, $default = false)
	{
		$data = $this->getData();

		if ( $this->parseSections ) {
			return isset($data[$section][$key]) ? $data[$section][$key] : $default;
		}

		return isset($data[$key]) ? $data[$key] : (isset($section) ? $section : $default);
	}

	/**
	 * Checks, if a configuration file exists on disk.
	 *
	 * @return boolean
	 */
	public function exists()
	{
		return file_exists($this->file);
	}

	/**
	 * Returns config status - is changed or not changed
	 *
	 * @return bool
	 */
	public function isChanged()
	{
		return $this->isChanged;
	}

	/**
	 * Sets value to system config (yet saveConfig must be called to write it to file)
	 *
	 * @param string $key     Key name.
	 * @param string $section Section name.
	 * @param mixed  $value   Value.
	 *
	 * @return void
	 */
	public function set($key, $section, $value = null)
	{
		$this->getData();
		$this->isChanged = true;

		if ( isset($value) ) {
			// create section, when missing
			if ( !array_key_exists($section, $this->data) ) {
				$this->data[$section] = array();
			}

			// create key in section
			$this->data[$section][$key] = $value;

			return;
		}

		unset($this->data[$section][$key]);
	}

	/**
	 * Saves config data to the file
	 *
	 * @param boolean $silent
	 *
	 * @return void
	 * @throws Exception
	 */
	public function save($silent = false)
	{
		if ( !is_writable($this->file) && !is_writable(dirname($this->file)) ) {
			$error_msg = 'Cannot write to "' . $this->file . '" file';

			if ( $silent ) {
				trigger_error($error_msg, E_USER_WARNING);

				return;
			}

			throw new Exception($error_msg);
		}

		$fp = fopen($this->file, 'w');
		fwrite($fp, '<' . '?' . 'php' . "\n\n");

		foreach ( $this->getData() as $section_name => $section_data ) {
			foreach ( $section_data as $key => $value ) {
				fwrite($fp, '$_CONFIG[\'' . $section_name . '\'][\'' . $key . '\'] = \'' . addslashes($value) . '\';' . "\n");
			}

			fwrite($fp, "\n");
		}

		fclose($fp);

		if ( function_exists('opcache_invalidate') ) {
			opcache_invalidate($this->file);
		}

		$this->isChanged = false;
	}

}


class kSystemConfigException extends Exception
{

}
