<?php

use servers\log\Log;

/**
* 控制器基类(抽象类)
* 定义了一组抽象方法,统一各action方法名，方便阅读及权限控制等
*
* #function 接收参数传递
*       函数定义 function($a,$b=0)
*       $a,$b参数可以接收来自 网址/v/, 网址/k/, _get, _post, php://input 中的传值
*       没有传递的参数使用默认值
*       除 网址/v/ 外其他方式只要键名一至即可，不要求次序相同
*
* #_CallBefore 在Action执行之前调用
*       function _CallBefore($method : string, $params : array)
*           $method 当前调用的方法名
*           $params 当前调用的方法名的参数键值对
*
* #_CallEnd 在Action执行之后调用
*       function _CallEnd($method : string, $params : array)
*           $method 当前调用的方法名
*           $params 当前调用的方法名的参数键值对
*/
abstract class action{
	use answer; //公共应答参数传递类
	
    /**
	  * 格式化数据为json
	  * code=0 表示成功,第二个参数为数据，不等于0表示错误代码,必须项
	  * code<>0 表示失败,第一个参数为错误代码,第二个参数为错误描述
	  * $rows, 不传默认为 null
	  * $page 从1开始, 不传默认为1
	  * $total, 为共多少页, 不传默认为1
	  * $records, 不传如果是数组默认为count($rows)，非数组默认 1
	  * $userdata, 附加值，不传默认为 null
	  * @param ... $var 参数顺序 $code, $rows|$msg, $page, $total, $records, $userdata
	  * @return json 成功 {"code":0,"data":{"a":1},"msg":""} <br> 失败 {"code":2000,"data":null,"msg":"执行数据库操作失败"}
	  */
	function json($code, $vars=[]){
		if($code==0){
			$rows = isset($vars[0]) ? $vars[0] : null;
			$page = isset($vars[1]) ? $vars[1] : 1;
			$total = isset($vars[2]) ? $vars[2] : 1;
			if(isset($vars[3])){
				$records = $vars[3];
			}else if(is_array($rows)){
				$records = count($rows);
			}else{
				$records = 1;
			}
			$userdata = isset($vars[4]) ? $vars[4] : null;
			$result = [
				'code' => 0,
				'page'=> $page,
				'total'=> $total,
				'records' => $records,
				'rows' => $rows,
				'userdata' => $userdata
			];
		}else{
			$msg = $vars;
			$result['code'] = $code;
			$result['msg'] = (isset(\mvc::$errorCode[$code])) ? \mvc::$errorCode[$code].', '.$msg : \mvc::$errorCode[1].', '.$msg;
		}
		return json_encode($result, JSON_UNESCAPED_UNICODE);
	}


	/**
	  * 提示 输出格式化后的 json
	  * @param string $msg 错误信息
	  * @return  void
	  */
	function msgJson($msg=''){
            $this->exitJson(1,$msg);
	}

	/**
	  * 成功时，输出格式化后的 json 并终止程序
	  * @param ... $vars 参数顺序 $rows, $page, $total, $records, $userdata, 也可以将全部内容用数组键值对放到 第一个参数中
	  * @return  void
	  */
	function successJson(...$vars){
		if(count($vars)==1 && isset($vars[0]['records'])){
			$data = [
				$vars[0]['rows'],
				$vars[0]['page'] * 1,
				$vars[0]['total'] * 1,
				$vars[0]['records'] * 1
			];
			if(isset($vars[0]['userdata']))
				$data[] = $vars[0]['userdata'];
		}else{
			$data = $vars;
		}
		if(\mvc::$isExportOutput){
		    //导出输出模式
		    \mvc::$ExportOutputData = $data;
		}else{
		    $this->exitJson(0, $data);
		}
	}

	/**
	  * 错误时，输出格式化后的 json 并终止程序
	  * @param int $code  错误代码
	  * @param string $msg 错误信息
	  * @return  void
	  */
	function errorJson($code, $msg=''){
		if($code==0){
			throw new \Exception ( lg('0为成功代码，不能出现在错误数据包里') );
		}
		$this->exitJson($code,$msg);
	}

	/**
	  * 输出格式化后的 json 并终止程序
	  * @param int $code  0:成功, 当!=0时为:失败代码
	  * @param array|string $data 数据
	  * @return  void
	  */
	function exitJson($code,$data=''){
		if(!DEBUG) ob_clean ();
		header('Content-Type','application/json');
		echo $this->json($code,$data);
		if(function_exists('fastcgi_finish_request')) {
			fastcgi_finish_request();
		}
		exit();
	}

	/**
	  * 视图输出
	  *
	  * 模板文件路径 [站点]/view/类名/方法.view
	  * @param ... $params  最多支持两个参数
	  * @return  void
	  * @example $this->view() //默认取当前类当前方法的视图
	  * @example $this->view('user/saveOk') //指定视图,当第一个参数是字符串时
	  * @example $this->view(get_defined_vars()) 将当前函数下所有变量转递给模板,当第一个参数是数组时
	  * @example $this->view([name1=>$value1,name2=>value2]),当第一个参数是数组时
	  * @example $this->view('user/saveOk',[name1=>$value1,name2=>value2]) ,两个参数是第一个为字符串视图名，第二个为数组给视图传值
	  * @uses \view::put()
	  */
	public function view(...$params)
	{
		$paramsCount = count($params);
		if($paramsCount==0){
			$viewFile = '';
		}else if($paramsCount==1){
			if(!is_string($params[0])){
				$viewFile = '';
				foreach($params[0] as $k=>$v){
					\view::set($k, $v);
				}
			}else{
				$viewFile = $params[0];
			}
		}else if($paramsCount==2){
			$viewFile = $params[0];
			foreach($params[1] as $k=>$v){
				\view::set($k, $v);
			}
		}else{
			throw new \Exception ( lg('参数数量不正确') );
		}
		\view::put($viewFile);
	}
	/**
	  *跳转到指定的 action
	  *
	  * @param  $action  类/方法名|html文件
	  * @return  void
	  * @example $this->goto('test/demo/demo1');
	  */
	public function goto($action)
	{
		ob_clean();
		header('location:'.\mvc::$cfg['path']['site'].'/'.$action);
		exit();
	}
	/**
	  *单页直接返回视图,并传统url请求参数，单页传参只支持 /k/方式
	  *
	  * @param string $viewfile 当前的视图模板名称
	  * @return  void
	  */
	public function _CallHtml($viewfile){
		$this->view($viewfile,\mvc::$req);
	}
	/**
	 * 当前Action模板文件
	 * @param array $groups  提交的条件
	 * @param function $replaceFunction  自定义替换值函数 function($field,$op,$value)
	 * @return string SQL where 语句
	 */
	function activeView(){
        $class = get_called_class();
		$path = explode('\\',$class);
		$space = $path[count($path)-2];
		$action = str_replace('Action','',end($path));
		$view = ($space=='action') ?  '' : $space.'.';
        $view .= $action . '.';
        return $view;
	}
	/**
	 * 得到全部记录Key,Value键值对
	 * @return array
	 */
	function getListKeyValue(){
		$obj = new $this->myModClass();
        $data = $obj->getRowsKeyValueAll();
        $this->successJson($data);
	}

	/**
	 * 得到全部记录Key,Value键值对生成select 为 jqgrid服务
	 * @return html select options
	 */
    public function getOptionsGrid()
    {
		$obj = new $this->myModClass();
        $rows = $obj->getRowsKeyValueAll();
        $options = '<select name="sn_pro_factory">';
        foreach($rows as $v){
            $options .= '<option value="'.$v['key'].'">'.$v['value'].'</option>';
        }
        $options .= '</select>';
        echo $options;
    }

	/*前置条件(网址传参)
	例: /pro/pair/list?elename=pro_pair&base64=0&rtype=or&rules=pp_p_id1:1;pp_p_id2:1
	例: /pro/pair/list?elename=pro_pair&base64=0&rtype=or&rules=pp_p_id1:@1,2,3,4;pp_p_id2:1
	*/
	public function urlRulesToWhere($req=null){
		if(is_null($req)) $req = \mvc::$req;
		$rulesQuery = (isset($req['rules']) && $req['rules']!='') ? $req['rules'] : null;
		$rules = [];
		if(!is_null($rulesQuery)){
			if(isset($req['base64']) && $req['base64']=='1')
				$rulesQuery = base64_decode($rulesQuery);
			$arr = explode(';',$rulesQuery);
			$rtype = (isset($req['rtype']) && $req['rtype']!='') ? strtoupper($req['rtype']) : 'AND';
			if($rtype=='OR'){
				foreach($arr as $v){
					$t = explode(':',$v);
					$t[1] = trim($t[1]);
					if(!in_array($t[0], ['word']) && substr($t[1],0,1)=='@') $t[1] = explode(',',substr($t[1],1));
					$rules['OR'][trim($t[0])] = $t[1];
				}
			}else{
				foreach($arr as $v){
					$t = explode(':',$v);
					$t[1] = trim($t[1]);
					if(!in_array($t[0], ['word']) && substr($t[1],0,1)=='@') $t[1] = explode(',',substr($t[1],1));
					$rules[trim($t[0])] = $t[1];
				}
			}
		}
		return $rules;
	}

	/**
	 * json 数组转 medoo 格式
	*/
	public function jsonRulesToWhere($jsonArr){
	    $data = [];
		foreach($jsonArr as $k=>$v){
		    $key = $k;
		    preg_match('/.*\{(.*)\}/',$key,$m);
		    if(isset($m[1])){
		        $key = strtr($key,['{'=>'[','}'=>']']);
		    }
		    if($k==='fields'){
		        if(is_string($v)) $v = [$v];
		        $fields = [];
		        foreach($v as $field){
		            if(strpos($field,' as ')>0){
		                list($raw,$as) = explode(' as ',$field);
		                $fields[$as] = raw($raw);
		            }else{
		                $fields[] = $field;
		            }
		        }
		        $v = $fields;
		    }else{
    		    if(is_array($v)){
    		        $v = $this->jsonRulesToWhere($v);
    		    }
		    }
		    $data[$key] = $v;
		}
		return $data;
	}

	public function _CallEnd()
    {
		if(SITE_TAG!='ADMIN') return;
		//后台日志todo
	}
}