<?php

class Template {
	public  $vars       = [];
	public  $blocks     = [];
	private $pagevars   = [];
	private $tpl_path   = NULL;
	private $cache_path = NULL;

	public function __construct ($tpl_path, array $pagevars) {
		if(!file_exists($tpl_path)){
			throw new Exception('Error templates folder not found.');
		} else {
			$this->tpl_path   = $tpl_path;
		}

		$this->pagevars = $pagevars;
	}

	public function assign ($vars, $value = null) {
		if (is_array($vars)) {
			$this->vars = array_merge($this->vars, $vars);
		} else if ($value !== null) {
			$this->vars[$vars] = $value;
		}
	}

	public function blockAssign ($name, $array) {
		$this->blocks[$name][] = (array)$array;
	}

	private function compileVars ($var) {
		$newvar = $this->compileVar($var[1]);
		return "<?php echo isset(" . $newvar . ") ? " . $newvar . " : '{" . $var[1] . "}' ?>";
	}

	private function compileVar ($var) {
		if (strpos($var, '.') === false) {
			$var = '$this->vars[\'' . $var . '\']';
		} else {
			$vars = explode('.', $var);
			if (!isset($this->blocks[$vars[0]]) && isset($this->vars[$var[0]]) && gettype($this->vars[$var[0]]) == 'array') {
				$var = '$this->vars[\'' . $vars[0] . '\'][\'' . $vars[1] . '\']';
			} else {
				$var = preg_replace("#(.*)\.(.*)#", "\$_$1['$2']", $var);
			}
		}
		return $var;
	}

	private function compileTags ($match) {
		switch ($match[1]) {
			case 'INCLUDE':
				return "<?php echo \$this->compile('" . $match[2] . "'); ?>";
				break;

			case 'INCLUDEPHP':
				return "<?php echo include(" . PATH . $match[2] . "'); ?>";
				break;

			case 'IF':
				return $this->compileIf($match[2], false);
				break;

			case 'ELSEIF':
				return $this->compileIf($match[2], true);
				break;

			case 'ELSE':
				return "<?php } else { ?>";
				break;

			case 'ENDIF':
				return "<?php } ?>";
				break;

			case 'BEGIN':
				return "<?php if (isset(\$this->blocks['" . $match[2] . "'])) { foreach (\$this->blocks['" . $match[2] . "'] as \$_" . $match[2] . ") { ?>";
				break;

			case 'BEGINELSE':
				return "<?php } } else { { ?>";
				break;

			case 'END':
				return "<?php } } ?>";
				break;
		 }
	}

	private function compileIf ($code, $elseif) {
		$ex = explode(' ', trim($code));
		$code = '';

		foreach ($ex as $value) {
			$chars = strtolower($value);

			switch ($chars) {
				case 'and':
				case '&&':
				case 'or':
				case '||':
				case '==':
				case '!=':
				case '!==':
				case '>':
				case '<':
				case '>=':
				case '<=':
				case '0':
				case is_numeric($value):
					$code .= $value;
					break;

				case 'not':
					$code .= '!';
					break;

				default:
					if (preg_match('/^[A-Za-z0-9_\-\.]+$/i', $value)) {
						$var = $this->compileVar($value);
						$code .= "(isset(" . $var . ") ? " . $var . " : '')";
					} else {
						$code .= '\'' . preg_replace("#(\\\\|\'|\")#", '', $value) . '\'';
					}
					break;
			}
			$code .= ' ';
		}

		return '<?php ' . (($elseif) ? '} else ' : '') . 'if (' . trim($code) . ") { ?>";
	}

	private function compile ($file) {
		$abs_file = $this->tpl_path.'/'.$file;

		$tpl = file_get_contents($abs_file);
		$tpl = preg_replace("#<\?(.*)\?>#", '', $tpl);
		$tpl = preg_replace_callback("#<!-- ([A-Z]+) (.*)? ?-->#U", array($this, 'compileTags'), $tpl);
		$tpl = preg_replace_callback("#{([A-Za-z0-9_\-.]+)}#U", array($this, 'compileVars'), $tpl);

		if (eval(' ?>'.$tpl.'<?php ') === false) {
			$this->error();
		}
	}

	public function error () {
		exit('Fehler im Template!');
	}

	public function render ($file, $data = NULL) {
		$this->assign($this->pagevars);

		if ($data !== NULL) {
			$this->assign($data);
		}

		$this->compile($file.'.tpl');
		exit();
	}
}