<?php

/**
 * Šablonovací systém
 *
 * @author Milan Lempera
 * @package Classes
 *
 * třída je odvozena od bTemplate a je šířena pod MIT licencí (/licence/bTemplate_licence.txt)
 *
 */

class Template {

  /**
   * cesta ke složce šablony
   * @var string
   */
  private $path = '';

  /**
   * začátek tagu
   * @var string
   */
  private $__btag = '<';

  /**
   * konec tagu
   * @var string
   */
  private $__etag = ' />';

  /**
   * loop - začátek start tagu
   * @var string
   */
  private $__bsloop = '<';

  /**
   * konec start tagu
   * @var string
   */
  private $__esloop = '>';


  /**
   * začátek end tagu
   * @var string
   */
  private $__beloop = '</';

  /**
   * konec end tagu
   * @var string
   */
  private $__eeloop = '>';


  /**
   * tagy pro nahrazení
   * @var array
   */
  private $scalars = array();

  /**
   * smyčky pro nahrazení
   * @var array
   */
  private $arrays  = array();

  /**
   * podmíněné smyčky pro nahrazení
   * @var array
   */
  private $carrays = array();

  /**
   * podmínky pro nahrazení
   * @var array
   */
  private $ifs = array();

  /**
   * Konstruktor
   *
   * @param $path
   */
  public function __construct() {
  }

  /**
   * Nastavení hodnoty tagu
   * @param $tag
   * @param $var
   * @param $if = NULL
   */
  public function set($tag, $var, $if = NULL) {
    if(is_array($var)) {
      $safe_var = array();
      //$this->arrays[$tag] = $var;
      foreach($var as $row) {
        $safe_row = array();
        foreach($row as $key=>$value) {
          $safe_row[$key] = htmlspecialchars($value);
        }
        $safe_var[] = $safe_row;
      }
      $this->arrays[$tag] = $safe_var;
      if($if) {
        $result = $var ? TRUE : FALSE;
        $this->ifs[] = $tag;
        $this->scalars[$tag] = $result;
      }
    }
    else {
      //$this->scalars[$tag] = $var;      
      $this->scalars[$tag] = htmlspecialchars($var);
      if($if) {
        $this->ifs[] = $tag;
      }
    }
  }
  
  /**
   * Nastavení hodnoty tagu (povolené HTML)
   * @param $tag
   * @param $var
   * @param $if = NULL
   */
  public function setHTML($tag, $var, $if = NULL) {
    if(is_array($var)) {
      $this->arrays[$tag] = $var;
      if($if) {
        $result = $var ? TRUE : FALSE;
        $this->ifs[] = $tag;
        $this->scalars[$tag] = $result;
      }
    }
    else {
      $this->scalars[$tag] = $var;
      if($if) {
        $this->ifs[] = $tag;
      }
    }
  }

  /**
   * Nastavení podmíněné smyčky
   * @param $tag
   * @param $array
   * @param $cases
   */
  function setCloop($tag, $array, $cases) {
    $this->carrays[$tag] = array(
  		'array' => $array,
		'cases' => $cases
    );
  }

  /**
   * Reset proměnných
   * @param $scalars
   * @param $arrays
   * @param $carrays
   * @param $ifs
   */
  function resetVars($scalars, $arrays, $carrays, $ifs) {
    if($scalars)
    $this->scalars = array();
    if($arrays)
    $this->arrays  = array();
    if($carrays)
    $this->carrays = array();
    if($ifs)
    $this->ifs     = array();
  }

  /**
   * Vrací pole začátek a konec tagu
   * @param $tag
   * @param $directive
   * @return array
   */
  function getTags($tag, $directive) {
    $tags['b'] = $this->__bsloop . $directive . $tag . $this->__esloop;
    $tags['e'] = $this->__beloop . $directive . $tag . $this->__eeloop;
    return $tags;
  }

  /**
   * Vrací tag
   * @param $tag
   * @return string
   */
  function getTag($tag) {
    return $this->__btag . 'tag:' . $tag . $this->__etag;
  }

  /**
   * Vyhlédá tag a vrátí ho
   * @param $t
   * @param $contents
   * @return string
   */
  function getStatement($t, &$contents) {

    $tag_length = strlen($t['b']);
    $fpos = strpos($contents, $t['b']) + $tag_length;
    $lpos = strpos($contents, $t['e']);
    $length = $lpos - $fpos;

    return substr($contents, $fpos, $length);
  }

  /**
   * Vložení proměnných do šablony
   * @param $contents
   * @return string
   */
  private function parse($contents, $reset_vars = true) {
    // podmínky
    if(!empty($this->ifs)) {
      foreach($this->ifs as $value) {
        $contents = $this->parseIf($value, $contents);
      }
    }

    // tagy
    foreach($this->scalars as $key => $value) {
      $contents = str_replace($this->getTag($key), $value, $contents);
    }

    // smyčky
    foreach($this->arrays as $key => $array) {
      $contents = $this->parseLoop($key, $array, $contents);
    }

    // podmíněné smyčky
    foreach($this->carrays as $key => $array) {
      $contents = $this->parseCloop($key, $array, $contents);
    }

    // reset proměnných
    if($reset_vars)
    $this->resetVars(FALSE, TRUE, TRUE, FALSE);

    return $contents;
  }

  /**
   * Parsování výrazu if
   * @param $tag
   * @param $contents
   * @return string
   */
  private function parseIf($tag, $contents) {
    $t = $this->getTags($tag, 'if:');
    $entire_statement = $this->getStatement($t, $contents);

    $tags['b'] = NULL;
    $tags['e'] = $this->__bsloop . 'else:' . $tag . $this->__esloop;

    if(($else = strpos($entire_statement, $tags['e']))) {
      $if = $this->getStatement($tags, $entire_statement);
      $else = substr($entire_statement, $else + strlen($tags['e']));
    }
    else {
      $else = NULL;
      $if = $entire_statement;
    }

    $this->scalars[$tag] ? $replace = $if : $replace = $else;

    return str_replace($t['b'] . $entire_statement . $t['e'], $replace, $contents);
  }

  /**
   * Parsování smyčky
   * @param $tag
   * @param $array
   * @param $contents
   * @return string
   */
  function parseLoop($tag, $array, $contents) {
    $t = $this->getTags($tag, 'loop:');
    $loop = $this->getStatement($t, $contents);
    $parsed = NULL;
    // procházení jesnotlivých smyček
    foreach($array as $key => $value) {
      if(is_numeric($key) && is_array($value)) {
        $i = $loop;
        foreach($value as $key2 => $value2) {
          if(!is_array($value2)) {
            // Nahrazení asociativních polí
            $i = str_replace($this->getTag($tag . '[].' . $key2), $value2, $i);
          }
          else {
            // Parsování smyčky
            $i = $this->parseLoop($tag . '[].' . $key2, $value2, $i);
          }
        }
      }
      elseif(is_string($key) && !is_array($value)) {
        $contents = str_replace($this->getTag($tag . '.' . $key), $value, $contents);
      }
      elseif(!is_array($value)) {
        $i = str_replace($this->getTag($tag . '[]'), $value, $loop);
      }

      // přidání iterace
      if(isset($i)) {
        $parsed .= rtrim($i);
      }
    }

    return str_replace($t['b'] . $loop . $t['e'], $parsed, $contents);
  }

  /**
   * Parsování podmíněné smyčky
   * @param $tag
   * @param $array
   * @param $contents
   * @return string
   */
  function parseCloop($tag, $array, $contents) {
    // Get the tags & loop
    $t = $this->getTags($tag, 'cloop:');
    $loop = $this->getStatement($t, $contents);

    // Set up the cases
    $array['cases'][] = 'default';
    $case_content = array();
    $parsed = NULL;

    // Get the case strings
    foreach($array['cases'] as $case) {
      $ctags[$case] = $this->getTags($case, 'case:');
      $case_content[$case] = $this->getStatement($ctags[$case], $loop);
    }

    // Process the loop
    foreach($array['array'] as $key => $value) {
      if(is_numeric($key) && is_array($value)) {
        // Set up the cases
        if(isset($value['case']))
        $current_case = $value['case'];
        else
        $current_case = 'default';

        unset($value['case']);
        $i = $case_content[$current_case];

        // Loop through each value
        foreach($value as $key2 => $value2) {
          $i = str_replace($this->getTag($tag . '[].' . $key2), $value2, $i);
        }
      }

      // Add the parsed iteration
      $parsed .= rtrim($i);
    }

    // Parse & return the final loop
    return str_replace($t['b'] . $loop . $t['e'], $parsed, $contents);
  }

  /**
   * Fetch file
   * @param $file_name
   * @return string
   */
  function fetch($file_name, $clear = true, $relink = true) {

    $file = TEMPLATE.DIRSEP.$this->path.DIRSEP.$file_name;
    Debug::addPoint('Template::fetch ',$file);
    $fp = fopen($file, 'rb');
    if(!$fp)
    return FALSE;

    $contents = fread($fp, filesize($file));
    fclose($fp);
    $html = $this->parse($contents);

    if ($relink == true) {
      $html = $this->relink($html);
    }

    if ($clear == true) {
      $html = $this->clear($html);
    }
    return $html;
  }

  /**
   * Fetch string
   * @param $file_name
   * @return string
   */
  function fetchString($string, $clear = true, $relink = true) {
    $html = $this->parse($contents);

    if ($relink == true) {
      $html = $this->relink($html);
    }

    if ($clear == true) {
      $html = $this->clear($html);
    }

    return $html;
  }

  private function relink($html) {
    $registry = Registry::getInstance();
    $url = empty($registry->url) ? URL : $registry->url;
    $path = TEMPLATE.DIRSEP.$this->path;

    $html = preg_replace("/<link([^>]+)href=\"(?!http|ftp)(.+)?\"([^>]+)\/>/", "<link\\1href=\"".$url.$path ."/\\2\"\\3/>", $html);
    $html = preg_replace("/<img([^>]+)src=\"(?!http|ftp)(.+)?\"([^>]+)\/>/", "<img\\1src=\"".$url.$path.'/'."\\2\"\\3/>", $html);
    return $html;
  }

  /**
   * Vyčištění šablony od nevyplněných tagů a prázdných míst 
   * @param $html, $space = true
   * @return	string
   */
  private function clear($html, $space = false) {
    $clear = array();
    $clear[] = '@<tag:.*/>@iuU';
    $clear[] = '@<sub:.*/>@iuU';
    $clear[] = '@<loop:.*</loop.*>@iusU';
    $clear[] = '@<cloop:.*</cloop.*>@iusU';
    $clear[] = '@<if:.*</if.*>@iusU';

    $replace = array('', '', '', '');

    if ($space == true) {
      $clear[] 	  = "/(?:(?)|(?))(\s+)(?=\<\/?)/";
      // @>[[:space:]]{2,}<@iusU';
      $replace[]  = '';
    }
    	
    $ret = preg_replace($clear,$replace,$html);
    return $ret;
  }

  public function setPath($path) {
  	$this->path = $path;
  }
}
?>