package cn.com.pc.penguin.core;

import cn.com.pc.penguin.service.PenguinSearchResults;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.document.MapFieldSelector;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.search.*;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by IntelliJ IDEA.
 * User: huangqinghe
 * To change this template use File | Settings | File Templates.
 */
public class PenguinSearcher implements NewDocumentAdded {
    private final static Logger logger = Logger.getLogger(PenguinSearcher.class);
    private volatile IndexSearcher searcher;
    private PenguinSettings settings;
    private ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
    private FieldSelector fieldSelector = new IDFieldSelector();

    public IndexReader getReader() throws Exception {
        return reader;
    }

    public void setReader(IndexReader reader) {
        this.reader = reader;
    }

    public IndexSearcher getSearcher() throws Exception {
        return searcher;
    }

    public void setSearcher(IndexSearcher searcher) {
        this.searcher = searcher;
    }

    private IndexReader reader;


    public PenguinSearcher(PenguinSettings settings) throws Exception {
        super();
        this.settings = settings;
        this.openSearch();
    }

    //refresh IndexReader for real-time
    public void newDocumentAdded(IndexReader r) throws Exception {
        rwlock.writeLock().lock();
        try {
            if (searcher != null) {
                this.searcher.close();
            }
            if (reader != null) {
                this.reader.close();
            }
            this.reader = r;
            this.searcher = new IndexSearcher(reader);
            logger.info("new DocumentAdded : " + this.reader.maxDoc());
        } finally {
            rwlock.writeLock().unlock();
        }
    }

    private void openSearch() throws Exception {
        try {
            this.reader = IndexReader.open(this.settings.directory(), true);
            this.searcher = new IndexSearcher(reader);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    public PenguinSearchResults search(PenguinArgs args) throws Exception {
        long a = System.currentTimeMillis();
        Query query = args.getQuery();
        Filter filter = args.getFilter();
        Sort sort = args.getSort();

        String type = args.getSearchType().toString();
        logger.debug("searchType: " + type);
        logger.debug(query);
        logger.debug(filter);
        logger.debug(sort);

        PenguinSearchResults sr = new PenguinSearchResults();
        rwlock.readLock().lock();
        try {
            TopDocs td;
            if (sort != null) {
                td = searcher.search(query, filter, args.getOffset() + args.getLimit(), sort);
            } else {
                td = searcher.search(query, filter, args.getOffset() + args.getLimit());
            }

            ScoreDoc[] hits = td.scoreDocs;
            int total = td.totalHits;
            sr.setTotal(total);

            FieldSelector fselector = null;
            String valueField = args.getValueField();
            boolean fetchValues = false;
            if (valueField != null && !"".equals(valueField)) {
                String[] fields = new String[]{"id", valueField};
                fselector = new MapFieldSelector(fields);
                fetchValues = true;
            } else {
                fselector = this.fieldSelector;
            }

            for (int i = args.getOffset(); i < hits.length; i++) {
                Document hitDoc = searcher.doc(hits[i].doc, fselector);
                sr.getLists().add(hitDoc.get("id"));

                if (fetchValues) {
                    sr.getValueLists().add(hitDoc.get(valueField));
                }
            }
        } finally {
            rwlock.readLock().unlock();
        }
        long b = System.currentTimeMillis();
        logger.debug("spend : " + (b - a) + "ms");
        return sr;
    }

    public void close() throws IOException {
        if (searcher != null) {
            searcher.close();
        }
    }
}
