package de.ids_mannheim.korap.query.spans;

import static de.ids_mannheim.korap.util.KrillByte.byte2int;

import java.io.IOException;
import java.util.BitSet;
import java.util.Map;

import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermContext;
import org.apache.lucene.util.Bits;

import de.ids_mannheim.korap.query.SpanClassFilterQuery;
import de.ids_mannheim.korap.query.SpanClassFilterQuery.ClassOperation;

public class ClassFilteredSpans extends SimpleSpans {

    private BitSet bitset1, bitset2;
    private ClassOperation operation;
    private byte classNum1, classNum2;


    public ClassFilteredSpans (SpanClassFilterQuery query,
                               LeafReaderContext context, Bits acceptDocs,
                               Map<Term, TermContext> termContexts)
            throws IOException {
        super(query, context, acceptDocs, termContexts);
        this.operation = query.getOperation();
        this.classNum1 = query.getClassNum1();
        this.classNum2 = query.getClassNum2();
        hasMoreSpans = firstSpans.next();
    }


    @Override
    public boolean next () throws IOException {
        while (hasMoreSpans) {
            matchPayload.clear();
            bitset1 = null;
            bitset2 = null;
            if (isClassOperationValid()) {
                this.matchStartPosition = firstSpans.start();
                this.matchEndPosition = firstSpans.end();
                this.matchDocNumber = firstSpans.doc();
                this.matchPayload.addAll(firstSpans.getPayload());
                hasMoreSpans = firstSpans.next();
                return true;
            }
            hasMoreSpans = firstSpans.next();
        }
        return false;
    }


    private boolean isClassOperationValid () throws IOException {
        setBitsets();

        int cardinality = Math.max(bitset1.cardinality(),
                bitset2.cardinality());

        bitset1.and(bitset2);
        // System.out.println("cardinality:" + cardinality);
        switch (operation) {
            case DISJOINT:
                if (bitset1.cardinality() == 0)
                    return true;
                break;
            case EQUAL:
                if (cardinality == bitset1.cardinality())
                    return true;
                break;
            case DIFFER:
                if (cardinality == 0 || cardinality != bitset1.cardinality())
                    return true;
                break;
            case INCLUDE:
                if (bitset1.cardinality() == bitset2.cardinality()) {
                    return true;
                }
                break;
            case INTERSECT:
                if (bitset1.cardinality() > 0)
                    return true;
                break;
        }

        return false;
    }


    private void setBitsets () throws IOException {
        BitSet bs = new BitSet();
        int start, end;
        // System.out.println("------------------------");
        for (byte[] payload : firstSpans.getPayload()) {
            /*
            System.err.println(
                               "** " +
                               payload[0] +
                               "|" +
                               byte2int(payload, 1) +
                               "|" +
                               byte2int(payload, 5) +
                               "|" +
                               payload[8] +
                               " **");
            */
            if (payload[0] == 0) {
                start = byte2int(payload, 1) + 1;
                end = byte2int(payload, 5) + 1;
                if (payload[9] == classNum1) {
                    // System.out.println("bitset1 " + start + " " +
                    // end);
                    if (bitset1 == null) {
                        bitset1 = new BitSet();
                        bitset1.set(start, end);
                    }
                    else {
                        bs.set(start, end);
                        bitset1.or(bs);
                    }
                    // System.out.println(bitset1);
                }
                else if (payload[9] == classNum2) {
                    // System.out.println("#bitset2 " + start + " " +
                    // end);
                    if (bitset2 == null) {
                        bitset2 = new BitSet();
                        bitset2.set(start, end);
                    }
                    else {
                        bs.set(start, end);
                        bitset2.or(bs);
                        // System.out.println("OR #2");
                    }
                    // System.out.println(bitset2);
                }
            }
        }

    }


    @Override
    public boolean skipTo (int target) throws IOException {
        // TODO Auto-generated method stub
        return false;
    }


    @Override
    public long cost () {
        // TODO Auto-generated method stub
        return 0;
    }
}
