<?php


/**
 * パースにより生成されたオブジェクトを元にHTML形式の文字列を生成するクラス。
 */
class HTMLConverter
{
	protected $anchormaker;
	protected $root;	//最上位の要素
	
	
	protected function __construct($root)
	{
		$this->root = $root;
		$this->anchormaker = new SectionAnchorMaker;
	}
	
	
	static function visit($e)
	{
		return $e->accept(new HTMLConverter($e));
	}
	
	
	function visitT_Body($e)
	{
		$elements = $e->getelements();
		$ret = array();
		foreach($elements as $elem){
			$ret[] = $elem->accept($this);
		}
		return join("\n", $ret);
	}
	
	
	function visitT_Empty($e)
	{
		return '';
	}
	
	
	function visitT_Heading($e)
	{
		static $list = array('',
			'<h3 class=\"subtitle\" id=\"{$id}\"><a href=\"{$link}#{$id}\"><span class=\"sanchor\">■</span></a> {$str}</h3>',
			'<h4 id=\"{$id}\">{$str}</h4>',
			'<h5 id=\"{$id}\">{$str}</h5>',
			'<h6 id=\"{$id}\">{$str}</h6>');
		static $count = 0;
		
		$level = $e->getlevel();
		if($e->getparent() === $this->root){
			$id = $this->anchormaker->makeid($level, $e->getelem()->getsource());
		}
		else{
			$id = "id$count";
			$count++;
		}
		$link = getURL($e->getpagename());
		$str = $e->getelem()->accept($this);
		return eval("return \"{$list[$level]}\";");
	}
	
	
	function visitT_Horizon($e)
	{
		return '<hr />';
	}
	
	
	function visitT_Pre($e)
	{
		return '<pre>' . htmlspecialchars($e->gettext()) . '</pre>';
	}
	
	
	function visitT_BlockQuote($e)
	{
		return '<blockquote>' . $e->getelem()->accept($this) . '</blockquote>';
	}
	
	
	function visitT_UL($e)
	{
		return "<ul>" . $e->getelem()->accept($this) . "</ul>";
	}
	
	
	function visitT_OL($e)
	{
		return "<ol>" . $e->getelem()->accept($this) . "</ol>";
	}
	
	
	function visitT_List($e)
	{
		$ret[] = '<li>';
		foreach($e->getelements() as $elem){
			$ret[] = $elem->accept($this);
			if(get_class($elem->getnext()) == 'T_LI'){
				$ret[] = "</li>\n<li>";
			}
		}
		$ret[] = '</li>';
		return join('', $ret);
	}
	
	
	function visitT_LI($e)
	{
		return $e->getelem()->accept($this);
	}
	
	
	function visitT_DL($e)
	{
		$ret = array();
		foreach($e->getelements() as $elem){
			$ret[] = $elem->accept($this);
		}
		return "<dl>\n" . join("\n", $ret) . "\n</dl>";
	}
	
	
	function visitT_DT($e)
	{
		return '<dt>' . $e->getelem()->accept($this) . '</dt>';
	}
	
	
	function visitT_DD($e)
	{
		return '<dd>' . $e->getelem()->accept($this) . '</dd>';
	}
	
	
	function visitT_Table($e)
	{
		$ret = array();
		foreach($e->getelements() as $elem){
			$ret[] = $elem->accept($this);
		}
		return "<table>\n" . join("\n", $ret) . "\n</table>";
	}
	
	
	function visitT_TR($e)
	{
		$ret = array();
		foreach($e->getelements() as $elem){
			$ret[] = $elem->accept($this);
		}
		return "\t<tr>\n" . join("\n", $ret) . "\n\t</td>";
	}
	
	
	function visitT_TD($e)
	{
		$ret = array();
		foreach($e->getelements() as $elem){
			$ret[] = $elem->accept($this);
		}
		
		$tag = $e->isheader() ? 'th' : 'td';
		$align = $e->getalign();
		$style = $align != null ? " style=\"text-align: {$align}\"" : '';
		
		return "\t\t<{$tag}{$style}>" . join("\n", $ret) . "</{$tag}>";
	}
	
	
	function visitT_BlockPlugin($e)
	{
		try{
			$plugin = Plugin::getPlugin($e->getpluginname());
			return $plugin->do_block($e->getpagename(), $e->getparam1(), $e->getparam2());
		}
		catch(PluginException $exc){
			return '<p class="warning">' . htmlspecialchars($exc->message()) . '</p>';
		}
	}
	
	
	function visitT_BlockTag($e)
	{
		try{
			$plugin = Plugin::getPlugin($e->getpluginname());
			return $plugin->do_blocktag($e->getpagename(), $e->getparam1(), $e->getparam2());
		}
		catch(PluginException $exc){
			return '<span class="warning">' . htmlspecialchars($exc->message()) . '</span>';
		}
	}
	
	
	function visitT_Comment($e)
	{
		return '';
	}
	
	
	function visitT_Paragraph($e)
	{
		$ret[] = '<div class="subsection">';
		foreach($e->getelements() as $elem){
			$ret[] = '<p>' . $elem->accept($this) . '</p>';
		}
		$ret[] = '</div>';
		return join("\n", $ret);
	}
	
	
	function visitT_Line($e)
	{
		$ret = array();
		foreach($e->getelements() as $elem){
			$ret[] = $elem->accept($this);
		}
		return join('', $ret);
	}
	
	
	function visitT_URL($e)
	{
		$url = htmlspecialchars($e->geturl());
		return "<a href=\"$url\">$url</a>";
	}
	
	
	function visitT_Mail($e)
	{
		$address = protectmail_url($e->getaddress());
		$alias = protectmail_html($e->getaddress());
		return "<a href=\"mailto:$address\">$alias</a>";
	}
	
	
	function visitT_BlacketName($e)
	{
		$pagename = $e->getpagename();
		$alias = $e->getalias() != '' ? $e->getalias() : $e->getpagename();
		if(mb_ereg('^' . EXP_URL . '$', $pagename)){
			$alias = htmlspecialchars($alias);
			return "<a href=\"$pagename\">$alias</a>";
		}
		else if(mb_ereg('^' . EXP_MAIL . '$', $pagename)){
			$address = protectmail_url($pagename);
			$alias = protectmail_html($alias);
			return "<a href=\"mailto:$address\">$alias</a>";
		}
		else if(mb_ereg('^(.+?):(.+)$', $pagename, $m) && !Wiki::getinstance()->ispage($pagename)){
			return makeinterwikilink($m[1], $m[2], $alias);
		}
		else{
			$fullname = resolvepath($pagename, $e->getcurrentpage());
			return makelink($fullname, $alias);
		}
	}
	
	
	function visitT_InlinePlugin($e)
	{
		try{
			$plugin = Plugin::getPlugin($e->getpluginname());
			return $plugin->do_inline($e->getpagename(), $e->getparam1(), $e->getparam2());
		}
		catch(PluginException $exc){
			return '<span class="warning">' . htmlspecialchars($exc->message()) . '</span>';
		}
	}
	
	
	function visitT_Footnote($e)
	{
		$ret = array();
		$footnote = Footnote::getinstance();
		$num = $footnote->reserve();
		return $footnote->setnote($e->getelem()->accept($this), $num);
	}
	
	
	function visitT_Strong($e)
	{
		$str = htmlspecialchars($e->getstr());
		$level = $e->getlevel();
		return $level == 1 ? "<em>$str</em>" : "<strong>$str</strong>";
	}
	
	
	function visitT_Text($e)
	{
		$ret = array();
		foreach($e->getelements() as $elem){
			$ret[] = $elem->accept($this);
		}
		return join('', $ret);
	}
	
	
	function visitT_String($e)
	{
		$exp = '&amp;(#\d{2,4}|#x[0-9a-fA-F]{2,3}|' . CHARACTER_ENTITY_REFERENCES . ');';
		
		$str = htmlspecialchars($e->getstring());
		return mb_ereg_replace($exp, '&\1;', $str);
	}
	
	
	function visitT_AutoLink($e)
	{
		return makelink($e->getpagename(), $e->getalias());
	}
	
	
	function visitT_FuzzyLink($e)
	{
		return '<a class="fuzzylink" href="' . SCRIPTPATH . '?cmd=fuzzylink&amp;key=' . rawurldecode($e->getkey()) . '" title="あいまいリンク">' . htmlspecialchars($e->getkey()) . '</a>';
	}
}



/**
 * 脚注を管理する。シングルトン。
 */
class Footnote
{
	protected $note = array();
	
	
	public static function getinstance()
	{
		static $ins = null;
		if($ins == null){
			$ins = new Footnote;
		}
		return $ins;
	}
	
	
	protected function __construct()
	{
		//do nothing.
	}
	
	
	/**
	 * 番号だけ予約する。
	 * 
	 * @return	int	番号
	 * */
	function reserve()
	{
		$this->note[] = '';
		return count($this->note);
	}
	
	
	/**
	 * 脚注を設定する。
	 * 
	 * @param	string	$html	追加するhtml形式文字列。
	 * @param	int	$num	予約しておいた番号
	 * @return	string	アンカー
	 */
	function setnote($html, $num)
	{
		$this->note[$num-1] = $html;
		$note = strip_tags($html);
		$str  = '<span class="hidden">(</span>';
		$str .= "<a class=\"footnote\" href=\"#footnote_{$num}\" id=\"footnote_{$num}_r\" title=\"{$note}\">*$num</a>";
		$str .= '<span class="hidden">)</span>';
		return $str;
	}
	
	
	/**
	 * 脚注を追加する。
	 * 
	 * もう使われなくなった関数だが、互換性のために残しておく。
	 * 
	 * @param	string	$html	追加するhtml形式文字列。
	 * @return	string	アンカー
	 */
	function addnote($html)
	{
		return $this->setnote($html, $this->reserve());
	}
	
	
	/**
	 * 脚注を取得する。
	 * 
	 * @return	string	html形式の文字列。
	 */
	function getnote()
	{
		if($this->note == array()){
			return '';
		}
		$smarty = new MySmarty(TPL_DIR);
		$smarty->assign('note', $this->note);
		return $smarty->fetch('footnote.tpl.htm');
	}
}



/**
 * 見出しアンカーのidを作るクラス。
 */
class SectionAnchorMaker
{
	protected $section = array('');
	
	
	/**
	 * アンカーのidを取得する。
	 * 
	 * @param	int	$level	見出しレベル
	 * @param	string	$str	見出し（wikiのソース）
	 */
	function makeid($level, $str)
	{
		for($i = 1; $i < $level; $i++){
			if(!isset($this->section[$i])){
				$this->section[$i] = '';
			}
		}
		
		$this->section[$i] = $str;
		return 'id' . substr(md5(join("\0", array_slice($this->section, 1, $level))), 0, 8);
	}
}

?>