<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think\Template\TagLib;

use Think\Template\TagLib;

/**
 * CX标签库解析类
 */
class Cx extends TagLib
{

    // 标签定义
    protected $tags = array(
        // 标签定义： attr 属性列表 close 是否闭合（0 或者1 默认1） alias 标签别名 level 嵌套层次
        'php'        => array(),
        'volist'     => array('attr' => 'name,id,offset,length,key,mod', 'level' => 3, 'alias' => 'iterate'),
        'foreach'    => array('attr' => 'name,item,key', 'level' => 3),
        'if'         => array('attr' => 'condition', 'level' => 2),
        'elseif'     => array('attr' => 'condition', 'close' => 0),
        'else'       => array('attr' => '', 'close' => 0),
        'switch'     => array('attr' => 'name', 'level' => 2),
        'case'       => array('attr' => 'value,break'),
        'default'    => array('attr' => '', 'close' => 0),
        'compare'    => array('attr' => 'name,value,type', 'level' => 3, 'alias' => 'eq,equal,notequal,neq,gt,lt,egt,elt,heq,nheq'),
        'range'      => array('attr' => 'name,value,type', 'level' => 3, 'alias' => 'in,notin,between,notbetween'),
        'empty'      => array('attr' => 'name', 'level' => 3),
        'notempty'   => array('attr' => 'name', 'level' => 3),
        'present'    => array('attr' => 'name', 'level' => 3),
        'notpresent' => array('attr' => 'name', 'level' => 3),
        'defined'    => array('attr' => 'name', 'level' => 3),
        'notdefined' => array('attr' => 'name', 'level' => 3),
        'import'     => array('attr' => 'file,href,type,value,basepath', 'close' => 0, 'alias' => 'load,css,js'),
        'assign'     => array('attr' => 'name,value', 'close' => 0),
        'define'     => array('attr' => 'name,value', 'close' => 0),
        'for'        => array('attr' => 'start,end,name,comparison,step', 'level' => 3),
    );

    /**
     * php标签解析
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _php($tag, $content)
    {
        $parseStr = '<?php ' . $content . ' ?>';
        return $parseStr;
    }

    /**
     * volist标签解析 循环输出数据集
     * 格式：
     * <volist name="userList" id="user" empty="" >
     * {user.username}
     * {user.email}
     * </volist>
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string|void
     */
    public function _volist($tag, $content)
    {
        $name  = $tag['name'];
        $id    = $tag['id'];
        $empty = isset($tag['empty']) ? $tag['empty'] : '';
        $key   = !empty($tag['key']) ? $tag['key'] : 'i';
        $mod   = isset($tag['mod']) ? $tag['mod'] : '2';
        // 允许使用函数设定数据集 <volist name=":fun('arg')" id="vo">{$vo.name}</volist>
        $parseStr = '<?php ';
        if (0 === strpos($name, ':')) {
            $parseStr .= '$_result=' . substr($name, 1) . ';';
            $name = '$_result';
        } else {
            $name = $this->autoBuildVar($name);
        }
        $parseStr .= 'if(is_array(' . $name . ')): $' . $key . ' = 0;';
        if (isset($tag['length']) && '' != $tag['length']) {
            $parseStr .= ' $__LIST__ = array_slice(' . $name . ',' . $tag['offset'] . ',' . $tag['length'] . ',true);';
        } elseif (isset($tag['offset']) && '' != $tag['offset']) {
            $parseStr .= ' $__LIST__ = array_slice(' . $name . ',' . $tag['offset'] . ',null,true);';
        } else {
            $parseStr .= ' $__LIST__ = ' . $name . ';';
        }
        $parseStr .= 'if( count($__LIST__)==0 ) : echo "' . $empty . '" ;';
        $parseStr .= 'else: ';
        $parseStr .= 'foreach($__LIST__ as $key=>$' . $id . '): ';
        $parseStr .= '$mod = ($' . $key . ' % ' . $mod . ' );';
        $parseStr .= '++$' . $key . ';?>';
        $parseStr .= $this->tpl->parse($content);
        $parseStr .= '<?php endforeach; endif; else: echo "' . $empty . '" ;endif; ?>';

        if (!empty($parseStr)) {
            return $parseStr;
        }
        return;
    }

    /**
     * foreach标签解析 循环输出数据集
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string|void
     */
    public function _foreach($tag, $content)
    {
        $name     = $tag['name'];
        $item     = $tag['item'];
        $key      = !empty($tag['key']) ? $tag['key'] : 'key';
        $name     = $this->autoBuildVar($name);
        $parseStr = '<?php if(is_array(' . $name . ')): foreach(' . $name . ' as $' . $key . '=>$' . $item . '): ?>';
        $parseStr .= $this->tpl->parse($content);
        $parseStr .= '<?php endforeach; endif; ?>';

        if (!empty($parseStr)) {
            return $parseStr;
        }
        return;
    }

    /**
     * if标签解析
     * 格式：
     * <if condition=" $a eq 1" >
     * <elseif condition="$a eq 2" />
     * <else />
     * </if>
     * 表达式支持 eq neq gt egt lt elt == > >= < <= or and || &&
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _if($tag, $content)
    {
        $condition = $this->parseCondition($tag['condition']);
        $parseStr  = '<?php if(' . $condition . '): ?>' . $content . '<?php endif; ?>';
        return $parseStr;
    }

    /**
     * else标签解析
     * 格式：见if标签
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _elseif($tag, $content)
    {
        $condition = $this->parseCondition($tag['condition']);
        $parseStr  = '<?php elseif(' . $condition . '): ?>';
        return $parseStr;
    }

    /**
     * else标签解析
     * @access public
     * @param array $tag 标签属性
     * @return string
     */
    public function _else($tag)
    {
        $parseStr = '<?php else: ?>';
        return $parseStr;
    }

    /**
     * switch标签解析
     * 格式：
     * <switch name="a.name" >
     * <case value="1" break="false">1</case>
     * <case value="2" >2</case>
     * <default />other
     * </switch>
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _switch($tag, $content)
    {
        $name     = $tag['name'];
        $varArray = explode('|', $name);
        $name     = array_shift($varArray);
        $name     = $this->autoBuildVar($name);
        if (count($varArray) > 0) {
            $name = $this->tpl->parseVarFunction($name, $varArray);
        }

        $parseStr = '<?php switch(' . $name . '): ?>' . $content . '<?php endswitch;?>';
        return $parseStr;
    }

    /**
     * case标签解析 需要配合switch才有效
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _case($tag, $content)
    {
        $value = $tag['value'];
        if ('$' == substr($value, 0, 1)) {
            $varArray = explode('|', $value);
            $value    = array_shift($varArray);
            $value    = $this->autoBuildVar(substr($value, 1));
            if (count($varArray) > 0) {
                $value = $this->tpl->parseVarFunction($value, $varArray);
            }

            $value = 'case ' . $value . ': ';
        } elseif (strpos($value, '|')) {
            $values = explode('|', $value);
            $value  = '';
            foreach ($values as $val) {
                $value .= 'case "' . addslashes($val) . '": ';
            }
        } else {
            $value = 'case "' . $value . '": ';
        }
        $parseStr = '<?php ' . $value . ' ?>' . $content;
        $isBreak  = isset($tag['break']) ? $tag['break'] : '';
        if ('' == $isBreak || $isBreak) {
            $parseStr .= '<?php break;?>';
        }
        return $parseStr;
    }

    /**
     * default标签解析 需要配合switch才有效
     * 使用： <default />ddfdf
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _default($tag)
    {
        $parseStr = '<?php default: ?>';
        return $parseStr;
    }

    /**
     * compare标签解析
     * 用于值的比较 支持 eq neq gt lt egt elt heq nheq 默认是eq
     * 格式： <compare name="" type="eq" value="" >content</compare>
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _compare($tag, $content, $type = 'eq')
    {
        $name     = $tag['name'];
        $value    = $tag['value'];
        $type     = isset($tag['type']) ? $tag['type'] : $type;
        $type     = $this->parseCondition(' ' . $type . ' ');
        $varArray = explode('|', $name);
        $name     = array_shift($varArray);
        $name     = $this->autoBuildVar($name);
        if (count($varArray) > 0) {
            $name = $this->tpl->parseVarFunction($name, $varArray);
        }

        if ('$' == substr($value, 0, 1)) {
            $value = $this->autoBuildVar(substr($value, 1));
        } else {
            $value = '"' . $value . '"';
        }
        $parseStr = '<?php if((' . $name . ') ' . $type . ' ' . $value . '): ?>' . $content . '<?php endif; ?>';
        return $parseStr;
    }

    public function _eq($tag, $content)
    {
        return $this->_compare($tag, $content, 'eq');
    }

    public function _equal($tag, $content)
    {
        return $this->_compare($tag, $content, 'eq');
    }

    public function _neq($tag, $content)
    {
        return $this->_compare($tag, $content, 'neq');
    }

    public function _notequal($tag, $content)
    {
        return $this->_compare($tag, $content, 'neq');
    }

    public function _gt($tag, $content)
    {
        return $this->_compare($tag, $content, 'gt');
    }

    public function _lt($tag, $content)
    {
        return $this->_compare($tag, $content, 'lt');
    }

    public function _egt($tag, $content)
    {
        return $this->_compare($tag, $content, 'egt');
    }

    public function _elt($tag, $content)
    {
        return $this->_compare($tag, $content, 'elt');
    }

    public function _heq($tag, $content)
    {
        return $this->_compare($tag, $content, 'heq');
    }

    public function _nheq($tag, $content)
    {
        return $this->_compare($tag, $content, 'nheq');
    }

    /**
     * range标签解析
     * 如果某个变量存在于某个范围 则输出内容 type= in 表示在范围内 否则表示在范围外
     * 格式： <range name="var|function"  value="val" type='in|notin' >content</range>
     * example: <range name="a"  value="1,2,3" type='in' >content</range>
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @param string $type  比较类型
     * @return string
     */
    public function _range($tag, $content, $type = 'in')
    {
        $name     = $tag['name'];
        $value    = $tag['value'];
        $varArray = explode('|', $name);
        $name     = array_shift($varArray);
        $name     = $this->autoBuildVar($name);
        if (count($varArray) > 0) {
            $name = $this->tpl->parseVarFunction($name, $varArray);
        }

        $type = isset($tag['type']) ? $tag['type'] : $type;

        if ('$' == substr($value, 0, 1)) {
            $value = $this->autoBuildVar(substr($value, 1));
            $str   = 'is_array(' . $value . ')?' . $value . ':explode(\',\',' . $value . ')';
        } else {
            $value = '"' . $value . '"';
            $str   = 'explode(\',\',' . $value . ')';
        }
        if ('between' == $type) {
            $parseStr = '<?php $_RANGE_VAR_=' . $str . ';if(' . $name . '>= $_RANGE_VAR_[0] && ' . $name . '<= $_RANGE_VAR_[1]):?>' . $content . '<?php endif; ?>';
        } elseif ('notbetween' == $type) {
            $parseStr = '<?php $_RANGE_VAR_=' . $str . ';if(' . $name . '<$_RANGE_VAR_[0] || ' . $name . '>$_RANGE_VAR_[1]):?>' . $content . '<?php endif; ?>';
        } else {
            $fun      = ('in' == $type) ? 'in_array' : '!in_array';
            $parseStr = '<?php if(' . $fun . '((' . $name . '), ' . $str . ')): ?>' . $content . '<?php endif; ?>';
        }
        return $parseStr;
    }

    // range标签的别名 用于in判断
    public function _in($tag, $content)
    {
        return $this->_range($tag, $content, 'in');
    }

    // range标签的别名 用于notin判断
    public function _notin($tag, $content)
    {
        return $this->_range($tag, $content, 'notin');
    }

    public function _between($tag, $content)
    {
        return $this->_range($tag, $content, 'between');
    }

    public function _notbetween($tag, $content)
    {
        return $this->_range($tag, $content, 'notbetween');
    }

    /**
     * present标签解析
     * 如果某个变量已经设置 则输出内容
     * 格式： <present name="" >content</present>
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _present($tag, $content)
    {
        $name     = $tag['name'];
        $name     = $this->autoBuildVar($name);
        $parseStr = '<?php if(isset(' . $name . ')): ?>' . $content . '<?php endif; ?>';
        return $parseStr;
    }

    /**
     * notpresent标签解析
     * 如果某个变量没有设置，则输出内容
     * 格式： <notpresent name="" >content</notpresent>
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _notpresent($tag, $content)
    {
        $name     = $tag['name'];
        $name     = $this->autoBuildVar($name);
        $parseStr = '<?php if(!isset(' . $name . ')): ?>' . $content . '<?php endif; ?>';
        return $parseStr;
    }

    /**
     * empty标签解析
     * 如果某个变量为empty 则输出内容
     * 格式： <empty name="" >content</empty>
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _empty($tag, $content)
    {
        $name     = $tag['name'];
        $name     = $this->autoBuildVar($name);
        $parseStr = '<?php if(empty(' . $name . ')): ?>' . $content . '<?php endif; ?>';
        return $parseStr;
    }

    public function _notempty($tag, $content)
    {
        $name     = $tag['name'];
        $name     = $this->autoBuildVar($name);
        $parseStr = '<?php if(!empty(' . $name . ')): ?>' . $content . '<?php endif; ?>';
        return $parseStr;
    }

    /**
     * 判断是否已经定义了该常量
     * <defined name='TXT'>已定义</defined>
     * @param <type> $attr
     * @param <type> $content
     * @return string
     */
    public function _defined($tag, $content)
    {
        $name     = $tag['name'];
        $parseStr = '<?php if(defined("' . $name . '")): ?>' . $content . '<?php endif; ?>';
        return $parseStr;
    }

    public function _notdefined($tag, $content)
    {
        $name     = $tag['name'];
        $parseStr = '<?php if(!defined("' . $name . '")): ?>' . $content . '<?php endif; ?>';
        return $parseStr;
    }

    /**
     * import 标签解析 <import file="Js.Base" />
     * <import file="Css.Base" type="css" />
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @param boolean $isFile  是否文件方式
     * @param string $type  类型
     * @return string
     */
    public function _import($tag, $content, $isFile = false, $type = '')
    {
        $file     = isset($tag['file']) ? $tag['file'] : $tag['href'];
        $parseStr = '';
        $endStr   = '';
        // 判断是否存在加载条件 允许使用函数判断(默认为isset)
        if (isset($tag['value'])) {
            $varArray = explode('|', $tag['value']);
            $name     = array_shift($varArray);
            $name     = $this->autoBuildVar($name);
            if (!empty($varArray)) {
                $name = $this->tpl->parseVarFunction($name, $varArray);
            } else {
                $name = 'isset(' . $name . ')';
            }

            $parseStr .= '<?php if(' . $name . '): ?>';
            $endStr = '<?php endif; ?>';
        }
        if ($isFile) {
            // 根据文件名后缀自动识别
            $type = $type ? $type : (!empty($tag['type']) ? strtolower($tag['type']) : null);
            // 文件方式导入
            $array = explode(',', $file);
            foreach ($array as $val) {
                if (!$type || isset($reset)) {
                    $type = $reset = strtolower(substr(strrchr($val, '.'), 1));
                }
                switch ($type) {
                    case 'js':
                        $parseStr .= '<script type="text/javascript" src="' . $val . '"></script>';
                        break;
                    case 'css':
                        $parseStr .= '<link rel="stylesheet" type="text/css" href="' . $val . '" />';
                        break;
                    case 'php':
                        $parseStr .= '<?php require_cache("' . $val . '"); ?>';
                        break;
                }
            }
        } else {
            // 命名空间导入模式 默认是js
            $type     = $type ? $type : (!empty($tag['type']) ? strtolower($tag['type']) : 'js');
            $basepath = !empty($tag['basepath']) ? $tag['basepath'] : __ROOT__ . '/Public';
            // 命名空间方式导入外部文件
            $array = explode(',', $file);
            foreach ($array as $val) {
                if (strpos($val, '?')) {
                    list($val, $version) = explode('?', $val);
                } else {
                    $version = '';
                }
                switch ($type) {
                    case 'js':
                        $parseStr .= '<script type="text/javascript" src="' . $basepath . '/' . str_replace(array('.', '#'), array('/', '.'), $val) . '.js' . ($version ? '?' . $version : '') . '"></script>';
                        break;
                    case 'css':
                        $parseStr .= '<link rel="stylesheet" type="text/css" href="' . $basepath . '/' . str_replace(array('.', '#'), array('/', '.'), $val) . '.css' . ($version ? '?' . $version : '') . '" />';
                        break;
                    case 'php':
                        $parseStr .= '<?php import("' . $val . '"); ?>';
                        break;
                }
            }
        }
        return $parseStr . $endStr;
    }

    // import别名 采用文件方式加载(要使用命名空间必须用import) 例如 <load file="__PUBLIC__/Js/Base.js" />
    public function _load($tag, $content)
    {
        return $this->_import($tag, $content, true);
    }

    // import别名使用 导入css文件 <css file="__PUBLIC__/Css/Base.css" />
    public function _css($tag, $content)
    {
        return $this->_import($tag, $content, true, 'css');
    }

    // import别名使用 导入js文件 <js file="__PUBLIC__/Js/Base.js" />
    public function _js($tag, $content)
    {
        return $this->_import($tag, $content, true, 'js');
    }

    /**
     * assign标签解析
     * 在模板中给某个变量赋值 支持变量赋值
     * 格式： <assign name="" value="" />
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _assign($tag, $content)
    {
        $name = $this->autoBuildVar($tag['name']);
        if ('$' == substr($tag['value'], 0, 1)) {
            $value = $this->autoBuildVar(substr($tag['value'], 1));
        } else {
            $value = '\'' . $tag['value'] . '\'';
        }
        $parseStr = '<?php ' . $name . ' = ' . $value . '; ?>';
        return $parseStr;
    }

    /**
     * define标签解析
     * 在模板中定义常量 支持变量赋值
     * 格式： <define name="" value="" />
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _define($tag, $content)
    {
        $name = '\'' . $tag['name'] . '\'';
        if ('$' == substr($tag['value'], 0, 1)) {
            $value = $this->autoBuildVar(substr($tag['value'], 1));
        } else {
            $value = '\'' . $tag['value'] . '\'';
        }
        $parseStr = '<?php define(' . $name . ', ' . $value . '); ?>';
        return $parseStr;
    }

    /**
     * for标签解析
     * 格式： <for start="" end="" comparison="" step="" name="" />
     * @access public
     * @param array $tag 标签属性
     * @param string $content  标签内容
     * @return string
     */
    public function _for($tag, $content)
    {
        //设置默认值
        $start      = 0;
        $end        = 0;
        $step       = 1;
        $comparison = 'lt';
        $name       = 'i';
        $rand       = rand(); //添加随机数，防止嵌套变量冲突
        //获取属性
        foreach ($tag as $key => $value) {
            $value = trim($value);
            if (':' == substr($value, 0, 1)) {
                $value = substr($value, 1);
            } elseif ('$' == substr($value, 0, 1)) {
                $value = $this->autoBuildVar(substr($value, 1));
            }

            switch ($key) {
                case 'start':
                    $start = $value;
                    break;
                case 'end':
                    $end = $value;
                    break;
                case 'step':
                    $step = $value;
                    break;
                case 'comparison':
                    $comparison = $value;
                    break;
                case 'name':
                    $name = $value;
                    break;
            }
        }

        $parseStr = '<?php $__FOR_START_' . $rand . '__=' . $start . ';$__FOR_END_' . $rand . '__=' . $end . ';';
        $parseStr .= 'for($' . $name . '=$__FOR_START_' . $rand . '__;' . $this->parseCondition('$' . $name . ' ' . $comparison . ' $__FOR_END_' . $rand . '__') . ';$' . $name . '+=' . $step . '){ ?>';
        $parseStr .= $content;
        $parseStr .= '<?php } ?>';
        return $parseStr;
    }

}
