<?php

/**
 * 基本模型处理类
  * ============================================================================
 * Copyright 2014 大秦科技，并保留所有权利。
 * 网站地址: http://www.qintech.net；
 * ----------------------------------------------------------------------------
 * 未获商业授权之前，不得将Magic CMS用于商业用途（包括但不限于企业网站、经营性网站
 * 以营利为目的或实现盈利的网站）未经官方许可，禁止在Magic CMS的整体或任何部分基础
 * 上以发展任何派生版本、修改版本或第三方版本用于重新分发。如果您未能遵守本协议的
 * 条款，您的授权将被终止，所被许可的权利将被收回，并承担相应法律责任。
 * ============================================================================
 */
if (!defined('KERNEL_PATH'))exit('No direct script access allowed');
class Model
{
    //数据表
    public $table = null;
    //数据库连接驱动对象
    public $db = null;
    //触发器状态
    public $trigger = true;
    //模型错误信息
    public $error;
    //模型操作数据
    public $data = array();
    //验证规则
    public $validate = array();
    //自动完成规则
    public $auto = array();
    //字段映射规则
    public $map = array();
    //别名方法
    public $alias
        = array('add' => 'insert','adds' =>'inserts', 'save' => 'update', 'all' => 'select',
                'del' => 'delete');

    /**
     * 构造函数
     *
     * @param null  $table  表名
     * @param bool  $full   全表名
     * @param null  $driver 驱动
     * @param array $param  参数
     */
    public function __construct(
        $table = null, $full = null, $param = array(), $driver = null
    ) {
        /**
         * 初始化默认表
         * 如果设置了table属性时,使用table属性值为表名
         */
        if ( ! $this->table) {
            $this->table = $table;
        }
        /**
         * 获得数据库引擎
         */
        $this->db = DbFactory::factory($driver, $this->table, $full);
        /**
         * 执行子类构造函数__init
         */
        if (method_exists($this, "__init")) {
            $this->__init($param);
        }
    }

    /**
     * 设置模型属性如表名字段名
     */
    public function __set($var, $value)
    {
        /**
         * 设置$data属性值用于插入修改等操作
         */
        $this->data[$var] = $value;
    }

    /**
     * 魔术方法
     */
    public function __get($name)
    {
        /**
         * 返回$this->data属性
         * $this->data属性指添加与编辑的数据
         */
        if (isset($this->data[$name])) {
            return $this->data[$name];
        }
    }

    public function __call($method, $param){
        /**
         * 执行别名函数
         * 如add 是insert别名,执行add时执行insert方法
         */
        if (isset($this->alias[$method])) {
            return call_user_func_array(
                array($this, $this->alias[$method]), $param
            );
        } else if (method_exists($this->db, $method)) {
            $RETURN = call_user_func_array(array($this->db, $method), $param);

            return $RETURN === null ? $this : $RETURN;
        }
    }

    /**
     * 重置模型
     */
    protected function __reset(){
        $this->data = array();
        $this->trigger = true;
    }

    /**
     * 获得添加、插入数据
     */
    public function data($data = array()){
        if (empty($data)) {
            if (empty($this->data)) {
                $this->data = $_POST;
            }
        } else {
            $this->data = $data;
        }
        /**
         * 系统开启转义时,去除转义操作
         */
        foreach ($this->data as $key => $val) {
            if (MAGIC_QUOTES_GPC && is_string($val)) {
                $this->data[$key] = stripslashes($val);
            }
        }

        return $this;
    }

    /**
     * 执行自动映射、自动验证、自动完成
     */
    public function create($data = array()){

        $this->data($data);
        /**
         * 批量执行方法
         * validate 自动验证
         * auto 自动完成
         * map 自动映射
         */
        $action = array('validate', 'auto', 'map');
        foreach ($action as $a) {
            if ( ! $this->$a()) {
                return false;
            }
        }

        return true;
    }

    /**
     * 字段映射
     * 将添加或更新的数据键名改为表字段名
     */
    public function map($data = array())
    {
        $this->data($data);
        if ( ! empty($this->map)) {
            foreach ($this->map as $k => $v) {
                //处理POST
                if (isset($this->data[$k])) {
                    $this->data[$v] = $this->data[$k];
                    unset($this->data[$k]);
                }
            }
        }

        return true;
    }

    /**
     * 当前操作的方法
     * 主要是判断数据中是否存在主键,有主键为更新操作,否则为添加操作
     * 1为插入操作 2为更新操作
     */
    private function getCurrentMethod()
    {
        //1 插入 , 2 更新
        return isset($this->data[$this->db->pri]) ? 2 : 1;
    }


    /**
     * 字段验证
     * 验证字段合法性,支持自定义函数,模型方法与Validate验证类方法的操作
     */
    public function validate($data = array())
    {
        /**
         * 验证规则为空时不验证
         */
        if (empty($this->validate)) {
            return true;
        }
        /**
         * 操作数据
         */
        $this->data($data);

        $data = &$this->data;
        /**
         * 当前方法
         */
        $motion = $this->getCurrentMethod();
        /**
         * 验证处理
         */


        foreach ($this->validate as $v) {

			//表单名称
			$name = $v[0];

			$status = preg_match('/(.*)\[(.*)\]+/',$name,$info);

			if ($status && !empty($info)) {
				$value = $data[$info[1]][$info[2]];
			}else{
				$value = $data[$name];
			}


            /**
             * 验证条件
             * 1 有表单时
             * 2 必须验证
             * 3 不为空时
             */
            $condition = isset($v[3]) ? $v[3] : 2;
            /**
             * 验证时机
             * 1 插入时
             * 2 更新时
             * 3 插入与更新
             */
            $action = isset($v[4]) ? $v[4] : 3;
            /**
             * 验证时间判断
             */
            if ( ! in_array($action, array($motion, 3))) {
                continue;
            }
            /**
             * 错误信息
             */
            $msg = $v[2];
           switch ($condition) {
                case 1 :
                    //有表单时
                    if ( ! isset($value)) {
                        continue 2;
                    }
                    break;
                case 2 :
                    //必须验证
                    if ( ! isset($value)) {
                        $this->error = $msg;

                        return false;
                    }
                    break;
                case 3 :
                    //不为空时
                    if (empty($value)) {
                        continue 2;
                    }
                    break;
            }
            if ($pos = strpos($v[1], ':')) {
                $func = substr($v[1], 0, $pos);
                $args = substr($v[1], $pos + 1);
            } else {
                $func = $v[1];
                $args = '';
            }

			//执行模型方法
            if (method_exists($this, $func)) {
                $res = call_user_func_array(
                    array($this, $func),
                    array($name, $value, $msg, $args)
                );
                /**
                 * 验证失败
                 */
                if ($res !== true) {
                    $this->error = $res;

                    return false;
                }

			//函数验证
            } else if (function_exists($func)) {


                 $res = $func($name, $value, $msg, $args);

                //验证失败
                if ($res !== true) {
                    $this->error = $res;

                    return false;
                }

			//Validate验证类处理
            } else {
                $validate = new Validate();
                $func     = '_' . $func;
                if (method_exists($validate, $func)) {
                    $res = call_user_func_array(
                        array($validate, $func),
                        array($name, $value, $msg, $args)
                    );

                    //验证失败
                    if ($res !== true) {
                        $this->error = $res;

                        return false;
                    }
                }
            }
        }

        return true;
    }

    /**
     * 自动完成
     *
     * @param array $data
     *
     * @return bool
     */
    public function auto($data = array())
    {
        /**
         * 获取数据
         */
        $this->data($data);
        $data = &$this->data;
        /**
         * 处理时机
         * 1 插入
         * 2 更新
         */
        $motion = $this->getCurrentMethod();
        foreach ($this->auto as $v) {
            /**
             * 表单名
             */
			if (preg_match_all('([\w\.]+)',$v[0],$info, PREG_SET_ORDER)) {
				$name = $data[$info[0][0]][$info[1][0]];

			}else{
				$name = $data[$v[0]];
			}
            /**
             * 方法名
             */
            $method = $v[1];
            /**
             * 执行方式
             * function 函数
             * method   模型方法
             * string   字符串
             */
            $handle = isset($v[2]) ? $v[2] : "string";
            /**
             * 处理条件
             * 1 有表单时
             * 2 必须处理
             * 3 值不为空时
             * 4 值为空时
             */
            $condition = isset($v[3]) ? $v[3] : 1;
            /**
             * 处理时机
             * 1 插入时处理
             * 2 更新时处理
             * 3 插入与更新都处理
             */
            $action = isset($v[4]) ? $v[4] : 3;
            /**
             * 验证处理时机
             */
            if ( ! in_array($action, array($motion, 3))) {
                continue;
            }
            switch ($condition) {
                case 1 :
                    //不存在字段时
                    if ( ! isset($name)) {
                        continue 2;
                    }
                    break;
                case 2 :
                    // 必须处理
                    if ( ! isset($name)) {
                        $name = '';
                    }
                    break;
                case 3 :
                    //值不为空时
                    if (empty($name)) {
                        continue 2;
                    }
                    break;
                case 4:
                    //值为空时
                    if (empty($name)) {
                        $name = '';
                    } else {
                        continue 2;
                    }
                    break;
            }
            $name = isset($name) ? $name : '';
            switch ($handle) {
                case "function" :
                    //函数
                    if (function_exists($method)) {
                        $name = $method($name);
                    }
                    break;
                case "method" :
                    //模型方法
                    if (method_exists($this, $method)) {
                        $name = $this->$method($name);
                    }
                    break;
                case "string" :
                    //字符串
                    $name = $method;
                    break;
            }
        }

        return true;
    }

    /**
     * 设置触发器状态
     *
     * @param $status
     *
     * @return bool
     */
    public function trigger($status)
    {
        $this->trigger = $status;

        return $this;
    }

    /**
     * 删除数据
     *
     * @param string $where 条件
     *
     * @return mixed
     */
    public function delete($where = '')
    {
        $this->trigger && method_exists($this, '__before_delete')
        && $this->__before_delete();
        $return = $this->db->delete($where);
        $this->trigger && method_exists($this, '__after_delete')
        && $this->__after_delete($return);
        /**
         * 重置模型
         */
        $this->__reset();

        return $return;
    }

    /**
     * 查找满足条件的一条记录
     *
     * @param string $where 条件,如果为数字查询主键值
     *
     * @return mixed
     */
    public function find($where = '')
    {
        $result = $this->select($where);

        return is_array($result) ? current($result) : $result;
    }

    /**
     * 查询结果
     *
     * @param string $where 条件
     *
     * @return mixed
     */
    public function select($where = '')
    {
        $this->trigger && method_exists($this, '__before_select')
        && $this->__before_select();
        $return = $this->db->select($where);
        $this->trigger && method_exists($this, '__after_select')
        && $this->__after_select($return);
        /**
         * 重置模型
         */
        $this->__reset();

        return $return;
    }

    /**
     * 更新数据
     *
     * @param array $data 更新的数据
     *
     * @return mixed
     */
    public function update($data = array())
    {
        $this->data($data);
        $this->trigger && method_exists($this, '__before_update')
        && $this->__before_update($this->data);
        $return = $this->db->update($this->data);
        $this->trigger && method_exists($this, '__after_update')
        && $this->__after_update($return);
        /**
         * 重置模型
         */
        $this->__reset();

        return $return;
    }

    /**
     * 插入数据
     *
     * @param array $data 新数据
     *
     * @return mixed
     */
    public function insert($data = array())
    {
        $this->data($data);
        $this->trigger && method_exists($this, '__before_insert')
        && $this->__before_insert($this->data);
        $return = $this->db->insert($this->data);
        $this->trigger && method_exists($this, '__after_insert')
        && $this->__after_insert($return);
        /**
         * 重置模型
         */
        $this->__reset();

        return $return;
    }

	/**
     * 批量插入数据
     *
     * @param array $data 新数据
     *
     * @return mixed
     */
    public function inserts($data = array())
    {
        $this->data($data);
        $this->trigger && method_exists($this, '__before_insert')
        && $this->__before_insert($this->data);
        $return = $this->db->inserts($this->data);
        $this->trigger && method_exists($this, '__after_insert')
        && $this->__after_insert($return);
        /**
         * 重置模型
         */
        $this->__reset();

        return $return;
    }

    /**
     * replace方式插入数据
     * 更新数据中存在主键或唯一索引数据为更新操作否则为添加操作
     *
     * @param array $data
     *
     * @return mixed
     */
    public function replace($data = array())
    {
        $this->data($data);
        $this->trigger && method_exists($this, '__before_insert')
        && $this->__before_insert($this->data);
        $return = $this->db->replace($this->data);
        $this->trigger && method_exists($this, '__after_insert')
        && $this->__after_insert($return);
        /**
         * 重置模型
         */
        $this->__reset();

        return $return;
    }
}