/*
 * Copyright (C) 2014 Indeed Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the
 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */
 package com.indeed.flamdex.lucene;

import com.indeed.flamdex.fieldcache.UnsortedIntTermDocIterator;
import org.apache.log4j.Logger;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.index.TermEnum;

import java.io.IOException;

/**
 * @author jsgroth
 */
final class LuceneUnsortedIntTermDocIterator implements UnsortedIntTermDocIterator {
    private static final Logger log = Logger.getLogger(LuceneUnsortedIntTermDocIterator.class);

    private final String field;
    private final TermEnum terms;
    private final TermDocs termDocs;

    private long currentTerm;
    private boolean first = true;

    LuceneUnsortedIntTermDocIterator(String field, TermEnum terms, TermDocs termDocs) {
        this.field = field.intern();
        this.terms = terms;
        this.termDocs = termDocs;
    }

    static LuceneUnsortedIntTermDocIterator create(final IndexReader r, final String field) throws IOException {
        final TermEnum terms = r.terms(new Term(field, ""));
        final TermDocs termDocs;
        try {
            termDocs = r.termDocs();
        } catch (IOException e) {
            try {
                terms.close();
            } catch (IOException e1) {
                log.error("error closing TermEnum", e1);
            }
            throw e;
        }
        return new LuceneUnsortedIntTermDocIterator(field, terms, termDocs);
    }

    @Override
    public boolean nextTerm() {
        try {
            return innerNextTerm();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings({"StringEquality"})
    private boolean innerNextTerm() throws IOException {
        // loop instead of calling itself recursively to avoid potential stack overflow
        while (true) {
            if (!first) {
                if (!terms.next()) return false;
            } else {
                first = false;
            }
            final Term term = terms.term();
            if (term == null || term.field() != field) return false;

            try {
                currentTerm = Long.parseLong(term.text());
            } catch (NumberFormatException e) {
                continue;
            }
            
            termDocs.seek(terms);

            return true;
        }
    }

    @Override
    public long term() {
        return currentTerm;
    }

    @Override
    public int nextDocs(int[] docIdBuffer) {
        try {
            return innerNextDocs(docIdBuffer);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private int innerNextDocs(int[] docIdBuffer) throws IOException {
        int i = 0;
        while (i < docIdBuffer.length && termDocs.next()) {
            docIdBuffer[i++] = termDocs.doc();
        }
        return i;
    }

    @Override
    public void close() {
        try {
            terms.close();
        } catch (IOException e) {
            log.error("error closing TermEnum", e);
        }
        try {
            termDocs.close();
        } catch (IOException e) {
            log.error("error closing TermDocs", e);
        }
    }
}
