<?php
require_once('TTrieMB.class.php');
class TKeyword {
	var $keywords = array();
	function __construct() {
	}

	function load() {
		$this->tree = new TTrieMB();
		$this->_loadDbKeyword();
		//$this->_loadHatenaKeyword();
	}

	function _loadDbKeyword() {
		include 'db.inc.php';
		$tmp = $db->selectList('keywords', 'keyword');
		foreach ($tmp as $row) {
			$this->tree->add($row);
		}
	}

	function matchAll($source) {
		$matches = array();
		$source = mb_strtoupper($source);
		$len = mb_strlen($source);
		for ($i=0; $i<$len; $i++) {
			$cnt = 1;
			$k = mb_substr($source, $i, $cnt);
			$ret = $this->tree->searchO($k);
			if (! $ret)
				continue;
			if ($ret->s)
				$matches[] = $ret->s;

			while (count($ret->trie) > 0) {
				$cnt++;
				$k = mb_substr($source, $i, $cnt);
				$ret = $ret->searchO($k, $cnt-1);
				if (! $ret)
					break;
				if ($ret->s)
					$matches[] = $ret->s;
			}
		}
		return $matches;
	}

	//最長一致パターン1。先優先最長一致。
	//マッチするキーワード数が少ない場合、matchLongest2よりも速い
	//"こん"と"んにちは"が登録されていて、こんにちはを検索した場合、"こん"がヒットする
	function matchLongest($source) {
		$matches = array();
		$source = mb_strtoupper($source);
		$len = mb_strlen($source);
		for ($i=0; $i<$len; $i++) {
			$k = mb_substr($source, $i, 1);
			$ret = $this->tree->search($k);
			if ($ret === FALSE)
				continue;
			if (count($ret) > 0) {
				usort($ret, '_tkeyword_strcomp');
				foreach ($ret as $keyword) {
					$l = mb_strlen($keyword);
					if (mb_substr($source, $i, $l) == $keyword) {
						$matches[] = $keyword;
						$i+=$l-1;
						break;
					}
				}
			}
		}
		return $matches;
	}

	//最長一致パターン2。全マッチ後絞り込み
	//マッチするキーワード数が多い場合、matchLongestよりも速い
	//"こん"と"んにちは"が登録されていて、こんにちはを検索した場合、"んにちは"がヒットする
	function matchLongest2($source) {
		$tmp = mb_strtoupper($source);
		$matches = $this->matchAll($tmp);
		if (empty($matches))
			return $matches;
		$final_matches = array();
		usort($matches, '_tkeyword_strcomp');
		foreach ($matches as $match) {
			$cnt = 0;
			$tmp = str_replace($match, '', $tmp, $cnt);
			if ($cnt)
				$final_matches[] = $match;
		}
		return $final_matches;
	}

	//ただのデモ用。
	function matchLongestToHtml($source) {
		$s = array();
		$len = mb_strlen($source);
		for ($i=0; $i<$len; $i++) {
			$k = mb_substr($source, $i, 1);
			$ret = $this->tree->search($k);
			if ($ret === FALSE) {
				$s[] = $k;
				continue;
			}
			if (count($ret) > 0) {
				usort($ret, '_tkeyword_strcomp');
				$has = FALSE;
				foreach ($ret as $keyword) {
					$l = mb_strlen($keyword);
					if (mb_substr($source, $i, $l) == $keyword) {
						$matches[] = $keyword;
						$i+=$l-1;
						$s[] = '<a href="http://d.hatena.ne.jp/keyword/'. rawurlencode($keyword).'">'.$keyword.'</a>';
						$has = TRUE;
						break;
					}
				}
				if (! $has)
					$s[] = $k;
			}
		}
		return implode($s);
	}

	function _loadTest() {
		$this->tree->add('こんにちは');
		$this->tree->add('こんばんは');
		$this->tree->add('こんにちは、僕は');
		$this->tree->add('こんにちは、私は');
		$this->tree->add('いもふ');
		$this->tree->add('ニート');
		$this->tree->add('ネオニート');
		$this->tree->add('ベトナム');
	}

	function _loadWikiKeyword() {
		$fp = fopen('jawiki-latest-all-titles-in-ns0', 'r');
		if (! $fp)
			return FALSE;

		while (! feof($fp)) {
			$buf = fgets($fp);
			if ($buf)
				$this->tree->add($buf);
		}

		fclose($fp);
		return TRUE;
	}

	function _loadHatenaKeyword() {
		$fp = fopen('keywordlist_furigana.csv', 'r');
		if (! $fp)
			return FALSE;

		while (! feof($fp)) {
			$buf = fgets($fp);
			list($furigana, $keyword) = explode("\t", $buf);
			if ($keyword)
				$this->tree->add($keyword);
		}

		fclose($fp);
		return TRUE;
	}
}

function _tkeyword_strcomp($s1, $s2) {
	$l1 = mb_strlen($s1);
	$l2 = mb_strlen($s2);
	if ($l1 == $l2)
		return 0;
	return ($l1 > $l2) ? -1 : 1;
}
?>