<?php



/**
 * オートリンクのための正規表現を管理するクラス。
 * 
 * WikiIDごとにシングルトン。
 */
class AutoLink /* implements PageObserver */
{
	protected $wikiid;	//WikiFarmのID
	
	protected $expression = array();	//オートリンクの正規表現
	protected $ignorelist = array();	//オートリンク対象外のページ
	
	
	/**
	 * インスタンスを取得する。IDごとにシングルトン。
	 * 
	 * @param	$id	WikiFarmのID。指定しない場合は現在のWikiFarmのIDが使われる。
	 */
	static function getinstance($id = WIKIID)
	{
		static $ins = array();
		
		if(!isset($ins[$id])){
			$ins[$id] = new AutoLink($id);
		}
		return $ins[$id];
	}
	
	
	/**
	 * コンストラクタ。
	 */
	protected function __construct($id)
	{
		$this->wikiid = $id;
		
		$page = Page::getinstance(':config/AutoLink/ignore', $id);
		$lines = explode("\n", $page->getsource());
		foreach($lines as $str){
			if(mb_ereg('^-\[\[(.+)\]\]', $str, $m)){
				$this->ignorelist[] = $m[1];
			}
		}
	}
	
	
	/**
	 * オートリンク用正規表現を取得する。
	 * 
	 * @param	string	$dir	起点となるディレクトリ名。
	 * @return	string	正規表現。
	 */
	function getexpression($dir = '')
	{
		if(!isset($this->expression[$dir])){
			$db = DataBase::getinstance($this->wikiid);
			
			$_dir = $db->escape($dir);
			$result = $db->query("SELECT exp FROM autolink WHERE dir = '$_dir'");
			$row = $db->fetch($result);
			if($row == false){
				$exp = $this->makeexpression($this->listup($dir));
				$_exp = $db->escape($exp);
				$db->query("INSERT INTO autolink (dir, exp) VALUES('$_dir', '$_exp')");
				$this->expression[$dir] = $exp;
			}
			else{
				$this->expression[$dir] = $row['exp'];
			}
		}
		return $this->expression[$dir];
	}
	
	
	/**
	 * オートリンクの対象となるページを列挙する。
	 * 
	 * @param	string	$dir	起点となるディレクトリ名。
	 * @return	array(string)	相対パス。
	 */
	protected function listup($dir)
	{
		$db = DataBase::getinstance($this->wikiid);
		
		$query = "SELECT pagename FROM page";
		if($dir != ''){
			$query .= " WHERE pagename like '" . $db->escape($dir) . "/%'";
		}
		
		$result = $db->query($query);
		$list = array();
		if($dir == ''){
			while($row = $db->fetch($result)){
				if(!in_array($row['pagename'], $this->ignorelist)){
					$list[] = $row['pagename'];
				}
			}
		}
		else{
			$len = strlen("{$dir}/");
			while($row = $db->fetch($result)){
				if(!in_array($row['pagename'], $this->ignorelist)){
					$list[] = substr($row['pagename'], $len);
				}
			}
		}
		return $list;
	}
	
	
	/**
	 * オートリンクのための正規表現を生成する。
	 * 
	 * @param	array(string)	&$pagelist	この関数の実行後、$pagelistの中身は保証されない。
	 * @return	string	オートリンクの正規表現
	 */
	protected function makeexpression(&$pagelist)
	{
		if(count($pagelist) <= 1){
			return count($pagelist) == 0 ? '' : mb_ereg_quote($pagelist[0]);
		}
		
		$emptyflag = false;
		$bin = array();
		while($pagelist != array()){
			$pagename = array_pop($pagelist);
			if($pagename != ''){
				$bin[mb_substr($pagename, 0, 1)][] = mb_substr($pagename, 1);
			}
			else{
				$emptyflag = true;
			}
		}
		
		$key = array_keys($bin);
		foreach($key as $k){
			$ret[] = mb_ereg_quote($k) . AutoLink::makeexpression($bin[$k]);
		}
		
		if(count($ret) == 1){
			return $emptyflag ? '(?:'.$ret[0].')?' : $ret[0];
		}
		else{
			return '(?:' . join('|', $ret) . ')' . ($emptyflag ? '?' : '');
		}
	}
	
	
	/**
	 * ページ更新と同時にオートリンク用正規表現を更新する。
	 */
	static function update($page)
	{
		AutoLink::getinstance($page->getwikiid())->refresh();
	}
	
	
	/**
	 * オートリンク用正規表現を作り直す。
	 */
	function refresh()
	{
		$db = DataBase::getinstance($this->wikiid);
		$db->query("DELETE FROM autolink");
	}
}

?>