<?php
/**
 * @class  	树表操作(id,parent_id,path,name 父ID类型),返回的数据对接 jsTree模式
 */
class LibTree
{
	protected $dbRead = null;
	protected $dbWrite = null;
	protected $table = null;
	protected $fieldId = null;
	protected $fieldParentId = null;
	protected $fieldCode = null;
	protected $fieldText = null;
	protected $fieldKeyword = null;
	protected $fieldLevel = null;
	protected $fieldSort = null;
	protected $fieldLft = null;
	protected $fieldRgt = null;
	protected $order = null;
	protected $rootName = null;
	/**
	  * @param [string] $rootName 树节点名称
	  * @param [string] $tableName 表的名称
	  * @param [arr] $fields 树字段
	  */
	function __construct($rootName,$tableName,$fields){
		//需要在之前初始化数据库类
		$this->dbRead = $GLOBALS['mainDbReadLink'];
		$this->dbWrite = $GLOBALS['mainDbWriteLink'];
		$this->table=$tableName;
		$this->fieldId=$fields['fieldId'];
		$this->fieldParentId=$fields['fieldParentId'];
		$this->fieldCode=$fields['fieldCode'];
		$this->fieldText=$fields['fieldText'];
		$this->fieldKeyword=$fields['fieldKeyword'];
		$this->fieldLevel=$fields['fieldLevel'];
		$this->fieldLft=$fields['fieldLft'];
		$this->fieldRgt=$fields['fieldRgt'];
		$this->fieldSort=$fields['fieldSort'];
		$this->order=$fields['order'];
		$this->rootName=$rootName;
	}
	/**
	 * Method 搜索节点
	  * @param  [string]  $text  要搜索的文本
	  * @return [array]
	 */
    public function searchNode($text,$parentId=0){
		$text = str_replace("'","\'",$text);
		if($this->fieldKeyword){
			$sql = sprintf("SELECT * FROM %s where %s like '%%%s%%'", $this->table, $this->fieldKeyword,$text);
		}else{
			$sql = sprintf("SELECT * FROM %s where %s like '%%%s%%'", $this->table, $this->fieldText,$text);
		}
		if($parentId>0){
			$row = $this->getRowById($parentId);
			$sql .= sprintf(" AND %s>%s AND %s<%s", $this->fieldLft, $row[$this->fieldLft], $this->fieldRgt, $row[$this->fieldRgt]); 
		}
		$sql .= " limit 0,200";
		$data = [];
		if($table = $this->dbRead->Table($sql)){
			foreach($table as $row){
				$this->getAllParentRow($row[$this->fieldParentId],$data);
				$data[] = $this->formatJsTreeData($row);
			}
		}
		$ids[] = '#';
		foreach($data as $v){
			if(!in_array($v['id'],$ids)){
				$ids[] = $v['id'];
			}
		}
		return $ids;
	}
	/**
	  * @method 创建节点
	  * @param  [string]  $parent_id  父ID
	  * @param  [string] $text  节点名
	  * @return [bool]
	  */
	public function createNode($parentId,$text){
		$parentRow = $this->getRowById($parentId);
		$sort = $this->getMaxSortByParent($parentId)+1;
		$data = [
				$this->fieldParentId=>$parentId,
				$this->fieldText=>$text,
				$this->fieldLevel=> $parentRow[$this->fieldLevel]+1,
				$this->fieldSort=> $sort
			];
		$result = 0;
		if($this->dbWrite->Insert($this->table,$data)) $result = $this->dbWrite->InsertID();
		return $result;
	}
	/**
	  * @method 修改节点名称
	  * @param  [string]  $id  ID
	  * @param  [string] $text  节点名
	  * @return [bool]
	  */
	public function renameNode($id,$text){
		return $this->dbWrite->Update($this->table,sprintf("%s='%s'",$this->fieldText,$text),sprintf("%s='%s'",$this->fieldId,$id));
	}
	/**
	  * @method 删除节点
	  * @param  [string]  $id  ID
	  * @param  [function]  $beginFunction  删除前callback, 返回true则执行
	  * @return [bool]
	  */
	public function deleteNode($id,$beginFunction){
		if($id && $beginFunction()){
			if($table = $this->getRowsByParent($id)){
				foreach($table as $row){
					$this->deleteNode($row[$this->fieldId]);
				}
			}
			//$this->dbWrite->Delete($this->table,sprintf("%s='%s'",$this->fieldId,$id));
			return true;
		}else{
			return false;
		}
	}
	/**
	  * @method 移动节点
	  * @param  [string]  $id  ID
	  * @param  [string]  $parent  新父ID
	  * @param  [string]  $old_parent  老父ID
	  * @param  [string]  $position  新的位置
	  * @param  [string]  $old_position  老的位置
	  * @return [bool]
	  */
	public function moveNode($id,$parent,$old_parent,$position,$old_position){
		if($id){
			//转换父节点
			if($parent != $old_parent){
				$level = $this->getLevelById($id);
				$newLevel = $this->getLevelById($parent)+1;
				if($level!=$newLevel){
					$this->updateLevel($id,$newLevel);
				}
				$this->dbWrite->Update($this->table,
					sprintf("%s='%s', %s=999999999",$this->fieldParentId,$parent,$this->fieldSort),
					sprintf("%s='%s'",$this->fieldId,$id)
				);
			}
			//修改位置
			//重置所有序号
			$sql = sprintf("SELECT * FROM %s WHERE %s='%s' ORDER BY %s,%s", 
						$this->table, 
						$this->fieldParentId, 
						$parent, 
						$this->fieldLevel,$this->fieldSort
			);
			if($table = $this->dbWrite->Table($sql)){
				$i=0;
				foreach($table as $row){
					$this->dbWrite->Update($this->table,sprintf("%s=%d",$this->fieldSort,$i),sprintf("%s='%s'",$this->fieldId,$row[$this->fieldId]));
					$i++;
				}
			}
			//将兄弟节点在当前位置后的序号+1
			$this->dbWrite->Update($this->table,
				sprintf("%s=%s+1",$this->fieldSort,$this->fieldSort),
				sprintf("%s='%s' AND %s>=%d",$this->fieldParentId,$parent,$this->fieldSort,$position)
			);
			//更新当前节点的序号
			$this->dbWrite->Update($this->table,
				sprintf("%s=%d",$this->fieldSort,$position),
				sprintf("%s='%s'",$this->fieldId,$id)
			);
			return true;
		}else{
			return false;
		}
	}
	/**
	  * @method 更新Level
	  * @param  [string]  $id  ID
	  * @param  [int]  $level  新的位置
	  * @return [bool]
	  */
	public function updateLevel($id,$level){
		if($id){
			if($table = $this->getRowsByParent($id)){
				foreach($table as $row){
					$this->updateLevel($row[$this->fieldId],$level+1);
				}
			}
			$this->dbWrite->Update($this->table,sprintf("%s=%d",$this->fieldLevel,$level),sprintf("%s='%s'",$this->fieldId,$id));
			return true;
		}else{
			return false;
		}
	}

	public function count(){
		$sql = sprintf("SELECT count(1) FROM %s", $this->table);
		return $this->dbRead->One($sql);
	}
	public function getRowById($id){
		$sql = sprintf("SELECT * FROM %s WHERE %s=%d LIMIT 0,1", $this->table, $this->fieldId,$id);
		return $this->dbRead->Row($sql);
	}
	public function getLevelById($id){
		$sql = sprintf("SELECT %s FROM %s WHERE %s=%d LIMIT 0,1", $this->fieldLevel, $this->table, $this->fieldId,$id);
		return $this->dbRead->One($sql);
	}
	/**
	  * @method 取所有父节点的行记录
	  * @param  [string]  $id  ID
	  * @param  [array]  $data
	  * @return [bool]
	  */
	public function getAllParentRow($parentId,&$data){
		if($row = $this->getRowById($parentId)){
			if($row[$this->fieldParentId]>0)
				$this->getAllParentRow($row[$this->fieldParentId],$data);
			$data[] = $this->formatJsTreeData($row,true);
		}
	}
	/**
	  * @method 返回所有等于父ID的行记录
	  * @param  [string]  $parent_id  父ID
	  * @return [bool]
	  */
	public function getRowsByParent($parent_id,$level=-1,$noid=0){
		$where = '';
		if($level>=0){
			$row = $this->getRowById($parent_id);
			$level = $row[$this->fieldLevel] + $level;
			$where .= sprintf(" AND %s<=%d",$this->fieldLevel,$level);
		}
		if($noid>0)
			$where .= sprintf(" AND %s<>%d",$this->fieldId,$noid);
		$sql = sprintf("SELECT *,(SELECT count(1) FROM `%s` WHERE %s=s.`%s`) children FROM `%s` s WHERE %s=%d %s ORDER BY %s", 
			$this->table, 
			$this->fieldParentId,
			$this->fieldId,
			$this->table, 
			$this->fieldParentId,
			$parent_id,
			$where,
			$this->order);
		return $this->dbRead->Table($sql);
	}
	/**
	  * @method 返回所有等于父ID的行记录最大的序号
	  * @param  [string]  $parent_id  父ID
	  * @return [int]
	  */
	public function getMaxSortByParent($parent_id){
		$result = 0;
		if($table = $this->getRowsByParent($parent_id)){
			foreach($table as $row){
				if($row[$this->fieldSort]>$result) $result = $row[$this->fieldSort];
			}
		}
		return $result;
	}
	/**
	  * @method 是否有子节点
	  * @param  [string] $id
	  * @return [bool]
	  */
	public function hasChildren($id){
		$sql = sprintf("SELECT 1 FROM %s WHERE %s='%s' LIMIT 0,1", $this->table, $this->fieldParentId,$id);
		return $this->dbRead->One($sql);
	}
	/**
	  * @method 返回所有等于父ID的行数据格式
	  * @param  [string]  parent_id  父ID
	  * @return [bool]
	  */
	public function getDataByParent($parent_id,$level=-1,$noid=0,$root=0){
		$data = [];
		if($parent_id=='#'){
			$children = ($this->count()) ? true : false;
			return  [
				'id'=> "0",
				'text'=>$this->rootName,
				'parent'=> '#',
				'children'=> $children,
				"type"=> 'root',
				"state"=>["opened"=>true]
			];
		}else if($root){
			$table = $this->getRowsByParent($parent_id,$level,$noid);
			$children = ($table) ? true : false;
			$row = $this->getRowById($root);
			return  [
				'id'=> $root,
				'text'=>$row[$this->fieldText],
				'parent'=> '#',
				'children'=> $children,
				"type"=> 'root',
				"state"=>["opened"=>true]
			];
		}else{
			if($table = $this->getRowsByParent($parent_id,$level,$noid)){
				foreach($table as $row){
					$data[] = $this->formatJsTreeData($row,true,$level);
				}
				return $data;
			}
		}
	}
	/**
	  * @method 返回所有等于父ID的行数据格式
	  * @param  [string]  $level  小于等于 哪一层级的节点,-1表示所有
	  * @return [bool]
	  */
	public function getDataAll($level=-1){
		if($level>=0)
			$where = sprintf(" WHERE %s<=%d",$this->fieldLevel,$level);
		$sql = sprintf("SELECT * FROM %s %s ORDER BY %s", $this->table, $where, $this->order);
		$data = [];
		$data[] = [
			'id'=> "0",
			'text'=> $this->rootName,
			'parent'=> '#',
			"type"=> 'folder',
			"state"=>["opened"=>true]
		];
		if($table = $this->dbRead->Table($sql)){
			foreach($table as $row){
				$data[] = $this->formatJsTreeData($row,false);
			}
		}
		return $data;
	}
	/**
	  * @method 返回所有等于父ID的行数据格式
	  * @param  [array]  $row  行记录
	  * @param  [bool]  $isAddChildren  是否添加有无子节点属性,纯json不需要，ajax一级级请求需要
	  * @return [bool]
	  */
	public function formatJsTreeData($row,$isAddChildren=true,$level=-1){
		$parent = $row[$this->fieldParentId];
		$children = ($this->hasChildren($row[$this->fieldId])) ? true : false;
		$icon = ($children) ? '' : 'glyphicon glyphicon-file';
		$type = ($children) ? 'folder' : 'file';
		$data = [
			'id'=> $row[$this->fieldId],
			'text'=>$row[$this->fieldText],
			'parent'=> $parent,
			"type"=> $type
		];
		if($this->fieldCode) $data['text'] = $row[$this->fieldCode].' '.$data['text'];
		if($level>=0 && $row[$this->fieldLevel]>=$level){ //超过层级限制
			;
		}else if($isAddChildren){
			$data['children'] = $children;
		}
		return $data;
	}
}