package it.unibz.inf.ontop.spec.mapping.impl;

import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import it.unibz.inf.ontop.injection.OntopModelSettings;
import it.unibz.inf.ontop.spec.mapping.Mapping;
import it.unibz.inf.ontop.spec.mapping.MappingMetadata;
import it.unibz.inf.ontop.model.atom.AtomPredicate;
import it.unibz.inf.ontop.iq.node.ConstructionNode;
import it.unibz.inf.ontop.iq.IntermediateQuery;
import it.unibz.inf.ontop.iq.tools.ExecutorRegistry;

import java.util.Optional;


public class MappingImpl implements Mapping {

    private final MappingMetadata metadata;
    private final ImmutableMap<AtomPredicate, IntermediateQuery> definitions;
    /**
     * TODO: remove it when the conversion to Datalog will not be needed anymore
     */
    private final ExecutorRegistry executorRegistry;

    @AssistedInject
    private MappingImpl(@Assisted MappingMetadata metadata,
                        @Assisted ImmutableMap<AtomPredicate, IntermediateQuery> mappingMap,
                        @Assisted ExecutorRegistry executorRegistry,
                        OntopModelSettings settings) {
        this.metadata = metadata;
        this.definitions = mappingMap;
        this.executorRegistry = executorRegistry;

        if (settings.isTestModeEnabled()) {
            for (IntermediateQuery query : mappingMap.values()) {
                if (projectNullableVariable(query))
                    throw new IllegalArgumentException(
                            "A mapping assertion must not return a nullable variable. \n" + query);
            }
        }
    }

    private static boolean projectNullableVariable(IntermediateQuery query) {
        ConstructionNode rootNode = query.getRootConstructionNode();
        return rootNode.getVariables().stream()
                .anyMatch(v -> rootNode.isVariableNullable(query, v));
    }

    @Override
    public MappingMetadata getMetadata() {
        return metadata;
    }

    @Override
    public Optional<IntermediateQuery> getDefinition(AtomPredicate predicate) {
        return Optional.ofNullable(definitions.get(predicate));
    }

    @Override
    public ImmutableSet<AtomPredicate> getPredicates() {
        return definitions.keySet();
    }

    @Override
    public ImmutableCollection<IntermediateQuery> getQueries() {
        return definitions.values();
    }

    @Override
    public ExecutorRegistry getExecutorRegistry() {
        return executorRegistry;
    }
}

@@ -376,36 +376,36 @@
 f.ontop.
-spec.mapping.Mapping
+iq.IntermediateQuery
 ;%0Aimport
@@ -428,36 +428,32 @@
 top.
-spec.mapping.MappingMetadata
+iq.node.ConstructionNode
 ;%0Aim
@@ -480,32 +480,33 @@
 top.
-model.atom.AtomPredicate
+iq.tools.ExecutorRegistry
 ;%0Aim
@@ -529,39 +529,39 @@
 f.ontop.
-iq.node.ConstructionNod
+model.atom.AtomPredicat
 e;%0Aimpor
@@ -581,36 +581,36 @@
 f.ontop.
-iq.IntermediateQuery
+spec.mapping.Mapping
 ;%0Aimport
@@ -633,33 +633,36 @@
 top.
-iq.tools.ExecutorRegistry
+spec.mapping.MappingMetadata
 ;%0A%0Ai
@@ -2239,69 +2239,452 @@
 ) %7B%0A
-        return Optional.ofNullable(definitions.get(predicate)
+%0A        IntermediateQuery query = definitions.get(predicate);%0A        if(query != null)%7B%0A            return checkArities(query, predicate)?%0A                    Optional.of(query):%0A                    Optional.empty();%0A        %7D%0A        return Optional.empty();%0A    %7D%0A%0A    private boolean checkArities(IntermediateQuery query, AtomPredicate predicate) %7B%0A        return  query.getProjectionAtom().getPredicate().getArity() == predicate.getArity(
 );%0A 

return only the mapping where the predicate has the same arity