package com.uas.search.console.support;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.NIOFSDirectory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uas.search.console.util.SearchUtils;

/**
 * 将索引加入缓存，对IndexSearcher进行管理
 * 
 * @author sunyj
 * @since 2016年7月7日 下午8:50:55
 */
public class IndexSearcherManager {

	/**
	 * 用于存放指定表的索引IndexSearcher
	 */
	private Map<String, IndexSearcher> indexSearchers = new ConcurrentHashMap<>();

	/**
	 * 标识指定表的IndexSearcher是否正在重新打开
	 */
	private Map<String, Boolean> reopening = new ConcurrentHashMap<>();

	private static Logger logger = LoggerFactory.getLogger(IndexSearcherManager.class);

	public IndexSearcherManager() {
		List<String> tableNames = SearchUtils.getTableNames();
		for (String tableName : tableNames) {
			reopening.put(tableName, false);
		}
	}

	/**
	 * 对指定表的IndexSearcher进行初始化操作
	 * 
	 * @param tableName
	 *            表名
	 * @return 指定表的FSDirectory
	 */
	private synchronized FSDirectory warm(String tableName) {
		String indexPath = SearchUtils.getIndexPath(tableName);
		FSDirectory directory = null;
		IndexSearcher indexSearcher = indexSearchers.get(tableName);
		// 本来NIOFSDirectory速度更快，但因为jre的bug，在Windows平台上，其性能很差
		try {
			if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
				directory = FSDirectory.open(Paths.get(indexPath));
			} else {
				directory = NIOFSDirectory.open(Paths.get(indexPath));
			}

			File[] files = directory.getDirectory().toFile().listFiles();
			// 不为空的话说明索引已成功加载
			if (indexSearcher == null) {
				// 路径不为空文件夹
				if (files.length != 0) {
					try {
						indexSearcher = new IndexSearcher(DirectoryReader.open(directory));
						indexSearchers.put(tableName, indexSearcher);
					} catch (IOException e) {
						e.printStackTrace();
					}
				} else {
					logger.error("索引文件不存在：" + tableName);
				}
			}
		} catch (IOException e) {
			logger.error("索引路径打开失败：" + indexPath);
			e.printStackTrace();
		}
		return directory;
	}

	/**
	 * 内存中指定表的IndexReader（可能需要）更新，根据版本号确定是否更新
	 * 
	 * @param tableName
	 *            表名
	 */
	public void maybeReopen(String tableName) {
		if (StringUtils.isEmpty(tableName)) {
			return;
		}
		startReopen(tableName);

		try {
			// 每次都要进行初始化处理，是为了防止加载索引时，索引为空，而后来索引不为空时，却不能够正确加载
			FSDirectory directory = warm(tableName);
			IndexSearcher currentSearcher = indexSearchers.get(tableName);
			if (currentSearcher == null) {
				return;
			}
			IndexSearcher searcher = get(tableName);
			Long currentVersion = ((DirectoryReader) searcher.getIndexReader()).getVersion();
			// 版本号不一致（本地索引有更改），更新IndexReader
			try {
				if (DirectoryReader.open(directory).getVersion() != currentVersion) {
					IndexReader newReader = DirectoryReader.open(directory);
					IndexSearcher newSearcher = new IndexSearcher(newReader);
					updateIndexSearcher(tableName, newSearcher);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			release(searcher);
		} finally {
			doneReopen(tableName);
		}

	}

	private synchronized void startReopen(String tableName) {
		while (reopening.get(tableName)) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		reopening.put(tableName, true);
	}

	private synchronized void doneReopen(String tableName) {
		reopening.put(tableName, false);
		notifyAll();
	}

	/**
	 * 得到指定表的IndexSearcher
	 * 
	 * @param tableName
	 *            表名
	 * @return
	 */
	public synchronized IndexSearcher get(String tableName) {
		if (StringUtils.isEmpty(tableName)) {
			return null;
		}
		IndexSearcher currentSearcher = indexSearchers.get(tableName);
		if (currentSearcher != null) {
			currentSearcher.getIndexReader().incRef();
		}
		return currentSearcher;
	}

	/**
	 * 释放indexSearcher
	 * 
	 * @param indexSearcher
	 */
	public synchronized void release(IndexSearcher indexSearcher) {
		if (indexSearcher != null) {
			try {
				// 对indexReader的引用减1，为0时成为垃圾
				indexSearcher.getIndexReader().decRef();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 更新指定表的IndexSearcher
	 * 
	 * @param tableName
	 *            表名
	 * @param newIndexSearcher
	 *            新IndexSearcher
	 */
	private synchronized void updateIndexSearcher(String tableName, IndexSearcher newIndexSearcher) {
		IndexSearcher currentSearcher = indexSearchers.get(tableName);
		release(currentSearcher);
		indexSearchers.put(tableName, newIndexSearcher);
	}
}
