package com.uas.search.console.b2b.support;

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.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.NIOFSDirectory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import com.uas.search.b2b.service.SearchService.Table_name;
import com.uas.search.console.b2b.util.ClassAndTableNameUtils;
import com.uas.search.console.b2b.util.SearchUtils;

/**
 * 对IndexWriter进行管理，防止同时有多个方法对索引进行修改，抛出LockObtainFailedException异常
 * 
 * @author sunyj
 * @since 2016年7月7日 下午8:50:04
 */
public class IndexWriterManager {

	/**
	 * 用于存放指定表的索引IndexWriter
	 */
	private Map<Table_name, IndexWriter> indexWriters = new ConcurrentHashMap<>();

	/**
	 * 标识指定表的索引是否正在使用
	 */
	private Map<Table_name, Boolean> using = new ConcurrentHashMap<>();

	private Logger logger = LoggerFactory.getLogger(getClass());

	public IndexWriterManager() {
		List<Table_name> tableNames = ClassAndTableNameUtils.getTableNames();
		for (Table_name tableName : tableNames) {
			using.put(tableName, false);
		}
	}

	/**
	 * 得到指定表的IndexWriter，用完后需调用release释放IndexWriter
	 * 
	 * @param tableName
	 *            表名
	 * @return IndexWriter
	 * @throws InterruptedException
	 */
	public synchronized IndexWriter get(Table_name tableName) throws InterruptedException {
		if (tableName == null) {
			return null;
		}
		startUsing(tableName);
		IndexWriter indexWriter = indexWriters.get(tableName);
		if (indexWriter == null) {
			// 此处用IK
			IndexWriterConfig config = new IndexWriterConfig(new IKAnalyzer());
			// 获取表对应的索引目录
			String indexPath = SearchUtils.getIndexPath(tableName);
			try {
				FSDirectory directory = null;
				// 本来NIOFSDirectory速度更快，但因为jre的bug，在Windows平台上，其性能很差
				if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
					directory = FSDirectory.open(Paths.get(indexPath));
				} else {
					directory = NIOFSDirectory.open(Paths.get(indexPath));
				}
				indexWriter = new IndexWriter(directory, config);
				indexWriters.put(tableName, indexWriter);
				return indexWriter;
			} catch (IOException e) {
				logger.error("索引路径打开失败：" + indexPath, e);
				return null;
			}
		} else {
			return indexWriter;
		}
	}

	/**
	 * 释放对指定表的indexWriter的使用
	 * 
	 * @param tableName
	 *            表名
	 */
	public synchronized void release(Table_name tableName) {
		if (tableName == null) {
			throw new NullPointerException("参数不能为空：tableName");
		}
		// indexWriter一直保持打开状态，也只存在一个实例，
		// indexWriter用完后只是释放对其的占用，不会进行close
		// TODO close方法有时会卡住（多出现在插入记录之后），暂时还未找到原因
		doneUsing(tableName);
	}

	private synchronized void startUsing(Table_name tableName) throws InterruptedException {
		while (using.get(tableName)) {
			wait();
		}
		using.put(tableName, true);
	}

	private synchronized void doneUsing(Table_name tableName) {
		using.put(tableName, false);
		notifyAll();
	}
}
