/* $Id: TextSearchEngine.java 379 2011-10-03 14:30:29Z shayashi $ */
package smart_gs.text_search;

import java.io.StringReader;

import smart_gs.current_text_search.Position;
import smart_gs.logical.Spread;
import java.util.List;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Iterator;
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.util.StringUtils;
import smart_gs.logical.*;

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).getFirstIDAT();
				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).getThirdIDAT();
				addResults(queryText, document, isCaseSensitive,isRegularExpressions);
				document = spreadsToSearch.get(i).getSecondIDAT();
				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();
		// SMLのソースをWysiWygのプレーンテキストに変換
		String planeText = source2Text(source);
		// isCaseSensitiveの値に従ってqueryText, planeTextを変換
		String ccQuery = convertCase(queryText, isCaseSensitive);
		String ccText = convertCase(planeText, 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(), planeText);
					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, planeText);
					result.setTextToShow(textToShow);
					results.add(result);
					// modify Northgrid 文字列ぶんだけずらす
//					i = indexOfResult + 1;
					i = indexOfResult + ccQuery.length();
					
				}
			}
		}
	}

	private String source2Text(String source) {
		// smlSourceをXMLのInputSourceに変換
		/*
		 * InputSource is = new InputSource(new StringReader(source)); String
		 * planeText = null; try{ // SAXパーサーファクトリを生成 SAXParserFactory spfactory =
		 * SAXParserFactory.newInstance(); // SAXパーサーを生成 SAXParser parser =
		 * spfactory.newSAXParser(); // ハンドラーを生成 TextSearchSax tss = new
		 * TextSearchSax(); // SMLsourceをハンドラーで処理 parser.parse(is, tss);
		 * planeText = tss.getPlaneText(); } catch(Exception e){
		 * e.printStackTrace(); }
		 */
		String planeText = source;
		planeText = StringUtils.replaceString(planeText, "<br>", "\n");
		Pattern pattern = Pattern.compile("<.+?>", Pattern.DOTALL);
		Matcher matcher = pattern.matcher(planeText);
		planeText = matcher.replaceAll("");
		
		// 2009/10/05 HTMLの変換 ADD NorthGrid
		planeText = planeText.replaceAll("&quot;", "\"");
		planeText = planeText.replaceAll("&gt;", "<");
		planeText = planeText.replaceAll("&lt;", ">");
		planeText = planeText.replaceAll("&amp;", "&");
		
		//2008/9/25 North Grid trimは不要になりました
		//planeText = planeText.trim();

		return planeText;
	}

	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;
	}
}
