package models.terms.meta;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import exceptions.SubstituteFailedException;
import models.Position;
import models.algebra.Expression;
import models.algebra.Variable;
import models.terms.RDLTerm;
public class MetaDynamicDependency extends MetaDependency implements MetaDynamicTerm {
private final MetaTermGenerator termGenerator;
public MetaDynamicDependency(MetaTermGenerator termGenerator) {
this.termGenerator = termGenerator;
}
public MetaDynamicDependency(MetaTermGenerator termGenerator, List<? extends RDLTerm> terms) {
this.termGenerator = termGenerator;
this.dependingTerm = terms.size() > 0 ? terms.get(0) : null;
this.dependedTerms = terms.size() > 1 ? terms.stream().skip(1).toList() : List.of();
for (RDLTerm term : terms) {
addChild(term);
}
}
public MetaDynamicDependency(MetaTermGenerator termGenerator, RDLTerm ...terms) {
this(termGenerator, Arrays.asList(terms));
}
@Override
public Set<MatchConstraint> isMatchedBy(RDLTerm another, MatchConstraint constraint, Position position) {
int maxIndex = another.getMaxIndex();
Set<MatchConstraint> result = new HashSet<>();
constraint.getContext().put(position, maxIndex);
if (! (Boolean) constraint.getContext().getOrDefault("isDynamicStarted", false)) {
int maxDepth = another.getMaxDepth();
constraint.getContext().put("isDynamicStarted", true);
for (int i = maxDepth; i >= 1; i--) {
MatchConstraint newConstraint = new MatchConstraint(constraint);
newConstraint.getContext().put("maxDepth", i);
MetaRDLTerm metaDep = generate(position.size(), maxIndex, i, newConstraint.getContext());
Set<MatchConstraint> res = metaDep.isMatchedBy(another, newConstraint, position);
result.addAll(res);
}
} else {
int maxDepth = (Integer) constraint.getContext().get("maxDepth");
MetaRDLTerm metaDep = generate(position.size(), maxIndex, maxDepth, constraint.getContext());
Set<MatchConstraint> res = metaDep.isMatchedBy(another, constraint, position);
result.addAll(res);
}
return result;
}
@Override
public MetaDependency generate(int depth, int maxIndex, int maxDepth, Map<Object, Object> context) {
if (maxIndex < 2) return null;
if (depth > maxDepth) return null;
RDLTerm dependingTerm = this.dependingTerm == null ? termGenerator.generate(0, depth, maxIndex, maxDepth, context) : this.dependingTerm;
List<RDLTerm> dependedTerms = new ArrayList<>();
int staticSize = this.dependedTerms == null ? 0 : this.dependedTerms.size();
for (int i = 0; i < Math.min(maxIndex - 1, staticSize); i++) {
dependedTerms.add(this.dependedTerms.get(i));
}
for (int i = staticSize; i < maxIndex - 1; i++) {
RDLTerm generatedTerm = termGenerator.generate(i + 1, depth, maxIndex, maxDepth, context);
if (generatedTerm == null) {
break;
}
dependedTerms.add(generatedTerm);
}
return new MetaDependency(dependingTerm, dependedTerms);
}
@Override
public MetaDependency allGenerate(int depth, int maxIndex, int maxDepth, Map<Object, Object> context) {
if (maxIndex < 2) return null;
if (depth > maxDepth) return null;
RDLTerm dependingTerm = this.dependingTerm == null ? termGenerator.generate(0, depth, maxIndex, maxDepth, context) : this.dependingTerm;
while (dependingTerm instanceof MetaDynamicTerm dynamicTerm) {
dependingTerm = dynamicTerm.allGenerate(depth + 1, maxIndex, maxDepth, context);
}
List<RDLTerm> dependedTerms = new ArrayList<>();
int staticSize = this.dependedTerms == null ? 0 : this.dependedTerms.size();
for (int i = 0; i < Math.min(maxIndex - 1, staticSize); i++) {
dependedTerms.add(this.dependedTerms.get(i));
}
for (int i = staticSize; i < maxIndex - 1; i++) {
RDLTerm generatedTerm = termGenerator.generate(i + 1, depth, maxIndex, maxDepth, context);
if (generatedTerm == null) {
break;
}
while (generatedTerm instanceof MetaDynamicTerm dynamicTerm) {
generatedTerm = dynamicTerm.allGenerate(depth + 1, maxIndex, maxDepth, context);
}
dependedTerms.add(generatedTerm);
}
return new MetaDependency(dependingTerm, dependedTerms);
}
@Override
public RDLTerm substitute(Map<Variable, RDLTerm> binding, Map<Object, Object> context, Position position) {
if (! context.containsKey(position)) throw new SubstituteFailedException();
if (! context.containsKey("maxDepth")) throw new SubstituteFailedException();
int maxIndex = (int) context.get(position);
int maxDepth = (int) context.get("maxDepth");
return generate(position.size(), maxIndex, maxDepth, context).substitute(binding, context, position);
}
@Override
public String toString() {
if (dependingTerm != null) {
return "[" +
getChild(0).toString() + " : " +
getChildren().stream().skip(1).map(Expression::toString).collect(Collectors.joining(",")) +
" ...? ]";
}
return "[ ...? ]";
}
}