<?php
   define('TPL_VAR_PREFIX', '$');
   define('TPL_VAR_SUFFIX', '$');
   define('TPL_ARR_PREFIX', '[');
   define('TPL_ARR_SUFFIX', ']');
   define('TPL_SUB_PREFIX', '{');
   define('TPL_SUB_SUFFIX', '}');
   define('TPL_COND_IF',    '??');
   define('TPL_COND_OR',     '::');
   define('TPL_COMMENT_START', '$#');
   define('TPL_COMMENT_END',   '#$');

   v3_factory::load_module('db');

   function _pq($string, $delim='/')
   {
      return preg_quote($string, $delim);
   }

   class v3_template
   {
      protected static $templates;
      protected $_content;
      protected $_name;

      function __construct($name=false, $content=false)
      {
         $this->set($name, $content);
      }

      protected function _load($name, $db=false)
      {
         if(isset(self::$templates[$name]))
            return self::$templates[$name];
         else {
            if($db === false)
               $db = new v3_db(true);
            $query = sprintf(
               "SELECT `name`, `content` FROM `%s` WHERE `name`='%s'",
               V3_IO_TEMPLATES, $name
            );
            $res = $db->query($query);
            $row = $db->fetch_assoc($res);
            self::$templates[$name] = $row;

            return $row;
         }
      }

      public function load($name=false)
      {
         $tpl_data = $this->_load($name !== false ? $name : $this->name, $db);

         $this->set($tpl_data['name'], $tpl_data['content']);
      }

      public function get()
      {
         return array(
            'name'    => $this->name(),
            'content' => $this->content()
         );
      }

      public function content()
      {
         return $this->_content;
      }

      public function name()
      {
         return $this->_name;
      }

      public function set($name=false, $content=false)
      {
         $this->set_name(_or($name, $this->_name));
         $this->set_content(_or($content, $this->_content));
      }

      public function set_content($content)
      {
         $this->_content = preg_replace(
            "|\\" . TPL_COMMENT_START . '.*?' . TPL_COMMENT_END . '|s',
            '',
            $content
         );
      }

      public function set_name($name)
      {
         $this->_name    = $name;
      }

      protected function _parse($tpl, $data, $entitise=true)
      {
         $out = $tpl;

         foreach($data as $key => $value) {
            $arr_regex = '/' . _pq(TPL_VAR_PREFIX . $key . TPL_ARR_PREFIX) .
                         '(.*)' . _pq(TPL_ARR_SUFFIX . TPL_VAR_SUFFIX) . '/';
            $cond_regex = '/' . _pq(TPL_SUB_PREFIX . $key .  TPL_COND_IF) . '(.*?)'
                              . _pq(TPL_COND_OR) . '(.*?)' . _pq(TPL_SUB_SUFFIX) . '/';

            if(strstr($out, TPL_SUB_PREFIX . $key . TPL_SUB_SUFFIX)) {
               $sub_repeat       = TPL_SUB_PREFIX . $key . '/' . TPL_SUB_SUFFIX;
               $start_tag        = _pq(TPL_SUB_PREFIX . $key . TPL_SUB_SUFFIX);
               $end_tag          = _pq(TPL_SUB_PREFIX . '/' . $key . TPL_SUB_SUFFIX);
               $before_start_tag = _pq(TPL_SUB_PREFIX . "$key:before"  . TPL_SUB_SUFFIX);
               $before_end_tag   = _pq(TPL_SUB_PREFIX . "/$key:before" . TPL_SUB_SUFFIX);
               $after_start_tag  = _pq(TPL_SUB_PREFIX . "$key:after"   . TPL_SUB_SUFFIX);
               $after_end_tag    = _pq(TPL_SUB_PREFIX . "/$key:after"  . TPL_SUB_SUFFIX);

               if(is_array($value)) {
                  while(preg_match("/$start_tag\\s*(.*?)\\s*$end_tag/s", $out, $match)) {
                     $sub_exp = $match[0];
                     $sub_tpl = $match[1];
                     $sub_out = $sub_before_out = $sub_after_out = '';

                     if(preg_match("/$before_start_tag\\s*(.*?)\\s*$before_end_tag/s", $sub_exp, $bmatch)) {
                        $sub_before_exp = $bmatch[0];
                        $sub_before_out = $bmatch[1];
                        $sub_tpl = str_replace($sub_before_exp, '', $sub_tpl);
                     }
                     if(preg_match("/$after_start_tag\\s*(.*?)\\s*$after_end_tag/s", $sub_exp, $amatch)) {
                        $sub_after_exp = $amatch[0];
                        $sub_after_out = $amatch[1];
                        $sub_tpl = str_replace($sub_after_exp, '', $sub_tpl);
                     }

                     foreach($value as $sub_key => $sub_data) {
                        if(is_array($sub_data)) {
                           foreach($sub_data as $sk => $sv) {
                              unset($sub_data[$sk]);
                              $sub_data["$key:$sk"] = $sv;
                           }
                        } else {
                           $sub_data[$sub_key] = $sub_data;
                        }
                        $sub_out .= $this->_parse($sub_tpl, $sub_data, $entitise);
                     }

                     $sub_out = $sub_before_out . $sub_out . $sub_after_out;
                     $out = str_replace($sub_exp, $sub_out, $out);
                  }
                  $out = str_replace($sub_repeat, $sub_out, $out);
               } else {
                  $out = preg_replace("/$start_tag(.*?)$end_tag/s", $value, $out);
                  $out = str_replace($sub_repeat, '', $out);
               }
            } else if(preg_match($arr_regex, $out)) {
               if(is_array($value)) {
                  while(preg_match($arr_regex, $out, $match)) {
                     $sub_exp   = $match[0];
                     $sub_index = $match[1];
                     $sub_val = isset($value[$sub_index]) ? $value[$sub_index] : '';
                     $out = str_replace($sub_exp, $sub_val, $out);
                  }
               } else {
                  $out = preg_replace($arr_regex, '', $out);
               }
            } else if(preg_match($cond_regex, $out, $match)) {
               list($cond_exp, $cond_true, $cond_false) = $match;
               if($value !== '' && $value !== false && $value !== 0)
                  $cond_tpl = $cond_true;
               else
                  $cond_tpl = $cond_false;

               $cond_out = $this->_parse($cond_tpl, is_array($value) ? $value : array($key => $value));

               $out = str_replace($cond_exp, $cond_out, $out);
            }

            if(!is_array($value)) {
               $out = str_replace(
                  TPL_VAR_PREFIX . $key . TPL_VAR_SUFFIX,
                  $entitise ? htmlentities($value) : $value,
                  $out
               );
            }
         }

         return $out;
      }

      public function parse($data, $entitise=false)
      {
         return $this->_parse($this->content(), $data, $entitise);
      }
   }
?>