<?php
use \lib\db;
/**
* 模型基类(抽象类)
* protected
*/
abstract class model{
	use answer; //公共应答参数传递类
	public $task;
	public $message;
	public $table;
	public $key;
	public $db;
	/**
	* 构造
	* @return void
	*/
	public function __construct($db=null){
        if($db==null)
		    $this->db = new db();
        else
		    $this->db = $db;
		}

    /**
     * 分页函数
     * 参数一: $table, $where, $page, $pagesize
     * 参数二: $table, $join, $column, $where, $page, $pagesize
     * @param ... $vars
     * @return int
     */
    public function paging(...$vars){
		$hasCache = 0; //缓存时间,0:不缓存
		if(count($vars)==4 || count($vars)==5){
			$page = $vars[2];
			$pagesize = $vars[3];
			if(isset($vars[1]['ORDER']))
				unset($vars[1]['ORDER']);
			if(count($vars)==5) $hasCache = $vars[4];
			if($hasCache>0) $cacheKey = md5(serialize([$vars[0],$vars[1]]));
		}else{
			$page = $vars[4];
			$pagesize = $vars[5];
			if(isset($vars[3]['ORDER']))
				unset($vars[3]['ORDER']);
			if(count($vars)==7) $hasCache = $vars[6];
			if($hasCache>0) $cacheKey = md5(serialize([$vars[0],$vars[1],$vars[2],$vars[3]]));
		}
		if(empty($pagesize)) $pagesize = 50;
		if(empty($page) || $page<1) $page = 1;
		/*
		if(	$hasCache==0
			|| !isset($_SESSION['caches']['records'])
			|| !isset($_SESSION['caches']['records'][$cacheKey])
			|| time() - $_SESSION['caches']['records'][$cacheKey]['time'] > $hasCache
			)
		{
			if(count($vars)==4 || count($vars)==5){
				$records = $this->db->count($vars[0],$vars[1]);
			}else{
				$records = $this->db->count($vars[0],$vars[1],$vars[2],$vars[3]);
			}

			if($hasCache>0) $_SESSION['caches']['records'][$cacheKey] = ['time'=>time(),'records'=>$records];
		}else{
			$records = $_SESSION['caches']['records'][$cacheKey]['records'];
		}*/
		$join = null;
		$column = null;
		if(count($vars)==4 || count($vars)==5){
			$table = $vars[0];
			$where = $vars[1];
			//$records = $this->db->count($vars[0],$vars[1]);
		}else{
			$table = $vars[0];
			$join =  $vars[1];
			$column = $vars[2];
			$where = $vars[3];
			//$records = $this->db->count($vars[0],$vars[1],$vars[2],$vars[3]);
		}
		if(!empty($where['GROUP'])){
			$column = '1';
			$where["LIMIT"] = [0,rand(2,50)];
			if(empty($join)){
				$records = $this->db->select($table,$column,$where);
			}else{
				$records = $this->db->select($table,$join,$column,$where);
			}
			$query = $this->db->last();
            $pos = strrpos($query,'LIMIT');
            $query = substr($query,0,$pos);
			//$records = $this->db->count("(".$query.") groupCount");
            $query = "SELECT COUNT(1) FROM (".$query.") groupCount";
            $st = $this->db->query($query);
            $records = $st->fetchColumn();
		}else{
			if(empty($join)){
				$records = $this->db->count($table,$where);
			}else{
				$records = $this->db->count($table,$join,$column,$where);
			}
		}

		$data['page'] = $page; //当前页码
		$data['pagesize'] = $pagesize; //一页多少条
		$data['total'] = ceil($records / $pagesize); //总页码数
		$data['records'] = $records; //总记录数
		$data['limit'] = ($page-1) * $pagesize;
        return $data;
    }
	/**
	 * 组装grid查询medoo
	 * @param array $groups  提交的条件
	 * @param function $replaceFunction  自定义替换值函数 function($field,$op,$value)
	 * @return string SQL where 语句
	 */
	function filter2medoo($groups,$replaceFunction=null,$level=0,$tag=0){
        if(isset($groups) && empty($groups)) return [];
		if(!is_array($groups)){
    	    $groups = htmlspecialchars_decode($groups);
    	    $groups = preg_replace("/[\t|\n|\r]/", "", $groups);
		    $groups = json_decode($groups,true);
		}
		$where = [];
		$rulesWhere = '';
		$groupsWhere = '';
		$rulesCount = isset($groups['rules']) ? count($groups['rules']) : 0;
		$groupsCount = isset($groups['groups']) ? count($groups['groups']) : 0;
		if($rulesCount==0 && $groupsCount==0) 
			return [];
		if($rulesCount) 
			$where = $this->rules2medoo($groups['rules'], $groups['groupOp'],$replaceFunction,$level);
		if($groupsCount){
			foreach($groups['groups'] as $i=>$group){
				$arr = $this->filter2medoo($group,$replaceFunction,$level+1,$i+1);
                if(!empty($arr)){
                    foreach($arr as $k=>$v)
                        $where[$k] = $v;
                }
                if(empty($arr)){
                    unset($groups['groups'][$i]);
                }
			}
		}
        if(empty($where)){
            return null;
        }else{
            return [$groups['groupOp']." #level_".$level.'_'.$tag =>$where];
        }
	}
	/**
	* @ignore
	*/
	function rules2medoo($rules,$groupOp,$replaceFunction,$level){
		$arr=[];
		foreach($rules as $i=>$rule){
			$rule['data'] = trim($rule['data']);

			if($replaceFunction) {
				$replaceFunction($rule['field'],$rule['op'],$rule['data']);
			}
            if(strpos($rule['op'], 'RAW') === 0){
                $arr[$rule['op']] = $rule['data'];
            }else if($rule['op']!=null){  //op = null 跳过
                if($rule['op']=='nu'){
                    if(strpos($rule['field'],'_time')===false){
                        $arr['AND #level_'.$level.'_'.$rule['field']] = [
                            $rule['field'].'[!] #op1'.'_'.$i => '',
                            $rule['field'].'[!] #op2'.'_'.$i => '0',
                            $rule['field'].'[!] #op3'.'_'.$i => null
                        ];
                    }else{
                        $arr['AND #level_'.$level.'_'.$rule['field']] = [
                            $rule['field'].'[!] #op3'.'_'.$i => null
                        ];
                    }
                }else if($rule['op']=='nn'){
                    if(strpos($rule['field'],'_time')===false){
                        $arr['OR #level_'.$level.'_'.$rule['field']]['OR'] = [
                            $rule['field'].'[=] #op1'.'_'.$i => '',
                            $rule['field'].'[=] #op2'.'_'.$i => '0',
                            $rule['field'].'[=] #op3'.'_'.$i => null
                        ];
                    }else{
                        $arr['OR #level_'.$level.'_'.$rule['field']]['OR'] = [
                            $rule['field'].'[=] #op3'.'_'.$i => null
                        ];
                    }
                }else{
                    $arr[$rule['field'].'['.$this->getMedooOp($rule['op']).'] #level_'.$level.'_'.$rule['field'].'_'.$i] =  $rule['data'];
                }
            }
		}
		return $arr;
	}
	function getMedooOp($op){
	    //cn:前匹配,nu:存在,nn:不存在,bw:模糊,bt:区间,
		$opArr = ["eq"=>"=","ne"=>"<>","le"=>"<=","lt"=>"<","gt"=>">","ge"=>">="
			,"cn"=>"=~","nc"=>"!~","nu"=>"<>''","nn"=>"=''","bw"=>"~","bt"=>"~","ni"=>"!"
			,"ld"=>"~,","not"=>"!","sql"=>"@","zh"=>"="
		];
		return ($opArr[$op]) ? $opArr[$op] : $op;
	}
	/**
	 * 组装grid查询SQL
	 * @param array $groups  提交的条件
	 * @param function $replaceFunction  自定义替换值函数 function($field,$op,$value)
	 * @return string SQL where 语句
	 */
	function filter2sql($groups,$replaceFunction=null){
		if(!is_array($groups)) $groups = json_decode(htmlspecialchars_decode($groups),true);
		$where = '';
		$rulesWhere = '';
		$groupsWhere = '';
		$rulesCount = (isset($groups['rules'])) ? count($groups['rules']) : 0;
		$groupsCount = isset($groups['groups']) ? count($groups['groups']) : 0;
		if($rulesCount==0 && $groupsCount==0) 
			return '';
		if($rulesCount) 
			$where = $this->rules2sql($groups['rules'], $groups['groupOp'],$replaceFunction);
		if($groupsCount){
			$arr = [];
			foreach($groups['groups'] as $group){
                $arr[] = '('.$this->filter2sql($group,$replaceFunction).')';
			}
			$groupsWhere .= implode(' '.$groups['groupOp'].' ',$arr);
		}
		if($groupsWhere){
			if($where=='')
				$where =  $groupsWhere;
			else
				$where .= ' '.$groups['groupOp'] .' '. $groupsWhere;
		}
		return $where;
	}
	/**
	* @ignore
	*/
	function rules2sql($rules,$groupOp,$replaceFunction){
		$op = ["eq"=>"='%s'","ne"=>"<>'%s'","le"=>"<='%s'","lt"=>"<'%s'","gt"=>">'%s'","ge"=>">='%s'"
			,"cn"=>"like '%%%s%%'","nc"=>"not like'%%%s%%'","nu"=>"<>''","nn"=>"=''"
			,"ld"=>"like '%%%s%%'",
		];
		$arr=[];
		foreach($rules as $rule){
			$rule['data'] = trim($rule['data']);
			if($replaceFunction) {
				$replaceFunction($rule['field'],$rule['op'],$rule['data']);
			}
			if($rule['op']!=null){
				if($rule['op']=='cn'){
					if(strpos($rule['data'],'*')!=false || strpos($rule['data'],'?')!=false){
						$rule['data'] = str_replace('*','%',$rule['data']);
						$rule['data'] = str_replace('?','_',$rule['data']);
						$arr[] = $rule['field'] .' '. sprintf("like '%s'", $rule['data']);
					}else{
						$arr[] = $rule['field'] .' '. sprintf($op[$rule['op']], $rule['data']);
					}
				}else if($rule['op']=='nc'){
					if(strpos($rule['data'],'*')!=false || strpos($rule['data'],'?')!=false){
						$rule['data'] = str_replace('*','%',$rule['data']);
						$rule['data'] = str_replace('?','_',$rule['data']);
						$arr[] = $rule['field'] .' '. sprintf("not like '%s'", $rule['data']);
					}else{
						$arr[] = $rule['field'] .' '. sprintf($op[$rule['op']], $rule['data']);
					}
				}else if($rule['op']=='nu'){
					$arr[] = sprintf("(%s='' or %s is null)",$rule['field'],$rule['field']); 
				}else if($rule['op']=='ld'){
					dd($rule);
					$arr[] = sprintf("(%s %s)",','.$rule['field'].',',','.$rule['data'].','); 
				}else{
					if($rule['data']!='' || $rule['op']=='nn')
						$arr[] = $rule['field'] .' '. sprintf($op[$rule['op']],$rule['data']);
				}
			}
		}
		if(count($arr)>0)
			$where = implode(' '.$groupOp.' ',$arr);
		return $where;
	}
    /**
	 * 处理list查询与排序条件,一般来自grid发起的请求
	 * @param $where,$page,$pagesize,$sidx,$sord,$orderby
	 * @return array
	 */
	protected function listWhere($where,$page,$pagesize,$sidx,$sord,$orderby){
		if($sidx!='' && !is_null($sidx)){
			if(strpos($sidx, ',') === false){
				if($sidx!='null') $where["ORDER"] = [$sidx=>strtoupper($sord)];
			}else{
				$orderby = $sidx. ' ' . $sord;
			}
		}
		if((!isset($where["ORDER"]) || empty($where["ORDER"])) && !empty($orderby)){
			$arr = explode(",",$orderby);
			foreach($arr as $v){
				$v = trim($v);
				$item = explode(" ",$v);
				if(count($item)==1) $item[1] = 'asc';
				$sidx = $item[0];
				$sord = $item[1];
				if($sidx!='null') $where["ORDER"][$sidx] = strtoupper($sord);
			}
		}
		return $where;
	}

    /**
     * @param $sql
     * @return mixed
     */
	public function mysqlBuffered($sql)
    {
       $this->db->pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
       return $this->db->pdo->query($sql,\PDO::FETCH_ASC);
    }

	/**
	 * 取当前用户
	 * @return string
	 */
	function getOpuser(){
		return $_SESSION['user']['user_id'];
	}

	/**
	 * 取当前时间
	 * @return string
	 */
	function getDateTime(){
		return date(\mvc::$cfg['datetimeformat'],time());
	}

	/**
	 * 取当前日期
	 * @return string
	 */
	function getDate(){
		return date(\mvc::$cfg['dateformat'],time());
	}
	
	/**
	 * 返回全部行记录
	 * @return array
	 */
	function getAll($where=null,$fields=null){
		if($fields==null) $fields = '*';
		return $this->db->select($this->table,$fields,$where);
	}

	/**
	 * 通过主键返回一行记录
	 * @param var $key 如ID
	 * @return array
	 */
	function getRowByKey($key,$fields=null){
		if($fields==null) $fields = '*';
        return $this->getRowByFieldValue($this->key,$key,$fields);
	}
	/**
	 * 通过主键集返回多行记录
	 * @param var $keys  "1,2,3" 或者 [1,2,3]
	 * @return array
	 */
	function getRowsByKeys($keys,$fields=null){
        if(!is_array($keys)) $keys = explode(',',$keys);
		if($fields==null) $fields = '*';
		$where = [$this->key=>$keys];
		$where['ORDER'] = [$this->key=>$keys];
		return $this->db->select($this->table,$fields,$where);
	}

	/**
	 * 通过指定字段值返回行记录
	 * @param string $field
	 * @param string $value
	 * @return array
	 */
	function getRowByFieldValue($field,$value,$fields=null){
		if($fields==null) $fields = '*';
		return $this->db->get($this->table,$fields,[$field=>$value]);
	}

	/**
	 * 通过指定字段值返回行记录集
	 * @param string $field
	 * @param string $value
	 * @return array
	 */
	function getRowsByFieldValue($field,$value,$fields=null){
		if($fields==null) $fields = '*';
		return $this->db->select($this->table,$fields,[$field=>$value]);
	}

	/**
	 * 通过指定字段值返回行记录
	 * @param array $where //条件数组
	 * @return array
	 */
	function getRowByWhere($where,$fields=null){
		if($fields==null) $fields = '*';
		return $this->db->get($this->table,$fields,$where);
	}

	/**
	 * 通过指定条件返回行记录集
	 * @param array $where //条件数组
	 * @return array
	 */
	function getRowsByWhere($where,$fields=null,$group=null){
		if($fields==null) $fields = '*';
		if($group!=null) $where['GROUP'] = $group;
		return $this->db->select($this->table,$fields,$where);
	}

	/**
	 * 通过指定条件返回多少条记录
	 * @param array $where //条件数组
	 * @return int
	 */
	function getCountByWhere($where,$group=null){
		if($group!=null) $where['GROUP'] = $group;
		return $this->db->count($this->table,$where);
	}

	/**
	 * 查询某个字段值是否存在
	 * @param string $field
	 * @param string $value
	 * @return array
	 */
	function has($field,$value){
		return $this->hasWithOut($field,$value);
	}

	/**
	 * 查询主键是否存在
	 * @param string $value
	 * @return array
	 */
	function hasKey($value){
		return $this->has($this->key,$value);
	}

	/**
	 * 查询某个字段值是否存在,但不包含某个key
	 * @param string $field
	 * @param string $value
	 * @return array
	 */
	function hasWithOut($field,$value,$key=null){
	    $where = (is_null($key)) ? [$field=>$value] : [$field=>$value,$this->key.'[!]'=>$key];
		$row = $this->getRowByWhere($where,[$this->key]);
        return !empty($row);
	}

	/**
	 * 得到全部记录
	 * @return array
	 */
	function getRowsAll(){
		return $this->db->select($this->table,'*',['ORDER'=>$this->order]);
	}
	/**
	 * 得到全部记录Key,Value键值对,为js排序考虑,使用有序二维数组
	 * @return array
	 */
	function getRowsKeyValueAll(){
		$rows = $this->getRowsAll($this->key,$this->name);
        $data = [];
        foreach($rows as $k=>$v){
            $data[$k]=[ 'key'=>$v[$this->key], 'value' => $v[$this->name] ];
        }
        return $data;
	}
	/**
	 * edit ceil 值修改
	 * @return array
	 */
	function setValue($key,$field,$value){
		return $this->db->update($this->table,[$field=>$value],[$this->key=>$key]);
	}
	/**
	 * 
	 * @return array
	 */
	function getValue($key,$field){
		return $this->db->get($this->table,$field,[$this->key=>$key]);
	}
	/**
	 * 根据条件统计记录数
	 * @param array $where, 条件
	 * @return bool
	 */
	function rowCount($where=[]){
		return $this->db->count($this->table,$where);
	}
	
	/**
	 * 删除记录
	 * @param string|int|array $ids, id集数组或字符串，字符串用 , 号分隔
	 * @return bool
	 */
	function deleteRows($ids){
        if(!is_array($ids)){
            $ids = explode(',',$ids);
        }
		return $this->db->delete($this->table,[$this->key=>$ids]);
	}
	
	/**
	 * 新添或替换记录
	 * @param array 
	 * @return bool
	 */
	function replaceRow($data,$keys=null,$noUpdateFields=null){
		return $this->db->replace($this->table,$data,$keys,$noUpdateFields);
	}

	/****************************************键值表*******************************************/

	/**
	 * 取键值表值
	 * @param string $k  键
	 * @param string $u  用户名,默认为空
	 * @return array

		CREATE TABLE `ckv` (
		  `k` varchar(255) NOT NULL DEFAULT '' COMMENT '键',
		  `u` varchar(20) NOT NULL DEFAULT '' COMMENT '用户',
		  `v` text COMMENT '值',
		  PRIMARY KEY (`k`,`u`)
		) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='键值配置表'

	 */
	function getKV($k,$u=''){
		$where['k'] = $k;
		if($u!='') $where['u'] = $u;
		$value = $this->db->get('ckv','v',$where);
		return json_decode($value,true);
	}

	/**
	 * 写键值表值
	 * @param string $k  键
	 * @param array $v  值
	 * @param string $u  用户名,默认为空
	 * @return bool
	 */
	function setKV($k,$v,$u=''){
		if($v!='') $v = json_encode($v);
		return $this->db->replace('ckv',['k'=>$k,'u'=>$u,'v'=>$v],['k','u']);
	}
    
	/****************************************非通用 开始*******************************************/
    
	/****************************************非通用 结束******************************************/
}