<?php
class Core_Config extends Zend_Config
{
    static protected $_cacheDir;

    public $dataArray;

    protected $_filename;
    protected $_section;

    /**
     * @param string $cacheDir
     */
    static public function setCacheDir($cacheDir)
    {
        self::$_cacheDir = $cacheDir;
    }

    /**
     * @return string
     */
    static public function getCacheDir()
    {
        return self::$_cacheDir;
    }

    /**
     * @param string $filename
     * @param string $section
     * @param string $token
     *
     * @return bool|string
     */
    static public function getCacheFile($filename, $section = null, $token = null)
    {
        $cacheDir = self::getCacheDir();
        if (null !== $cacheDir) {
            return $cacheDir .'/__Core_Config_'. md5($filename . $section . $token) .'.php';
        }
        return false;
    }

    /**
     * @param array $filename
     * @param string $section
     * @param string $options
     */
    public function __construct($filename, $section = null, $options = null)
    {
        $this
            ->setFilename($filename)
            ->setSection($section);

        $dataArray          = $this->_loadConfig($filename, $section, $options);

        $rawDataArray       = false;
        $allowModifications = false;
        if (is_bool($options)) {
            $allowModifications = $options;
        } elseif (is_array($options)) {
            if (isset($options['allowModifications'])) {
                $allowModifications = (bool) $options['allowModifications'];
            }
            if (isset($options['rawDataArray'])) {
                $rawDataArray = (bool) $options['rawDataArray'];
            }
        }

        if ($rawDataArray) {
            $this->dataArray = $dataArray;
        } else {
            parent::__construct($dataArray, $allowModifications);
        }
    }

    /**
     * @return string
     */
    public function getFilename()
    {
        return $this->_filename;
    }

    /**
     * @param string $filename
     *
     * @return Core_Config
     */
    public function setFilename($filename)
    {
        $this->_filename = $filename;
        return $this;
    }

    /**
     * @return string
     */
    public function getSection()
    {
        return $this->_section;
    }

    /**
     * @param string $section
     *
     * @return Core_Config
     */
    public function setSection($section)
    {
        $this->_section = $section;
        return $this;
    }

    /**
     * @param string $file
     * @param string $section
     * @param string $options
     *
     * @return array|mixed
     * @throws InvalidArgumentException
     */
    protected function _loadConfig($file, $section = null, $options = null)
    {
        $inject = null;
        $token  = null;
        if (isset($options['inject'])) {
            $inject = $options['inject'];
            unset($options['inject']);

            $token  = md5(serialize($inject));
        }

        $cacheFile = self::getCacheFile($file, $section, $token);
        if ($cacheFile
            && file_exists($cacheFile)
            && filemtime($cacheFile) >= filemtime($file)) {
            // cache hit, huzzah!
            return include $cacheFile;
        }

        $suffix = strtolower(pathinfo($file, PATHINFO_EXTENSION));
        switch ($suffix) {
            case 'ini':
                $config = new Core_Config_Ini($file, $section, $options);
                $data   = $config->toArray();
                break;

            case 'xml':
                $config = new Zend_Config_Xml($file, $section, $options);
                $data   = $config->toArray();
                break;

            case 'json':
                $config = new Zend_Config_Json($file, $section, $options);
                $data   = $config->toArray();
                break;

            case 'yml':
            case 'yaml':
                $options = array_merge((array) $options, array('yamldecoder' => array('sfYaml', 'load')));
                $config  = new Zend_Config_Yaml($file, $section, $options);
                $data    = $config->toArray();
                break;

            case 'php':
            case 'inc':
                $data    = include $file;
                break;
            default:
                throw new InvalidArgumentException(sprintf('%s: config format "%s" not supported', get_class(), $suffix));
        }

        // processing config values
        // - stripping trans() marker
        // - injecting values from $inject
        array_walk_recursive($data, array($this, '_process'), $inject);

        // store $data array to cache
        if ($cacheFile) {
            Core_Cache::cache($cacheFile, $data, $file);
        }

        return $data;
    }

    /**
     * @param string $value
     * @param string $key
     * @param Zend_Config $config
     *
     * @throws InvalidArgumentException
     */
    protected function _process(&$value, $key, Zend_Config $config = null)
    {
        $matches = array();        
        if (null !== $config && preg_match_all('/%([a-zA-Z][a-zA-z0-9-_]*(\.[a-zA-Z][a-zA-z0-9-_]*)*)%/us', $value, $matches)) {
            $tokens      = array_unique($matches[0]);
            foreach ($tokens as $token) {
                $current = $config;
                $parts   = explode(".", trim($token, '%'));
                for($i = 0; $i < count($parts); $i++) {
                    if (!isset($current->{$parts[$i]})) {
                        throw new InvalidArgumentException(sprintf('Error injecting config, token %s not found', $token));
                    }
                    $current = $current->{$parts[$i]};
                }
                $value = str_replace($token, $current, $value);
            }
        } else if (preg_match('/^trans\(\"(?<message>.*)\"\)$/', $value, $matches)) {
            $value = $matches['message'];
        }
    }
}