<?

/*
статусы
1 - опубликован
2 - ожидает модерации
3 - удален
*/

class LabComments
{
	protected $module_id;
	
	public $status;
	public $user_id;
	public $level;
	public $item_key;
	public $url;
	
	function __construct($comment_id=null, $item_id=null)
	{
		$this->module_id=LabCMS::i()->getModuleId();
		$this->_setCommentInfo($comment_id, $item_id);
	}
	
	public function add($data)
	{
		if (!$this->id)
		{
			$left_key=$this->_getMaxKey()+1;
			$level=1;
		}
		else
		{
			$left_key=$this->right_key;
			$level=$this->level+1;
		}
		$right_key=$left_key+1;
		$comment_id=Db::i()->query("INSERT INTO ?_comments
							  (
							  parent_id,
							  item_id,
							  text,
							  putdate,
							  user_id,
							  login,
							  email,
							  status,
							  left_key,
							  right_key,
							  level,
							  ip)
							  VALUES(?, ?, ?, NOW(), ?, ?, ?, ?, ?, ?, ?, INET_ATON(?))",
							  $this->id,
							  $this->item_id,
							  $data['text'],
							  User::i()->isGuest()?null:User::i()->id,
							  User::i()->isGuest()?$data['login']:User::i()->login,
							  User::i()->isGuest()?$data['email']:User::i()->email,
							  $data['status'],
							  $left_key,
							  $right_key,
							  $level,
							  User::i()->getIP()); 
		if ($comment_id)
		{
			Db::i()->query("UPDATE ?_comments SET right_key=right_key+2, left_key=IF(left_key>?, left_key+2, left_key) WHERE right_key>=? AND comment_id!=? AND item_id=?", $left_key, $left_key, $comment_id, $this->item_id);

			$this->_changeCounter(1);
			return $comment_id;
		}
	}
	
	public function edit($data)
	{
		if (!$this->_checkCommentDeleted()) return false;
		Db::i()->query("UPDATE ?_comments SET
										text=?
										{, login=?}
										{, email=?}
										, updated=NOW()
										WHERE comment_id=?",
										$data['text'],
										$data['login']?$data['login']:DBSIMPLE_SKIP,
										$data['email']?$data['email']:DBSIMPLE_SKIP,
										$this->id);
		return true;
	}
	
	public function delete()
	{
		if ($this->_checkCommentExists() && $this->_checkCommentDeleted())
		{
			return Db::i()->query("UPDATE ?_comments SET status=3, abuse=NULL, updated=NOW() WHERE comment_id=?", $this->id);
		}
		return false;
	}
	
	public function unhide()
	{
		if ($this->_checkCommentExists() && $this->_checkCommentDeleted())
		{
			return Db::i()->query("UPDATE ?_comments SET status=1, updated=NOW() WHERE comment_id=?", $this->id);
		}
	}
	
	public function undelete()
	{
		if ($this->_checkCommentExists())
		{
			if (!$this->_commentDeleted()) return $this->_error('Ошибка восстановления комментария');
			Db::i()->query("UPDATE ?_comments SET status=1, updated=NOW() WHERE comment_id=?", $this->id);
			return true;
		}
		return false;
	}

	public function deleteBranch()
	{
		if ($this->_checkCommentExists())
		{
			$count=Db::i()->query("DELETE FROM ?_comments WHERE left_key>=? AND right_key<=? AND item_id=?", $this->left_key, $this->right_key, $this->item_id);
			Db::i()->query("UPDATE ?_comments
								SET left_key=IF(left_key>?d, left_key-(?d-?d + 1), left_key),
								right_key=right_key-(?d-?d+1)
								WHERE right_key>?d
								 AND item_id=?",
								$this->left_key,
								$this->right_key,
								$this->left_key,
								$this->right_key,
								$this->left_key,
								$this->right_key,
								$this->item_id);
			$this->_changeCounter(-$count);
			return true;
		}
		return false;
	}
	
	public function abuse()
	{
		if (!array_key_exists(User::i()->id, (array)$this->abuse))
		{
			$this->abuse[User::i()->id]=User::i()->login;
			Db::i()->query("UPDATE ?_comments SET abuse=? WHERE comment_id=?", serialize($this->abuse), $this->id);
			return true;
		}
		return false;
	}
	
	protected function _getMaxKey()
	{
		return intval(Db::i()->selectCell("SELECT MAX(right_key) FROM ?_comments WHERE item_id=?", $this->item_id));
	}
	
	protected function _setCommentInfo($comment_id, $item_id)
	{
		if ($comment_id) $this->info=$this->getCommentInfo($comment_id);
		elseif($item_id) $this->info=$this->getItemInfo($item_id);
		if ($this->info)
		{
			foreach($this->info as $k=>$v)
			{
				$this->$k=$v;
			}
		}
		else $this->_error("Комментарий не найден");
	}
	
	public function getCommentInfo($comment_id)
	{
		if ($this->id==$comment_id) return $this->info;
		$info=Db::i()->selectRow("SELECT *, INET_NTOA(ip) as ip FROM ?_comments c
									JOIN ?_comments_items ci ON c.item_id=ci.item_id
									WHERE comment_id=? AND module_id=?", $comment_id, $this->module_id);
		if ($info)
		{
			$info=self::_getCommentArray($info);
			$info['id']=$info['comment_id'];
		}
		return $info;
	}
	
	public function getItemInfo($item_id)
	{
		return Db::i()->selectRow("SELECT * FROM ?_comments_items ci
									WHERE item_id=? AND module_id=?", $item_id, $this->module_id);
	}
	
	public static function getComments($item_key, $url)
	{
		$result=Db::i()->select("SELECT *, UNIX_TIMESTAMP(updated) as last_update, INET_NTOA(ip) as ip, ci.item_id as item_id
									FROM ?_comments_items ci
									LEFT JOIN ?_comments c ON ci.item_id=c.item_id
									WHERE item_key=?
									AND module_id=?
									ORDER BY left_key", $item_key, LabCMS::i()->getModuleId());
		if ($result) 
		{
			$comments['url']=$result[0]['url'];
			$comments['counter']=$result[0]['counter'];
			$comments['item_id']=$result[0]['item_id'];

			if(!$result[0]['comment_id']) unset($result[0]);
			if($result)	self::_getCommentsArray($result, $comments);

			$result=null;
			//TODO на время отладки проверка структуры комментариев. Потом убрать.
			if ($comments['comments'] && access('admin') && $error=self::_checkStructure($comments['item_id'])) LabCMS::i()->setMessage(_m('Нарушена структура комментариев. ').$error, false, true);
		}
		else $comments=self::_addItem($item_key, $url);

		return $comments;
	}
	
	public function getNewComments()
	{
		$result=Db::i()->select("SELECT *, INET_NTOA(ip) as ip FROM ?_comments 
										WHERE comment_id>? AND item_id=?", $this->id, $this->item_id);
		if ($result) self::_getCommentsArray($result, $comments);
		$result=null;
		return $comments['comments'];
	}
	
	public static function getAbusedComments($all_modules=false)
	{
		$result=Db::i()->select("SELECT *, INET_NTOA(ip) as ip FROM ?_comments_items ci
								JOIN ?_comments c ON ci.item_id=c.item_id
								WHERE {module_id=? AND}
								abuse IS NOT NULL
								ORDER BY left_key", $all_modules?DBSIMPLE_SKIP:LabCMS::i()->getModuleId());
		if ($result) 
		{
			self::_getCommentsArray($result, $comments);
			$result=null;
			return $comments;
		}
	}
	
	public static function getAbusedCounter($all_modules=false)
	{
		return Db::i()->selectCell("SELECT COUNT(comment_id) FROM ?_comments_items ci
								JOIN ?_comments c ON ci.item_id=c.item_id
								WHERE {module_id=? AND}
								abuse IS NOT NULL", $all_modules?DBSIMPLE_SKIP:LabCMS::i()->getModuleId());
	}
	
	public static function getModerationComments($all_modules=false)
	{
		$result=Db::i()->select("SELECT *, INET_NTOA(ip) as ip FROM ?_comments_items ci
								JOIN ?_comments c ON ci.item_id=c.item_id
								WHERE {module_id=? AND}
								`status`=2
								ORDER BY left_key", $all_modules?DBSIMPLE_SKIP:LabCMS::i()->getModuleId());
		if ($result) 
		{
			self::_getCommentsArray($result, $comments);
			$result=null;
			return $comments;
		}
	}
	
	public static function getModerationCounter($all_modules=false)
	{
		return Db::i()->selectCell("SELECT COUNT(comment_id) FROM ?_comments_items ci
								JOIN ?_comments c ON ci.item_id=c.item_id
								WHERE {module_id=? AND}
								`status`=2", $all_modules?DBSIMPLE_SKIP:LabCMS::i()->getModuleId());
	}
	
	
	
	public static function updateAbusedComments($data)
	{
		if (!empty($data)) foreach($data as $comment_id=>$action)
		{
			$cmt=new self($comment_id);
			if ($action=='del' || $action=='ok')
			{
				if ($action=='del') $cmt->delete();
				$cmt->_resetAbuse();
			}
		}
	}
	
	public static function updatePremoderatedComments($data)
	{
		if (!empty($data)) foreach($data as $comment_id=>$action)
		{
			$cmt=new self($comment_id);
			if ($action=='del' || $action=='ok')
			{
				if ($action=='del') $cmt->delete();
				$cmt->unhide();
			}
		}
	}
	
	public static function deleteItem($item_key)
	{
		Db::i()->query("DELETE c, ci FROM ?_comments c JOIN ?_comments_items ci ON c.item_id=ci.item_id WHERE item_key=? AND module_id=?", $item_key, LabCMS::i()->getModuleId());
	}
	
	public static function getCommentsCounters($items)
	{
		return Db::i()->selectCol("SELECT item_key AS ARRAY_KEY, counter FROM ?_comments_items WHERE item_key IN (?a) AND module_id=?", (array)$items, LabCMS::i()->getModuleId());
	}
	
	public static function getLastComments($item_id=null, $current_module=null)
	{
		if ($item_id) $current_module=null;
		$result=Db::i()->select("SELECT *, UNIX_TIMESTAMP(updated) as last_update, INET_NTOA(ip) as ip
								FROM ?_comments_items ci
								JOIN ?_comments c ON ci.item_id=c.item_id
								WHERE level!=0 
								{AND item_id=?}
								{AND module_id=?}
								ORDER BY left_key",
								$item_id?$item_id:DBSIMPLE_SKIP,
								$current_module?LabCMS::i()->getModuleId():DBSIMPLE_SKIP
								);
		if ($result) 
		{
			self::_getCommentsArray($result, $comments);
			return $comments;
		}
	}
	
	public static function getLastUpdate($item_key)
	{
		return Db::i()->selectCell("SELECT UNIX_TIMESTAMP(MAX(updated))
								FROM ?_comments_items ci USE INDEX(item_key) JOIN ?_comments c ON ci.item_id=c.item_id
								WHERE item_key=? AND module_id=?", $item_key, LabCMS::i()->getModuleId());
	}
	
	protected function _resetAbuse()
	{
		Db::i()->query("UPDATE ?_comments SET abuse=NULL WHERE comment_id=?", $this->id);
	}
	
	public static function updateUrl($item_id, $url)
	{
		Db::i()->query("UPDATE ?_comments_items SET url=? WHERE item_id=? AND module_id=?", $url, $item_id, LabCMS::i()->getModuleId());
	}
	
	protected static function _addItem($item_key, $url)
	{
		$item=array('item_key'=>$item_key, 'url'=>$url, 'module_id'=>LabCMS::i()->getModuleId());
		$item['item_id']=Db::i()->query("INSERT IGNORE INTO ?_comments_items (?#) VALUES(?a)", array_keys($item), array_values($item));
		if ($item['item_id'])
		{
			$item['comments']=array();
			$item['last_update']=time();
			$item['counter']=0;
			return $item;
		}
	}
	
	protected static function _getCommentsArray($data, &$comments)
	{
		foreach($data as $item)
		{
			$comments['comments'][]=self::_getCommentArray($item);
			if ($comment['last_update']>$comments['last_update']) $comments['last_update']=$comment['last_update'];
		}
	}
	
	protected static function _getCommentArray($item)
	{
		$abuse=unserialize($item['abuse']);
		$item['abuse']=$abuse;
		$item['abuse_count']=$abuse?count($abuse):0;
		$item['leaf']=($item['right_key']-$item['left_key']-1)?false:true;
		return $item;
	}
	
	protected function _checkCommentExists()
	{
		if (!$this->id) return $this->_error("Комментарий не найден");
		return true;
	}
	
	
	protected function _checkCommentDeleted()
	{
		if ($this->_commentDeleted()) return $this->_error("Комментарий удален");
		return true;
	}
	
	protected function _commentDeleted()
	{
		return $this->status==3;
	}
	
	protected function _error($msg, $lang=false)
	{
		return LabCMS::i()->error($msg, $lang);
	}
	
	protected static function _checkStructure($item_id)
	{
		$result=Db::i()->selectCol("SELECT comment_id FROM ?_comments WHERE left_key>=right_key AND item_id=?", $item_id);
		if ($result) return 'Элементы с левым ключом больше правого: '.implode(', ', $result);
		
		$result=Db::i()->selectRow("SELECT COUNT(comment_id) as total, MIN(left_key) as min_key, MAX(right_key) as max_key FROM ?_comments WHERE item_id=?", $item_id);
		if ($result['min_key']!=1) return 'Миннимальный ключ равен '.$result['min_key'];
		if ($result['max_key']!=$result['total']*2) return 'Максимальный ключ равен '.$result['max_key'].' и не равен '.$result['total']*2;
		
		$result=Db::i()->select("SELECT comment_id, MOD((right_key-left_key), 2) AS ostatok FROM ?_comments WHERE item_id=? HAVING ostatok=0", $item_id);
		if ($result) return print_r($result, true);
		
		$result=Db::i()->select("SELECT comment_id, MOD((left_key-level+2), 2) AS ostatok FROM ?_comments HAVING ostatok=0");
		if ($result) print_r($result, true);
		
		$result=Db::i()->select("SELECT t1.comment_id, COUNT(t1.comment_id) AS rep, MAX(t3.right_key) AS max_right
								FROM ?_comments AS t1, ?_comments AS t2, ?_comments AS t3
								WHERE t1.left_key <> t2.left_key
								AND t1.left_key <> t2.right_key
								AND t1.right_key <> t2.left_key
								AND t1.right_key <> t2.right_key
								AND t1.item_id=t2.item_id
								AND t2.item_id=t3.item_id
								AND t3.item_id=?
								GROUP BY t1.comment_id HAVING max_right <> SQRT(4 * rep + 1) + 1 ", $item_id);
		if ($result) return print_r($result, true);
		
		return false;
	}
	protected function _changeCounter($count=1)
	{
		Db::i()->query("UPDATE ?_comments_items SET counter=counter+?d WHERE item_id=?", $count, $this->item_id);
	}
	
	public static function repairComments()
	{
		if ($items=Db::i()->selectCol("SELECT item_id FROM ?_comments_items WHERE counter>0"))
		{
			foreach($items as $item_id)
			{
				self::buildNestedKeys(NULL, 0, 0, $item_id);
			}
		}
	}
	
	protected static function buildNestedKeys($parent_id, $left, $level, $item_id)
	{
		$right = $left+1;
		$level++;
		if ($rows = DB::i()->select("SELECT comment_id FROM ?_comments WHERE item_id = ? { and parent_id = ? } { AND parent_id IS NULL and 1=?d}
		ORDER BY comment_id ASC", $item_id, !is_null($parent_id) ? $parent_id:DBSIMPLE_SKIP, is_null($parent_id) ? 1:DBSIMPLE_SKIP))
		{
			foreach ($rows as $row)
			{
				$right = self::buildNestedKeys($row['comment_id'], $right, $level, $item_id);
			}
		}
		$level--;
		if (!is_null($parent_id))
		{
			DB::i()->query("UPDATE ?_comments
			SET left_key=?d, right_key=?d , `level`=?d
			WHERE comment_id=? ", $left, $right, $level, $parent_id);
		}
		return $right+1;
	}
	
}