/* $Id: ExternalSearchEngineController.java 434 2011-10-18 14:28:15Z shayashi $ */
package smart_gs.image_search;

import image_search.Query;

import java.awt.Rectangle;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.lang.InterruptedException;
import smart_gs.logical.Spread;
import smart_gs.util.CanonicalPathString;
import smart_gs.image_search.logical.TextType;

import jp.ac.hokudai.meme.core_smart_gs.searcher.FoundRect;
import jp.ac.hokudai.meme.core_smart_gs.searcher.InputStreamThread;
import jp.ac.hokudai.meme.core_smart_gs.searcher.PagedPolygon;
import jp.ac.hokudai.meme.core_smart_gs.searcher.PagedRect;
import jp.ac.hokudai.meme.core_smart_gs.searcher.PolygonParser;
import jp.ac.hokudai.meme.core_smart_gs.searcher.ResultRects;

public class ExternalSearchEngineController {
	private File assignListFile;
	private Process p;
	private InputStreamThread it,et;
	private BufferedWriter bw;
	private ResultRects resultRects = new ResultRects();
	private ArrayList<String> resultStrings = new ArrayList<String>();
	
	static final String COM_ASSIGN = "assign";
	static final String COM_SEARCH = "search";
	static final String COM_EXIT = "exit";
	static final String PREFIX_P0 = "p0";
	static final String PREFIX_X = "x";
	static final String PREFIX_Y = "y";
	static final String REGEXP_RECORD = "r(\\d+)d(\\d+\\.?\\d+)(.*)";
	static final String MSG_OK = "OK";
	static final String RECORD_EOD = "EOD";
	static final String ERRMSG_PARSE = "Internal Error: parse error";
	
	public ResultRects search(Query query, List<Spread> spreads) {
		assignListFile = makeAssignListFile(spreads);
		startExternalSearchEngine();
		execAssign(assignListFile);
		execSearch(query, 1, 100);
		execExit();
		stopExternalSearchEngine();
		return getResultRectsFromStrings(resultStrings);
	}
	
	public File makeAssignListFile(List<Spread> spreads){
		File file = new File(smart_gs.SmartGS.getImageSearchAssignlistfilePathString());
		try {
			HashMap<String, String> dscLocation = new HashMap<String, String>();
			
			file.createNewFile();
	        FileOutputStream fos = new FileOutputStream(smart_gs.SmartGS.getImageSearchAssignlistfilePathString());
	        OutputStreamWriter osw = new OutputStreamWriter(fos);
	        BufferedWriter bw = new BufferedWriter(osw);
			for(int i = 0; i < spreads.size(); i++){
				if(spreads.get(i).getDscFile() == null){
					continue;
				}

				bw.write(CanonicalPathString.get(spreads.get(i).getFile()));
				bw.newLine();
				
				String bmp = spreads.get(i).getFile().getParent();
				String dsc = spreads.get(i).getDscFile().getParent();
				
				dscLocation.put(bmp, dsc);
			}
			bw.close();
			osw.close();
			fos.close();
			
			for(Iterator<String> i = dscLocation.keySet().iterator(); i.hasNext(); ){
				String key = i.next();
				File f = new File(key+"/DscLocation");
		        FileOutputStream fosd = new FileOutputStream(f);
		        OutputStreamWriter oswd = new OutputStreamWriter(fosd);
		        BufferedWriter bwd = new BufferedWriter(oswd);
		        bwd.write(dscLocation.get(key));
		        bwd.close();
				oswd.close();
				fosd.close();
				
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return file;
	}
	
	public void startExternalSearchEngine(){
		try {
			p = Runtime.getRuntime().exec(smart_gs.SmartGS.getImageSearchExefilePathString());
			// DscSearchからの標準出力を受け付けるスレッド
			it = new InputStreamThread(p.getInputStream(), InputStreamThread.TYPE_STDOUT, this);
			it.start();
			// DscSearchからの標準エラー出力を受け付けるスレッド
			et = new InputStreamThread(p.getErrorStream(), InputStreamThread.TYPE_STDERR, this);
			et.start();
	       	// DscSearchの標準入力へ書き込むBufferedWriter       	
	        bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public void execAssign(File file) {
		try {
			bw.write(COM_ASSIGN + " " + CanonicalPathString.get(file));
			System.out.println(COM_ASSIGN + " " + CanonicalPathString.get(file));
			bw.newLine();
			bw.flush();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	public void execSearch(Query query, int first, int count) {
		String queryString = getStringFromRectangle(query.getRectangle());
		try {
			bw.write(COM_SEARCH + " " + queryString + " " + first + " " + count);
			System.out.println(COM_SEARCH + " " + queryString + " " + first + " " + count);
			bw.newLine();
			bw.flush();		
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	public void execExit() {
		try {
			bw.write(COM_EXIT);
			bw.newLine();
			bw.flush();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	public void stopExternalSearchEngine(){
		try {
			it.join();
			et.join();
	        p.getInputStream().close();
	        p.getOutputStream().close();
	        p.getErrorStream().close();
			p.destroy();
			assignListFile.delete();
		} catch (IOException e) {
			throw new RuntimeException(e);
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}		
	}

	public void addSearchResult(String str) {
		if(str.equals(MSG_OK)) {
			return;
		}
		resultStrings.add(str);
	}
	
	public String getStringFromRectangle(Rectangle rect) {
		StringBuffer queryStringBuffer = new StringBuffer();
		queryStringBuffer.append(PREFIX_P0);
		queryStringBuffer.append(PREFIX_X);
		queryStringBuffer.append((int)rect.getX());
		queryStringBuffer.append(PREFIX_Y);
		queryStringBuffer.append((int)rect.getY());
		if (TextType.isHorizontal()) {
			queryStringBuffer.append(PREFIX_X);
			queryStringBuffer.append((int)rect.getX() + (int)rect.getWidth());
			queryStringBuffer.append(PREFIX_Y);
			queryStringBuffer.append((int)rect.getY());
		} else {
			queryStringBuffer.append(PREFIX_X);
			queryStringBuffer.append((int)rect.getX());
			queryStringBuffer.append(PREFIX_Y);
			queryStringBuffer.append((int)rect.getY() + (int)rect.getHeight());
		}
		return queryStringBuffer.toString();
	}

	public ResultRects getResultRectsFromStrings(ArrayList<String> strs) {
        for (Iterator<String> iterator = strs.iterator(); iterator.hasNext();) {
        	String recordString = (String) iterator.next();
			if(recordString.equals(RECORD_EOD)) {
				break;
			}
			Pattern pat = Pattern.compile(REGEXP_RECORD);
			Matcher mat = pat.matcher(recordString);
			if (mat.find()) {
			} else {
				throw new RuntimeException(new ParseException(ERRMSG_PARSE, 0));
			}
			PolygonParser parser = new PolygonParser(mat.group(3));
			try {
				List<PagedPolygon> polygon = parser.getPolygon();
				PagedRect[] pagedRects = new PagedRect[polygon.size()];
				int idx = 0;
				for (Iterator<PagedPolygon> ite_p= polygon.iterator(); ite_p.hasNext();idx++) {
					PagedPolygon pagedPolygon = (PagedPolygon) ite_p.next();
					pagedRects[idx] = new PagedRect(pagedPolygon.getPageNum(), pagedPolygon.getPolygon().getBounds(), -1, -1);
				}
				FoundRect foundRect = new FoundRect(pagedRects, Double.parseDouble(mat.group(2)));
				resultRects.addResult(foundRect);
			} catch (ParseException e) {
				throw new RuntimeException(e);
			}			
        }
        return resultRects;
	}
}
