/* $Id: TextSearchEngine.java 899 2014-12-25 03:20:16Z ohura $ */
package smart_gs.text_search;

import java.util.List;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Iterator;

import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import com.hexidec.ekit.component.ExtendedHTMLEditorKit;

import smart_gs.text_search.logical.SearchResult;
import smart_gs.logical.SmartDocument;
import smart_gs.text_search.logical.TextSearchConstants;
import smart_gs.text_search.swingui.TextToShow;
import smart_gs.logical.*;
import sml_editor.swingui.GSHTMLEditorTextPane;

public class TextSearchEngine {
	private static TextSearchEngine singleton = new TextSearchEngine();
	private String queryText;
	private List<Spread> spreadsToSearch;
	private int documentType;
	private List<SearchResult> results;
	private SmartDocument document;
	private boolean isCaseSensitive;
	private boolean isRegularExpressions;

	public TextSearchEngine() {
	}

	public static TextSearchEngine getInstance() {
		return singleton;
	}

	public void setQueryText(String queryText) {
		this.queryText = queryText;
	}

	public void setCaseSensitive(boolean caseSensitive) {
		this.isCaseSensitive = caseSensitive;
	}
	
	public void setRegularExpressions(boolean regularExpressions){
		this.isRegularExpressions = regularExpressions;
	}

	public void setSearchScope(List<Spread> spreadsToSearch) {
		this.spreadsToSearch = spreadsToSearch;
	}

	public void setDocumentType(int documentType) {
		this.documentType = documentType;
	}

	public void execute() {
		this.results = new ArrayList<SearchResult>();
		switch (documentType) {
		case TextSearchConstants.TEXT:
			ArrayList<UsersNote> arrList1 = (ArrayList<UsersNote>) UsersNoteTree
					.getInstance().getUsersNotes();
			Iterator<UsersNote> it1 = arrList1.iterator();
			while (it1.hasNext()) {
				addResults(queryText, (SmartDocument) it1.next(),isCaseSensitive,isRegularExpressions);
			}
			break;
		case TextSearchConstants.FIRSTIDAT:
			for (int i = 0; i < spreadsToSearch.size(); i++) {
				document = spreadsToSearch.get(i).getFirstIDAT();
				addResults(queryText, document, isCaseSensitive,isRegularExpressions);
			}
			break;
		case TextSearchConstants.THIRDIDAT:
			for (int i = 0; i < spreadsToSearch.size(); i++) {
				document = spreadsToSearch.get(i).getThirdIDAT();
				addResults(queryText, document, isCaseSensitive,isRegularExpressions);
			}
			break;
		case TextSearchConstants.SECONDIDAT:
			for (int i = 0; i < spreadsToSearch.size(); i++) {
				document = spreadsToSearch.get(i).getSecondIDAT();
				addResults(queryText, document, isCaseSensitive,isRegularExpressions);
			}
			break;
		case TextSearchConstants.ALLDOCUMENT:
			for (int i = 0; i < spreadsToSearch.size(); i++) {
				document = spreadsToSearch.get(i).getFirstIDAT();
				addResults(queryText, document, isCaseSensitive,isRegularExpressions);
				document = spreadsToSearch.get(i).getSecondIDAT();
				addResults(queryText, document, isCaseSensitive,isRegularExpressions);
				document = spreadsToSearch.get(i).getThirdIDAT();
				addResults(queryText, document, isCaseSensitive,isRegularExpressions);
			}
			ArrayList<UsersNote> arrList2 = (ArrayList<UsersNote>) UsersNoteTree
					.getInstance().getUsersNotes();
			Iterator<UsersNote> it2 = arrList2.iterator();
			while (it2.hasNext()) {
				addResults(queryText, (SmartDocument) it2.next(),isCaseSensitive,isRegularExpressions);
			}
			break;
		}
	}

	private void addResults(String queryText, SmartDocument document,boolean isCaseSensitive,boolean isRegularExpressions) {
		String documentURI = document.getURI();
		// SMLのソース
		String source = document.getSource();
		// Convert HTML source to plain text using GSHTMLEditorTextPane
		GSHTMLEditorTextPane tempTextPane = new GSHTMLEditorTextPane(null, null);
		ExtendedHTMLEditorKit htmlKit = new ExtendedHTMLEditorKit();
		tempTextPane.setEditorKit(htmlKit);
		source = source.replaceAll(" ", "&nbsp;");
		tempTextPane.setText(source);

		queryText = queryText.replace(' ', '\u00a0'); // no-break space
		Document baseDocument = tempTextPane.getDocument();
		String plainText;
		try {
			plainText = baseDocument.getText(0, baseDocument.getLength());
//			System.out.println("debug: TextSearchEngine: plainText: " + plainText);
			// isCaseSensitiveの値に従ってqueryText, planeTextを変換
			String ccQuery = convertCase(queryText, isCaseSensitive);
			String ccText = convertCase(plainText, isCaseSensitive);
			int lengthOfQuery = queryText.length();
			// 検索結果に表示するテキスト
			TextToShow textToShow;
			int indexOfResult;
			int i = 0;
			if (ccText == null) {
				return;
			}
			// 正規表現処理
			if(isRegularExpressions){
				try{
					Pattern p = Pattern.compile(ccQuery);
					Matcher m = p.matcher(ccText);
					while(m.find()){
						indexOfResult = m.start();
						String resultStr = m.group();
						SearchResult result = new SearchResult();
						result.setResult(resultStr, documentURI, indexOfResult);
						textToShow = textToShow(indexOfResult, resultStr.length(), plainText);
						result.setTextToShow(textToShow);
						results.add(result);
					}
				}catch(Exception e){
					e.printStackTrace();
				}
			}else{
				if (!(ccText.indexOf(ccQuery) == -1)) {
					while (true) {
						indexOfResult = ccText.indexOf(ccQuery, i);
						if (indexOfResult == -1) {
							i = 0;
							break;
						}
						SearchResult result = new SearchResult();
						result.setResult(queryText, documentURI,indexOfResult);
						textToShow = textToShow(indexOfResult, lengthOfQuery, plainText);
						result.setTextToShow(textToShow);
						results.add(result);
						i = indexOfResult + ccQuery.length();
					}
				}
			}
		} catch (BadLocationException e1) {
			e1.printStackTrace();
		}
	}

	private String convertCase(String source, boolean isCaseSensitive) {
		if (source == null) {
			return null;
		}
		if (isCaseSensitive) {
			return source;
		}
		return source.toLowerCase();
	}

	/*
	 * WysiWygのテキスト全体から
	 * 
	 */
	private TextToShow textToShow(int index, int lengthOfQuery, String text) {
		String textToShow = text.substring(index, index + lengthOfQuery);
		String pre;
		String query;
		String fol;
		// クエリ前後の表示する文字数
		final int num = 10;
		// テキストの長さ
		int length = text.length();

		if (index + 1 <= num) {
			if (index == 0) {
				pre = "";
			} else {
				pre = text.substring(0, index);
			}
		} else {
			pre = "..." + text.substring(index - num, index);
		}
		query = text.substring(index, index + lengthOfQuery);
		if (length - index - lengthOfQuery <= num) {
			if (length - index - lengthOfQuery == 0) {
				fol = "";
			} else {
				fol = text.substring(index + lengthOfQuery);
			}
		} else {
			fol = text.substring(index + lengthOfQuery, index + lengthOfQuery
					+ num)
					+ "...";
		}
		textToShow = pre + query + fol;
		return new TextToShow(textToShow, pre.length(), lengthOfQuery);
	}

	public List<SearchResult> getResults() {
		return this.results;
	}
}
