package com.wiki.standalone.moxkiriya.parser.blockparser;

import java.io.BufferedReader;
import java.util.ArrayList;

import com.wiki.standalone.moxkiriya.WikiEngine;

public class WikiBodyBlockParser {
	/** body要素開始タグ 文字列 */
	private final String START_TAG = "<body>";
	
	/** body要素終了タグ 文字列 */
	private final String END_TAG = "</body>";

	/** Page titleのparse結果 */
	private String titleHtml_;

	/** BlockParserクリエイター */
	WikiBlockParserCreator blockParserCreator_;

	/** Pageの先頭行 */
	private String firstLine_ = null;

	/**
	 * Wikiマークアップで許可するHTML要素のリスト
	 */
	private static final ArrayList<String> acceptElementList_ = new ArrayList<String>() {
		private static final long serialVersionUID = 1L;
		{ add(WikiCodeBlockParser.NOTATION_REGEX_LINEHEAD); }
		{ add(WikiCodeBlockParser.NOTATION_REGEX_LINETAIL);}
		{ add(WikiPreBlockParser.NOTATION_REGEX_LINEHEAD); }
		{ add(WikiPreBlockParser.NOTATION_REGEX_LINETAIL);}
	};

	/** プリプロセッサ状態enum　*/
	enum PreprocessStatus {
		INIT,
		OPEN,
		CLOSE;
	};

	/** parse状態enum　*/
	enum ParseStatus {
		INIT,
		OPEN,
		CLOSE;
	};

	/**
	 * コンストラクタ
	 * @param wikiRepository
	 */
	public WikiBodyBlockParser(WikiEngine wikiEngine) {
		blockParserCreator_ = new WikiBlockParserCreator(wikiEngine);
	}

	/**
	 * Pageタイトルparse処理
	 * @param title
	 */
	public void parsePageTitle(String title) {
		titleHtml_ = "<h1>" + title + "</h1>\n";
	} 
	
	public String parse(BufferedReader reader) throws Exception {
		StringBuffer buf = new StringBuffer();

		startElementProcess(buf);
		buf.append(titleHtml_);
		
		String bodyBlock = parseCore(reader);

		buf.append(blockParserCreator_.getTOC(firstLine_));
		buf.append(bodyBlock);
		endElementProcess(buf);
		
		return buf.toString();
	}

	/**
	 * Wikiマークアップで許可するHTML要素以外を無効化する
	 * @param line
	 * @return
	 */
	private String parsePreprocess(String line) {
		StringBuffer buf           = new StringBuffer("");
		StringBuffer tagBuf        = null;
		char[]       lineCharArray = line.toCharArray();

		PreprocessStatus status    = PreprocessStatus.INIT;
		for(int count = 0; count < lineCharArray.length; count++) {
			if(status == PreprocessStatus.INIT) {
				if(lineCharArray[count] == '<') {
					status = PreprocessStatus.OPEN;
					tagBuf = new StringBuffer();
					tagBuf.append(lineCharArray[count]);
				}
				else {
					buf.append(lineCharArray[count]);
				}
			}
			else if(status == PreprocessStatus.OPEN) {
				tagBuf.append(lineCharArray[count]);
				if(lineCharArray[count] == '>') {					
					String tag = tagBuf.toString();
					for(String accept: acceptElementList_) {
						if(tag.toLowerCase().matches(accept)) {
							buf.append(tag);
							tagBuf = null;
						}
					}

					if(tagBuf != null) {
						/*
						 * acceptリストにないタグパターンの場合
						 */
						buf.append(tag.replaceAll("<", "&lt;")
								.replaceAll(">", "&gt;"));
						tagBuf = null;
					}
					status = PreprocessStatus.INIT;
				}
			}
			else {
				buf.append(lineCharArray[count]);
			}
		}
		
		if(status == PreprocessStatus.OPEN) {
			String tag = tagBuf.toString();
			buf.append(tag.replaceAll("<", "&lt;"));
		}

		return buf.toString();
	}
	
	/**
	 * parseコア処理
	 * @param reader
	 * @return htmlコンテンツ文字列
	 * @throws Exception
	 */
	private String parseCore(BufferedReader reader) throws Exception {
		StringBuffer buf    = new StringBuffer("");
		StringBuffer paragraphBuf = null;
		String       line   = null;
		ParseStatus  status = ParseStatus.INIT;

		WikiBlockParser        parser             = null;
	
		while((line = reader.readLine()) != null) {
			if(firstLine_ == null) {
				firstLine_ = line;
				if(firstLine_.equals("{{#TOC}}") == true) {
					continue;
				} 
			}
			
			/*
			 * XSS殺し
			 */
			String processingline = parsePreprocess(line);
			
			do {
				if(status != ParseStatus.OPEN) {
					status       = ParseStatus.OPEN;
					paragraphBuf = new StringBuffer();
				}
	
				if(parser == null) {
					parser         = blockParserCreator_.create(processingline);
					processingline = parser.startElementProcess(processingline, paragraphBuf);
				}

				processingline = parser.parse(processingline, paragraphBuf);
	
				if(parser.isBlockEnd() == true) {
					processingline = parser.endElementProcess(processingline, paragraphBuf);
					parser = null;
					
					if(line.isEmpty() == true) {
						buf.append("<p>\n");
						buf.append(paragraphBuf + "\n");
						buf.append("</p>\n");

						status       = ParseStatus.CLOSE;
						paragraphBuf = null;						
					}
				}
			} while(processingline.isEmpty() != true);			
		}

		if(status == ParseStatus.OPEN) {
			if(parser != null) {
				parser.endElementProcess(line, paragraphBuf);
			}
			buf.append("<p>\n");
			buf.append(paragraphBuf + "\n");
			buf.append("</p>\n");
			status = ParseStatus.CLOSE;
		}

		return buf.toString();
	}
	
	public void startElementProcess(StringBuffer buf) {
		buf.append(START_TAG + "\n");
	}

	public void endElementProcess(StringBuffer buf) {
		buf.append(END_TAG + "\n");
	}
}
