package terms.meta;
import static org.junit.jupiter.api.Assertions.*;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.junit.jupiter.api.Test;

import models.algebra.Variable;
import models.terms.Dependency;
import models.terms.RDLTerm;
import models.terms.Resource;
import models.terms.meta.MatchConstraint;
import models.terms.meta.MetaDependency;
import models.terms.meta.MetaDependencyVariable;
import models.terms.meta.MetaDynamicDependency;
import models.terms.meta.MetaRDLTerm;
import models.terms.meta.MetaRDLTermVariable;
import models.terms.meta.MetaResource;
import models.terms.meta.MetaTermGenerator;

public class MetaDynamicDependencyTest {

	Resource a = new Resource("a", 1);
	Resource b = new Resource("b", 1);
	Resource c = new Resource("c", 1);
	Resource d = new Resource("d", 1);
	Resource e = new Resource("e", 1);
	Resource f = new Resource("f", 1);
	Resource g = new Resource("g", 1);
	
	@Test
	void DynamicDependencyMatchTest() {
		MetaDynamicDependency md1 = new MetaDynamicDependency((ci, cd, mi, md, context) -> new MetaResource(new Variable("x" + ci)));
		Dependency d1 = new Dependency(a, b, c, d);
		assertFalse(md1.isMatchedBy(d1).isEmpty());
		
		Dependency d2 = new Dependency(new Dependency(a, b), c);
		Dependency d3 = new Dependency(d2, d, e);
		MetaDynamicDependency md2 = new MetaDynamicDependency(new MetaTermGenerator() {
			@Override
			public MetaRDLTerm generate(int curIndex, int curDepth, int maxIndex, int maxDepth, Map<String, Object> context) {
				if (curDepth != maxDepth && curIndex == 0) {
					return new MetaDynamicDependency(this);
				}
				return new MetaResource(new Variable("x" + curDepth + "_" + curIndex));
			}
		});
		assertFalse(md2.isMatchedBy(d2).isEmpty());
		assertTrue(md2.isMatchedBy(d3).isEmpty());
		
		MetaResource x = new MetaResource(new Variable("x"));
		MetaResource y = new MetaResource(new Variable("y"));
		Dependency d4 = new Dependency(a, b);
		MetaDynamicDependency md3 = new MetaDynamicDependency((ci, cd, mi, md, context) -> new MetaResource(new Variable("x" + ci)), x, y);
		Set<MatchConstraint> constraint = new MetaDependency(x, y).isMatchedBy(d4);
		Dependency d5 = new Dependency(a, b, c);
		constraint = md3.isMatchedBy(d5, constraint);
		assertFalse(constraint.isEmpty());
		Map<Variable, RDLTerm> binding = new HashMap<>();
		binding.put(new Variable("x"), a);
		binding.put(new Variable("y"), b);
		binding.put(new Variable("x2"), c);
		assertEquals(constraint.iterator().next().getBinding(), binding);
	}
	
	@Test
	void DynamicSubstituteTest() {
		MetaDynamicDependency md1 = new MetaDynamicDependency((ci, cd, mi, md, context) -> new MetaResource(new Variable("x" + ci)));
		Dependency d1 = new Dependency(a, b, c, d);
		MatchConstraint result = md1.isMatchedBy(d1).iterator().next();
		RDLTerm generated = md1.substitute(result.getBinding(), Map.of("maxIndex", 4, "maxDepth", 1));
		assertEquals(generated, d1);
		
		Dependency d2 = new Dependency(new Dependency(a, b), c);
		Dependency d3 = new Dependency(d2, d, e);
		MetaDynamicDependency md2 = new MetaDynamicDependency(new MetaTermGenerator() {
			@Override
			public MetaRDLTerm generate(int curIndex, int curDepth, int maxIndex, int maxDepth, Map<String, Object> context) {
				if (curDepth != maxDepth && curIndex == 0) {
					return new MetaDynamicDependency(this);
				}
				return new MetaResource(new Variable("x" + curDepth + "_" + curIndex));
			}
		});
		result = md2.isMatchedBy(d2).iterator().next();
		generated = md2.substitute(result.getBinding(), Map.of("maxIndex", d2.getMaxIndex(), "maxDepth", d2.getMaxDepth()));
		assertEquals(generated, d2);
	}
	
	@Test
	void DynamiMatchTest() {
		MetaDynamicDependency md1 = new MetaDynamicDependency(
				new MetaTermGenerator() {
					@Override
					public MetaRDLTerm generate(int curIndex, int curDepth, int maxIndex, int maxDepth, Map<String, Object> context) {
						if (curDepth == maxDepth - 1 && curIndex == 0) {
							return new MetaDependencyVariable(new Variable("d"));
						}
						if (curIndex == 0) {
							return new MetaDynamicDependency(this);
						}
						return new MetaRDLTermVariable(new Variable("x"));
					}
				}
		);
		
		Dependency d1 = new Dependency(a, b);
		Dependency d2 = new Dependency(d1, b);
		Dependency d3 = new Dependency(d2, b);
		assertFalse(md1.isMatchedBy(d2).isEmpty());
		assertFalse(md1.isMatchedBy(d3).isEmpty());
	}
	
}
