package org.commcare.modern.engine.cases.query;

import org.commcare.cases.model.Case;
import org.commcare.cases.query.*;
import org.commcare.cases.util.QueryUtils;
import org.commcare.modern.engine.cases.CaseIndexTable;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.trace.EvaluationTrace;
import org.javarosa.xpath.expr.XPathExpression;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

/**
 * This handler detects contexts in which the query is likely to trip many index lookups and will
 * strategically perform a cache of relevant indexes
 *
 * Created by ctsims on 1/25/2017.
 */

public class CaseIndexPrefetchHandler implements QueryHandler<IndexedValueLookup> {

    /**
     * This should be roughly the point at which 1 query of N items in the db will be faster
     * than N queries of 1 item, even if only one item ends up being used.
     */
    private static final int BULK_LOAD_THRESHOLD = 500;

    private final CaseIndexTable mCaseIndexTable;

    public static final class Cache implements QueryCache {
        Vector<String> currentlyFetchedIndexKeys = new Vector<>();
        private HashMap<String, Vector<Integer>> indexCache = new HashMap<>();
    }

    public CaseIndexPrefetchHandler(CaseIndexTable caseIndexTable) {
        this.mCaseIndexTable = caseIndexTable;
        //TODO: Profile table by each type of index for size to identify threshold changes.
    }

    @Override
    public int getExpectedRuntime() {
        return 1;
    }

    @Override
    public IndexedValueLookup profileHandledQuerySet(Vector<PredicateProfile> profiles) {
        IndexedValueLookup ret = QueryUtils.getFirstKeyIndexedValue(profiles);
        if(ret != null){
            if (ret.getKey().startsWith(Case.INDEX_CASE_INDEX_PRE)) {
                return ret;
            }
        }
        return null;

    }

    @Override
    public List<Integer> loadProfileMatches(IndexedValueLookup querySet, QueryContext context) {
        String indexName = querySet.getKey().substring(Case.INDEX_CASE_INDEX_PRE.length());
        String value = (String)querySet.value;

        Cache cache = context.getQueryCache(Cache.class);
        if(!cache.currentlyFetchedIndexKeys.contains(indexName)) {
            if(context.getScope() < BULK_LOAD_THRESHOLD) {
                return null;
            }

            EvaluationTrace trace = new EvaluationTrace("Index Bulk Prefetch [" + indexName + "]");
            int indexFetchSize = mCaseIndexTable.loadIntoIndexTable(cache.indexCache, indexName);
            trace.setOutcome("Loaded: " + indexFetchSize);
            context.reportTrace(trace);
            cache.currentlyFetchedIndexKeys.add(indexName);
        }
        String cacheKey = indexName + "|" + value;
        return cache.indexCache.get(cacheKey);
    }

    @Override
    public void updateProfiles(IndexedValueLookup querySet, Vector<PredicateProfile> profiles) {
        profiles.remove(querySet);
    }

    @Override
    public Collection<PredicateProfile> collectPredicateProfiles(Vector<XPathExpression> predicates, QueryContext context, EvaluationContext evaluationContext) {
        return null;
    }
}

@@ -112,17 +112,220 @@
 s.query.
-*
+IndexedValueLookup;%0Aimport org.commcare.cases.query.PredicateProfile;%0Aimport org.commcare.cases.query.QueryCache;%0Aimport org.commcare.cases.query.QueryContext;%0Aimport org.commcare.cases.query.QueryHandler
 ;%0Aimport

Fix auto import