package org.opencds.cqf.ruler.cdshooks.hooks;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import org.cqframework.cql.elm.execution.ListTypeSpecifier;
import org.cqframework.cql.elm.execution.ParameterDef;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.opencds.cqf.cql.engine.data.CompositeDataProvider;
import org.opencds.cqf.cql.engine.execution.Context;
import org.opencds.cqf.cql.engine.fhir.retrieve.RestFhirRetrieveProvider;
import org.opencds.cqf.cql.engine.fhir.searchparam.SearchParameterResolver;
import org.opencds.cqf.cql.engine.model.ModelResolver;
import org.opencds.cqf.cql.engine.retrieve.TerminologyAwareRetrieveProvider;
import org.opencds.cqf.cql.evaluator.engine.retrieve.PriorityRetrieveProvider;
import org.opencds.cqf.ruler.cdshooks.evaluation.EvaluationContext;
import org.opencds.cqf.ruler.cdshooks.providers.PrefetchDataProviderDstu2;
import org.opencds.cqf.ruler.cdshooks.providers.PrefetchDataProviderR4;
import org.opencds.cqf.ruler.cdshooks.providers.PrefetchDataProviderStu3;
import org.opencds.cqf.ruler.cdshooks.response.CdsCard;

import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;


public abstract class BaseHookEvaluator<P extends IBaseResource> {

    protected ModelResolver modelResolver;

    public BaseHookEvaluator(ModelResolver modelResolver) {
        this.modelResolver = modelResolver;
    }

    public List<CdsCard> evaluate(EvaluationContext<P> context) throws IOException {

        // resolve context resources parameter
        // TODO - this will need some work for libraries with multiple parameters
        if (context.getLibrary().getParameters() != null && !(context.getHook() instanceof PatientViewHook)) {
            for (ParameterDef params : context.getLibrary().getParameters().getDef()) {
                if (params.getParameterTypeSpecifier() instanceof ListTypeSpecifier) {
                    context.getContext().setParameter(null, params.getName(), context.getContextResources());
                }
            }
        }

        // Remote data retriever
        RestFhirRetrieveProvider remoteRetriever = new RestFhirRetrieveProvider(
                new SearchParameterResolver(context.getFhirContext()), context.getHookFhirClient());

        remoteRetriever.setTerminologyProvider(context.getContext().resolveTerminologyProvider());
        remoteRetriever.setExpandValueSets(context.getProviderConfiguration().getExpandValueSets());
        remoteRetriever.setMaxCodesPerQuery(context.getProviderConfiguration().getMaxCodesPerQuery());
        remoteRetriever.setSearchStyle(context.getProviderConfiguration().getSearchStyle());

        TerminologyAwareRetrieveProvider prefetchRetriever;
        if (context.getFhirVersion() == FhirVersionEnum.DSTU3) {
            prefetchRetriever = new PrefetchDataProviderStu3(context.getPrefetchResources(), modelResolver);
        } else if (context.getFhirVersion() == FhirVersionEnum.DSTU2) {
            prefetchRetriever = new PrefetchDataProviderDstu2(context.getPrefetchResources(), modelResolver);
        }
        else {
            prefetchRetriever = new PrefetchDataProviderR4(context.getPrefetchResources(), modelResolver);
        }

        // TODO: Get the "system" terminology provider.
        prefetchRetriever.setTerminologyProvider(context.getContext().resolveTerminologyProvider());

        PriorityRetrieveProvider priorityRetrieveProvider = new PriorityRetrieveProvider(Arrays.asList(prefetchRetriever, remoteRetriever));
        context.getContext().registerDataProvider("http://hl7.org/fhir",
                new CompositeDataProvider(this.modelResolver, priorityRetrieveProvider));
        context.getContext().registerTerminologyProvider(prefetchRetriever.getTerminologyProvider());

        return evaluateCdsHooksPlanDefinition(context.getContext(), context.getPlanDefinition(),
                context.getHook().getRequest().getContext().getPatientId(), context.getSystemFhirClient());
    }

    public abstract List<CdsCard> evaluateCdsHooksPlanDefinition(Context context, P planDefinition, String patientId,
            IGenericClient applyClient);
}

@@ -393,32 +393,175 @@
 cution.Context;%0A
+import org.opencds.cqf.cql.engine.fhir.retrieve.Dstu3FhirQueryGenerator;%0Aimport org.opencds.cqf.cql.engine.fhir.retrieve.R4FhirQueryGenerator;%0A
 import org.openc
@@ -1465,21 +1465,24 @@
 ;%0A%0A    p
-ublic
+rotected
  BaseHoo
@@ -2240,32 +2240,92 @@
     
-// Remote data retriever
+SearchParameterResolver srp = new SearchParameterResolver(context.getFhirContext());
 %0A   
@@ -2405,79 +2405,37 @@
 der(
-%0A                new SearchParameterResolver(context.getFhirContext()),
+srp, modelResolver,%0A         
  con
@@ -2764,33 +2764,32 @@
 uery());%0A       
- 
 remoteRetriever.
@@ -2852,24 +2852,80 @@
 rchStyle());
+%0A       remoteRetriever.setModelResolver(modelResolver);
 %0A%0A        Te
@@ -3140,32 +3140,198 @@
 modelResolver);%0A
+            remoteRetriever.setFhirQueryGenerator(%0A              new Dstu3FhirQueryGenerator(srp, context.getContext().resolveTerminologyProvider(), modelResolver));%0A
         %7D else i
@@ -3488,32 +3488,77 @@
 modelResolver);%0A
+            // TODO: We need a dstu2 version%0A
         %7D%0A      
@@ -3665,32 +3665,195 @@
 modelResolver);%0A
+            remoteRetriever.setFhirQueryGenerator(%0A              new R4FhirQueryGenerator(srp, context.getContext().resolveTerminologyProvider(), modelResolver));%0A
         %7D%0A%0A     

Fix missing FhirQueryGenerator (#569)