package de.danielbasedow.prospecter.core.query;

import aima.core.logic.propositional.parsing.ast.Connective;
import aima.core.logic.propositional.parsing.ast.Sentence;
import aima.core.logic.propositional.visitors.ConvertToCNF;
import de.danielbasedow.prospecter.core.MatchCondition;
import de.danielbasedow.prospecter.core.Token;
import de.danielbasedow.prospecter.core.query.build.*;

import java.util.*;

/**
 * represents a Query (a tuple of queryId, bitmask and a list of conditions)
 */
public class Query {
    protected final int queryId;
    protected final int bits;
    protected final ClauseNode clauseNode;
    protected final Map<Condition, Long> postings = new HashMap<Condition, Long>();

    public int getQueryId() {
        return queryId;
    }

    public Query(int queryId, ClauseNode clauseNode) {
        this.clauseNode = clauseNode;
        this.queryId = queryId;

        Sentence cnf = getCNF(clauseNode);

        int tmpBits = 0;
        for (int bit = 0; bit < cnf.getNumberSimplerSentences(); bit++) {
            tmpBits++;
            Sentence disjunction = cnf.getSimplerSentence(bit);
            for (int p = 0; p < disjunction.getNumberSimplerSentences(); p++) {
                Sentence sentence = disjunction.getSimplerSentence(p);
                boolean isNegativeCondition = false;
                if (sentence.isNotSentence()) {
                    sentence = sentence.getSimplerSentence(0);
                    isNegativeCondition = true;
                }
                Condition condition = ((PropositionSymbol) sentence).getCondition();
                if (condition.getToken().getCondition() == MatchCondition.IN) {
                    //If this is an IN query we're dealing with a Token containing a List<Token>
                    Object t = condition.getToken().getToken();
                    if (t instanceof List) {
                        for (Token token : (List<Token>) t) {
                            Condition tmpCondition = new Condition(condition.getFieldName(), token, isNegativeCondition);
                            postings.put(tmpCondition, QueryPosting.pack(queryId, bit));
                        }
                    }
                } else {
                    condition.setNot(isNegativeCondition);
                    postings.put(condition, QueryPosting.pack(queryId, bit));
                }
            }
        }
        bits = tmpBits;
    }

    public static Sentence getCNF(Sentence sentence) {
        Conjunction conjunction = new Conjunction();
        flatten(conjunction, ConvertToCNF.convert(sentence));
        return conjunction;
    }

    public static Sentence getCNF(ClauseNode clauseNode) {
        return getCNF(PropositionalSentenceMapper.map(clauseNode));
    }

    public static void flatten(Conjunction conjunctionCollector, Sentence sentence) {
        if (sentence.getNumberSimplerSentences() > 0 && sentence.getConnective() == Connective.AND) {
            for (int i = 0; i < sentence.getNumberSimplerSentences(); i++) {
                flatten(conjunctionCollector, sentence.getSimplerSentence(i));
            }
        } else {
            Disjunction disjunctionCollector = new Disjunction();
            flatten(disjunctionCollector, sentence);
            conjunctionCollector.add(disjunctionCollector);
        }
    }

    public static void flatten(Disjunction disjunctionCollector, Sentence sentence) {
        if (sentence.getNumberSimplerSentences() > 0 && sentence.getConnective() == Connective.OR) {
            for (int i = 0; i < sentence.getNumberSimplerSentences(); i++) {
                flatten(disjunctionCollector, sentence.getSimplerSentence(i));
            }
        } else {
            disjunctionCollector.add(sentence);
        }
    }

    /**
     * Get QueryPostings for every Condition
     *
     * @return map of Condition -> QueryPosting
     */
    public Map<Condition, Long> getPostings() {
        return postings;
    }

    public ClauseNode getClauses() {
        return clauseNode;
    }

    public int getBits() {
        return bits;
    }
}

@@ -2297,63 +2297,338 @@
     
+long 
 posting
-s.put(condition, QueryPosting.pack(queryId, bit)
+ = QueryPosting.pack(queryId, bit);%0A                    if (isNegativeCondition && postings.containsValue(posting)) %7B%0A                        throw new InvalidQueryException(%22The query is too complex. More than one negation in a disjunction!%22);%0A                    %7D%0A                    postings.put(condition, posting
 );%0A 

throw invalid query exception if there is more than one negation in a disjunction (at least for now)