<?php
/**
* Web Application Component Toolkit
*
* @link http://www.phpwact.org/
*
* @author Wact Development Team
* @link http://www.phpwact.org/team
*
* @copyright Copyright 2006, Jeff Moore
* @license http://opensource.org/licenses/mit-license.php MIT
*/

/**
* Manage a set of templates.
*/
class Wact_Template_Loader {

    /**
     */
    const RESOURCE_SEGMENT_SEPARATOR = '/';

    /**
     */
    const RESOURCE_SEGMENT_DELIMITER = '_';

    /**
     */
    const FIELD_DELIMITER = '_';

    /**
    */
    protected $reader;

    /**
    * @var Wact_Cache_Store
    */
    protected $cache;

    /**
    */
    protected $compiler;
    
    /**
    */
    public function __construct($reader = NULL, $cache = NULL) {
        if ($reader == NULL) {
            $reader = new Wact_Template_Reader_File();
        }
        
        if ($cache == NULL) {
            $cache = new Wact_Cache_Store_File();
        }

        $this->reader = $reader;
        $this->cache = $cache;
    }
    
    /**
    * Given a resource id, return an object representing that resource.
    * @param string resourceId Resource id
    * @return object
    */
    public function load($resourceId, $baseClass = null, $extra = null) {

        // @todo: We need to leave instantiation to the caller

        $class = $this->getClassName($resourceId, $baseClass, $extra);
        if (class_exists($class, FALSE)) {
            return new $class();
        }

        $cacheId = $this->getCacheId($class);
        if ($this->cache->includeCode($cacheId)) {
            return new $class();
        } else {
            $compiler = $this->getCompiler();
            $compiler->compile($resourceId, $this->cache, $cacheId, $class, $baseClass);
            if ($this->cache->includeCode($cacheId)) {
                return new $class();
            } else {
                throw new Wact_Template_Exception('Could not include compiled file');
            }
        }
    }

    /**
    */
    protected function getCacheId($compiledClassName) {
        $id = implode(array_reverse(explode(self::FIELD_DELIMITER, $compiledClassName)), DIRECTORY_SEPARATOR);

        $id = str_replace(self::RESOURCE_SEGMENT_DELIMITER, '_', $id) . '.php';
        return $id;
    }

    /**
    * Convert a resource specification into a class name.
    * @return string Class name that resource will be compiled to
    */
    public function getClassName($resourceId, $baseClass = null, $extra = null) {

        if (strpos($resourceId . $baseClass . $extra, '_tpl_') !== FALSE) {
            throw new Wact_Template_Exception('Cannot use the character sequence "_tpl_" in a resource id');
        }

        $class = str_replace('.tpl.', '_tpl_', $resourceId);
        $class = str_replace(self::RESOURCE_SEGMENT_SEPARATOR, self::RESOURCE_SEGMENT_DELIMITER, $class);
        
        if (empty($baseClass)) {
            $seppos = (int) strrpos($resourceId, self::RESOURCE_SEGMENT_SEPARATOR);
            if ($resourceId[$seppos + 1] == '_') {
                $baseClass = 'Wact_View_Partial';
            } else {
                $baseClass = 'Wact_View_Root';
            }
        }
        
        $class .= self::FIELD_DELIMITER . $baseClass;
        
        if (!empty($extra)) {
            $class .= self::FIELD_DELIMITER . $extra;
        }

        if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $class)) {
            throw new Wact_Template_Exception('Invalid resource id "{ResourceId}', $resourceId);
        }
        return $class;
    }

    /**
    * get an instance of the Wact_Template_Compiler
    * @return Wact_Template_Compiler
    */
    protected function getCompiler() {
        if (!is_object($this->compiler)) {
            $this->compiler = new Wact_Template_Compiler($this, $this->reader);
        }
        return $this->compiler;
    }
    
}

?>