package it.unibz.inf.ontop.constraints.impl;

import com.google.common.collect.*;
import it.unibz.inf.ontop.constraints.*;
import it.unibz.inf.ontop.model.atom.AtomPredicate;
import it.unibz.inf.ontop.model.atom.DataAtom;

import java.util.*;

public class ImmutableCQContainmentCheckUnderLIDs<P extends AtomPredicate> implements ImmutableCQContainmentCheck<P> {

    private final Map<ImmutableList<DataAtom<P>>, ImmutableSet<DataAtom<P>>> chaseCache = new HashMap<>();

    private final BasicLinearInclusionDependenciesImpl<P> dependencies;

    public ImmutableCQContainmentCheckUnderLIDs(BasicLinearInclusionDependenciesImpl<P> dependencies) {
        this.dependencies = dependencies;
    }

    @Override
    public boolean isContainedIn(ImmutableCQ<P> cq1, ImmutableCQ<P> cq2) {
        ImmutableHomomorphism.Builder builder = ImmutableHomomorphism.builder();
        // get the substitution for the answer variables first
        // this will ensure that all answer variables are mapped either to constants or
        //       to answer variables in the base (but not to the labelled nulls generated by the chase)
        if (builder.extend(cq2.getAnswerVariables(), cq1.getAnswerVariables()).isValid()) {
            ImmutableHomomorphismIterator<P> iterator = new ImmutableHomomorphismIterator<>(
                    builder.build(),
                    cq2.getAtoms(),
                    chaseCache.computeIfAbsent(cq1.getAtoms(), dependencies::chaseAllAtoms));
            return iterator.hasNext();
        }
        return false;
    }

    public ImmutableHomomorphismIterator<P> homomorphismIterator(ImmutableHomomorphism base, ImmutableList<DataAtom<P>> from, ImmutableList<DataAtom<P>> to) {
         return new ImmutableHomomorphismIterator<>(
                base,
                from,
                chaseCache.computeIfAbsent(to, dependencies::chaseAllAtoms));
    }
}
