<?php
namespace SabaiApps\Framework;

/**
 * Template class for managing and rendering templates.
 * Provides methods for displaying, including, rendering, and checking the existence of template files.
 */
class Template {

	private $_dirs, $_paths = array();

	/**
	 * Constructor.
	 *
	 * @param array $dirs Array of directories to search for templates.
	 */
	public function __construct( array $dirs = array() ) {
		$this->_dirs = $dirs;
	}

	/**
	 * Gets the list of template directories.
	 *
	 * @return array Array of template directories.
	 */
	public function getDirs() {
		return $this->_dirs;
	}

	/**
	 * Gets the list of writable template directories.
	 *
	 * @return array Array of writable template directories.
	 */
	public function getWritableDirs() {
		$ret = array();
		foreach ( $this->_dirs as $dir ) {
			if ( is_writable( $dir ) ) {
				$ret[] = $dir;
			}
		}
		return $ret;
	}

	/**
	 * Displays a template file or content.
	 *
	 * @param string|array $templateName Name(s) of the template file(s).
	 * @param array        $vars Variables to pass to the template.
	 * @param string       $extension File extension of the template (default: '.html').
	 * @param bool         $echoContent Whether to echo the content (default: true).
	 * @return mixed Rendered content or void if echoed.
	 * @throws Exception If no valid template file is found.
	 */
	public function display( $templateName, array $vars = array(), $extension = '.html', $echoContent = true ) {
		if ( false !== $ret = $this->includeTemplate( $templateName, $vars, $extension ) ) {
			return $ret;
		}

		if ( isset( $vars['content'] ) ) {
			if ( $echoContent ) {
				echo $vars['content'];
				return;
			}
			return $vars['content'];
		}
		throw new Exception( sprintf( 'No valid template file was found: %s (%s)', implode( ', ', (array) $templateName ), $extension ) );
	}

	/**
	 * Includes a template file.
	 *
	 * @param string|array $templateName Name(s) of the template file(s).
	 * @param array        $vars Variables to pass to the template.
	 * @param string       $extension File extension of the template (default: '.html').
	 * @return mixed Included template content or false if not found.
	 */
	public function includeTemplate( $templateName, array $vars = array(), $extension = '.html' ) {
		foreach ( (array) $templateName as $template_name ) {
			if ( $template_path = $this->exists( $template_name, $extension ) ) {
				return $this->_include( $template_path, $vars );
			}
		}
		return false;
	}

	/**
	 * Includes a specific file.
	 *
	 * @param string $templatePath Path to the template file.
	 * @param array  $vars Variables to pass to the template.
	 * @return mixed Included template content.
	 */
	public function includeFile( $templatePath, array $vars = array() ) {
		return $this->_include( $templatePath, $vars );
	}

	/**
	 * Renders a template file and returns the output as a string.
	 *
	 * @param string|array $templateName Name(s) of the template file(s).
	 * @param array        $vars Variables to pass to the template.
	 * @param string       $extension File extension of the template (default: '.html').
	 * @return string Rendered template content.
	 * @throws Exception If rendering fails.
	 */
	public function render( $templateName, array $vars = array(), $extension = '.html' ) {
		ob_start();
		try {
			$this->display( $templateName, $vars, $extension );
		} catch ( \Exception $e ) {
			ob_end_clean();
			throw $e;
		}
		return ob_get_clean();
	}

	/**
	 * Checks if a template file exists.
	 *
	 * @param string $templateName Name of the template file.
	 * @param string $extension File extension of the template (default: '.html').
	 * @return string|false Path to the template file if found, false otherwise.
	 */
	public function exists( $templateName, $extension = '.html' ) {
		// Template name is a full template path?
		if ( strpos( $templateName, '/' ) === 0 ) {
			$original_template_name = $templateName; // save for later use
			$templateName           = basename( $templateName );
		}
		// Already resolved?
		if ( isset( $this->_paths[ $templateName ][ $extension ] ) ) {
			return $this->_paths[ $templateName ][ $extension ];
		}
		// Search all template directories
		$template_file = $templateName . $extension . '.php';
		foreach ( $this->_dirs as $template_dir ) {
			$template_path = $template_dir . '/' . $template_file;
			if ( file_exists( $template_path ) ) {
				$this->_paths[ $templateName ][ $extension ] = $template_path;
				return $template_path;
			}
		}
		// Search full path if it was originally requested that way
		if ( isset( $original_template_name )
			&& file_exists( $template_path = $original_template_name . $extension . '.php' )
		) {
			$this->_paths[ $templateName ][ $extension ] = $template_path;
			return $template_path;
		}
		// Template not found
		$this->_paths[ $templateName ][ $extension ] = false;
		return false;
	}

	/**
	 * Includes a template file and extracts variables.
	 *
	 * @param string $templatePath Path to the template file.
	 * @param array  $vars Variables to pass to the template.
	 * @return mixed Included template content.
	 */
	private function _include() {
		extract( func_get_arg( 1 ), EXTR_SKIP );
		return include func_get_arg( 0 );
	}
}
