package cn.com.pc.penguin.core;

import cn.com.pc.penguin.service.PenguinSearchResults;
import cn.com.pc.penguin.utils.PenguinUtils;
import org.json.JSONObject;

import java.io.*;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 * User: huangqinghe
 * To change this template use File | Settings | File Templates.
 */
public class PenguinSearchHandler {
    private static final Object OUTPUT_MUTEX = new Object();
	private ObjectOutputStream output;
	Map<String, SearchManager> searchManagers = new HashMap<String, SearchManager>();
	private Map<String,Boolean> indexOnFile = new HashMap<String, Boolean>();

	public void setIndexOnFile(Map<String, Boolean> indexOnFile) {
		this.indexOnFile = indexOnFile;
	}

    //项目暂时使用立即commit形式
	public void init() throws Exception {
		try {
			this.rescueIndex();
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		new Thread(new CommitWorker()).start();
	}

	public void index(PenguinDocument penguinDoc) throws Exception {
		this.indexPenguinDocument(penguinDoc);
		//this.savePenguinDocument(penguinDoc);
	}

	private void indexPenguinDocument(PenguinDocument penguinDoc) throws Exception {
		String searchType = penguinDoc.getSearchType().toString();
		try {
			SearchManager sm = getSearchManager(searchType);
			sm.createIndex(penguinDoc.getDocs(), penguinDoc.getKeys());
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

	private synchronized SearchManager getSearchManager(String searchType) throws Exception {
		SearchManager sm = searchManagers.get(searchType);
		if (sm == null) {
			Boolean isOnFile = this.indexOnFile.get(searchType);
			sm = SearchManagerFactory.createSearchManager(searchType,isOnFile);
			searchManagers.put(searchType, sm);
		}
		return sm;
	}



	public PenguinSearchResults search(PenguinArgs args) {
		PenguinSearchResults sr = null;
		String searchType = args.getSearchType().toString();
		try {
			SearchManager sm = getSearchManager(searchType);
			sr = sm.search(args);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return sr;
	}

	private void rescueIndex() throws Exception {
		// rebuild the uncommitted indexes
		long a = System.currentTimeMillis();
		File commitFile = this.getUnCommitedFile();
		if (commitFile.exists()) {
			FileInputStream fis = null;
			BufferedInputStream bis = null;
			ObjectInputStream ois = null;
			try {
				fis = new FileInputStream(commitFile);
				bis = new BufferedInputStream(fis);
				ois = new ObjectInputStream(bis);
				String jpd = (String) ois.readObject();
				PenguinDocument pd = new PenguinDocument(new JSONObject(jpd));
				while (pd != null) {
					System.out.println("rebuild : " + pd.getKeys().size());
					this.indexPenguinDocument(pd);
					jpd = (String) ois.readObject();
					pd = new PenguinDocument(new JSONObject(jpd));
				}
			} catch (EOFException e) {
				System.out.println("end of commitFile");
			} finally {
				if (ois != null) {
					ois.close();
				}
				if (bis != null) {
					bis.close();
				}
				if (fis != null) {
					fis.close();
				}
			}
		}

		long b = System.currentTimeMillis();
		System.out.println("rescue Indes took:" + (b - a) + "ms");
		this.commit();
	}

	private void createNewOutputStream() throws Exception {
		File commitFile = this.getUnCommitedFile();
		commitFile.delete();
		FileOutputStream fos = new FileOutputStream(commitFile, false);
		BufferedOutputStream bfs = new BufferedOutputStream(fos);
		this.output = new ObjectOutputStream(bfs);

	}

	public void savePenguinDocument(PenguinDocument penguinDoc) throws Exception{
		synchronized (OUTPUT_MUTEX) {
            this.output.writeObject(penguinDoc.toJSON());
            this.output.flush();
        }
	}

	private File getUnCommitedFile() throws Exception {
        String catalog = PenguinUtils.getPenguinCatalog();
		System.out.println("catalog dir : " + catalog);
		File dir = new File(catalog);
		File commitFile = new File(catalog + File.separatorChar +  "uncommitedIndex.log");
		if (!dir.exists()) {
			dir.mkdirs();
		}
		return commitFile;
	}

	public void close() throws Exception {
		this.commit();
	}

	public String commit() throws Exception {
		synchronized (OUTPUT_MUTEX) {
			StringBuilder sb = new StringBuilder();
			for (Map.Entry<String, SearchManager> entry : searchManagers.entrySet()) {
				entry.getValue().commit();
				sb.append(entry.getKey());
				sb.append(" has commited <br>");
			}
			//this.createNewOutputStream();
			return sb.toString();
		}
	}

	class CommitWorker implements Runnable {

		public CommitWorker() {
		}

		public void run() {
			while (true) {
				try {
					// commit the indexes per 30 minutes
					Thread.sleep(30 * 60 * 1000);
					System.out.println("CommitWorker commit " + new Date());
					long a = System.currentTimeMillis();
					String message = commit();
					System.out.println(message);
					System.out.println("indexes have commited, spent "
							+ (System.currentTimeMillis() - a) + "ms");
				} catch (Throwable t) {
					t.printStackTrace();
				}
			}
		}
	}
}
