<?php

namespace HwapX\Core\Library;

use F3;
use Prefab;
use Template as F3Template;
use Base;

use Hwapx\Core\Library\Web;

class Template extends F3Template {
  protected $f3              = null;
  protected $template_dir    = '';
  protected $active_template = 'default';
  protected $module_dir      = '';
  protected $filters         = array();
  protected $minify          = false;

  public function __construct() {
    parent::__construct();
    $f3 = $this->f3          = F3::instance();
    //$this->template    = Template::instance();

    $this->module_dir      = $this->f3->get('app.module.dir');
    $this->template_dir    = $this->f3->get('app.template.dir');
    $this->active_template = $this->f3->get('app.template.active');
    $this->minify          = $this->f3->get('app.template.assets.minify');

    if(!$this->active_template)
      $this->active_template = 'default';

    $this->f3->set('meta.styles.local', array());
    $this->f3->set('meta.styles.remote', array());
    $this->f3->set('meta.styles.raw', array());

    $this->f3->set('meta.scripts.local', array());
    $this->f3->set('meta.scripts.remote', array());
    $this->f3->set('meta.scripts.raw', array());

    $this->f3->set('meta.html', array());

    $this->f3->set('breadcrumbs', array());

    $this->f3->set('UI', $this->template_dir."|".$this->module_dir);

    $this->extend('include', array($this, '_include'));
    $this->extend('exclude', array($this, '_exclude'));
    $this->extend('ignore', array($this, '_ignore'));
    $this->extend('loop', array($this, '_loop'));
    $this->extend('repeat', array($this, '_repeat'));
    $this->extend('check', array($this, '_check'));
    $this->extend('true', array($this, '_true'));
    $this->extend('false', array($this, '_false'));
    $this->extend('switch', array($this, '_switch'));
    $this->extend('case', array($this, '_case'));
    $this->extend('default', array($this, '_default'));
    $this->extend('place', array($this, '_place'));

    $this->filter('date', function($time, $format = null) use($f3) {
      if(is_int($time)) {
        return date($format ?: $f3->get('app.datetime.format'), $time);
      }

      if($format) {
        return date($format, strtotime($time));
      }

      if(preg_match('/^\d{2,4}-\d{1,2}-\d{1,2}$/', $time)) {
        return date($f3->get('app.date.format'), strtotime($time));
      }

      if(preg_match('/^\d{2,4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}$/', $time)) {
        return date($f3->get('app.datetime.format'), strtotime($time));
      }

      if(preg_match('/^\d{1,2}:\d{1,2}:\d{1,2}$/', $time)) {
        return date($f3->get('app.time.format'), strtotime($time));
      }

      return $time;
    });

    $f3 = $this->f3;
    $this->filters['fmt'] = function() use($f3) {
      $args = func_get_args();
      if(count($args) === 1 && is_array($args[0]))
        return call_user_func_array(array($f3, 'format'), $args[0]);
      else
        return call_user_func_array(array($f3, 'format'), $args);
    };
    $this->filter('dict', function() use($f3) {
      $args  = func_get_args();
      if(count($args) === 1 && is_array($args[0])) {
        $args = $args[0];
      }

      if(count($args) > 0)
        if($f3->exists($f3->get('PREFIX').'.'.$args[0]))
          $args[0] = $f3->get($f3->get('PREFIX').'.'.$args[0]);

      return call_user_func_array(array($f3, 'format'), $args);
    });
    $this->filter('upload', function($file) use ($f3) {
      return $f3->get('UPLOADS') . $file;
    });
    $this->filter('resize', function($file, $width, $height, $crop = true, $enlarge = false) use ($f3) {
      $md5   = md5($file);
      $dir   = $f3->get('THUMB');
      $thumb = "$dir/{$md5}_{$width}x{$height}-{$crop}-{$enlarge}.png";
      
      if(file_exists($thumb))
        return $thumb;
        
      if(!file_exists($dir))
        mkdir($dir, 0644);
      
      $img = new \Image($file, false, $f3->get('UPLOADS'));
      $img->resize($width, $height, $crop, $enlarge);
      $f3->write($thumb, $img->dump('png', 9, PNG_ALL_FILTERS));
      
      return $thumb;
    });
  }

  protected function parseFilename($file) {
    $fromTemplate = $this->template_dir.$this->active_template.'/'.$file;

    $parts    = explode('/', $file);
    $vendor   = array_shift($parts);
    $module   = array_shift($parts);
    $path     = implode('/', $parts);

    $file = file_exists($fromTemplate) ? $this->active_template.'/'.$file : "$vendor/$module/views/$path";

    return $file;
  }

  protected function parseAssetFilename($file) {
    $parts    = explode('/', $file);
    $vendor   = array_shift($parts);
    $module   = array_shift($parts);
    $path     = implode('/', $parts);

    $fromModule   = "$vendor/$module/assets/$path";
    $fromTemplate = $this->template_dir.$this->active_template."/$vendor/$module/assets/$path";
    $fromAssets   = 'assets/'.$file;

    if(file_exists($fromTemplate))
      $filepath = $fromTemplate;
    elseif(file_exists($fromModule))
      $filepath = $fromModule;
    elseif(file_exists($fromAssets))
      $filepath = $fromAssets;
    else
      $filepath = null;

    return $filepath;
  }

  public function asset($path, $minify = null) {
    if($minify === null)
      $minify = $this->minify;

    $path = $this->parseAssetFilename($path);

    if(!$path)
      return null;

    $filename = basename($path);
    $dirname  = dirname($path);

    if($minify)
      return Web::instance()->minify($filename, null, true, $dirname);
    else
      return $this->f3->read($path);
  }

  public function render($file, $mime = null, array $hive = null, $ttl = 0) {
    $fromTemplate = $this->template_dir.$this->active_template.'/'.$file;

    $parts    = explode('/', $file);
    $vendor   = array_shift($parts);
    $module   = array_shift($parts);
    $path     = implode('/', $parts);

    $file = file_exists($fromTemplate) ? $this->active_template.'/'.$file : "$vendor/$module/views/$path";

    if($mime == null && file_exists($file)) {
      $mime = mime_content_type($file);
    }

    if($hive)
      $hive = array_merge($this->f3->hive(), $hive);

    return parent::render($file, $mime, $hive, $ttl);
  }

  /*public function defineFilter($name, $func) {
    $this->filters[$name] = $func;
  }

  public function __call($func,array $args) {
    if(isset($this->filters[$func])) {
      return call_user_func_array($this->filters[$func], $args);
    } else {
      return parent::__call($unc, $args);
    }
  }

  /**
  * Convert token to variable
  * @return string
  * @param $str string
  **
  function token($str) {
    $self=$this;
    $ret = preg_replace_callback(
      '/\{\-(.+?)\-\}|\{\{(.+?)\}\}(\n+)?/s',
      function($expr) use($self) {
        if ($expr[1])
          return $expr[1];
        $str=trim($self->token($expr[2]));
        $str=preg_replace_callback('/([^|]+?)\h*\|(\h*\w+(?:\h*[,;]\h*\w+)*)/', function($parts) {
          $result=$parts[1];
          foreach (Base::instance()->split($parts[2]) as $func)
            $result=(($func=='format')?'\Base::instance()':'$this').
              '->'.$func.'('.$result.')';
            return $result;
        }, $str);
        return $str;
      },
      $str
    );

    $ret = trim(preg_replace('/\{\{(.+?)\}\}/s',trim('\1'),
      Base::instance()->compile($ret)));

    return $ret;
  }*/

  /**
  * Convert token to variable
  * @return string
  * @param $str string
  **/
  function token($str) {
    $str=trim(preg_replace('/\{\{(.+?)\}\}/s',trim('\1'),
      Base::instance()->compile($str)));
    if (preg_match('/^(.+)(?<!\|)\|((?:\h*\w+(?:\h*[,;]?))+)/s',
      $str,$parts)) {
      $str=trim($parts[1]);
      foreach (Base::instance()->split($parts[2]) as $func)
        $str=is_string($cmd=$this->filter($func))?$cmd.'('.$str.')':
          '\Base::instance()->call('.
            '$this->filter(\''.$func.'\'),['.$str.'])';
    }
    return $str;
  }

  public function _content($args) {
    $this->f3->push('__place.'.$args['@attrib']['name'], isset($args[0]) ? $args[0] : '');
    return '';
  }

  public function _place($args) {
    $place = $args['@attrib']['name'];

    return "<?php if(\Base::instance()->exists('__place.$place')) echo implode(PHP_EOL, \Base::instance()->get('__place.$place')) ?>";
  }

  public function place($name, $data) {
    /*if(!$this->f3->exists("__place.$name")) {
      $this->f3->set("__place.$name", array());
    }*/
    $this->f3->push("__place.$name", $data);
  }

  /**
  * Render template string
  * @return string
  * @param $str string
  * @param $hive array
  **/
  function rendered($filename,array $hive=array(), $ttl = 0) {
    $hive= array_merge(\Base::instance()->hive(), $hive);

    extract($hive);
    ob_start();
    eval(' ?>'.$this->render($filename, null, $hive, $ttl).'<?php ');
    return ob_get_clean();
  }

  function cached($info, &$data = null) {
    if($result = $cache->exists($this->f3->hash($info), $data))
      return $result;

    foreach ($this->f3->split($this->f3->get('UI')) as $dir)
      if ($result = $cache->exists($this->f3->hash($dir.$info),$data))
        return $result;

    return false;
  }

  function clear($info) {
    if($result = $cache->clear($this->f3->hash($info)))
      return true;

    foreach ($this->f3->split($this->f3->get('UI')) as $dir)
      if ($result = $cache->clear($this->f3->hash($dir.$info)))
        return true;

    return false;
  }
  
  function resolve($node,array $hive=array(), $ttl = 0, $persist = false, $escape = NULL) {
    $hive  = array_merge($this->f3->hive(), $hive);
    
    return parent::resolve($node, $hive, $ttl, $persist, $escape);
  }
}