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

import org.junit.jupiter.api.Test;

import models.algebra.Constant;
import models.algebra.Variable;
import models.terms.Dependency;
import models.terms.DependencyTerm;
import models.terms.Resource;
import models.terms.meta.MatchConstraint;
import models.terms.meta.MetaDynamicTerm;
import models.terms.meta.MetaRDLTerm;
import models.terms.meta.MetaResource;
import models.terms.meta.MetaTermGenerator;
import utils.Utils;

public class MetaDynamicTermTest {

	Resource a = new Resource("a", Utils.INT, 1);
	Resource b = new Resource("b", Utils.INT, 1);
	Resource c = new Resource("c", Utils.INT, 1);
	Resource d = new Resource("d", Utils.INT, 1);
	Resource e = new Resource("e", Utils.INT, 1);
	Resource f = new Resource("f", Utils.INT, 1);
	Resource g = new Resource("g", Utils.INT, 1);
	Resource h = new Resource("h", Utils.INT, 1);
	Resource i = new Resource("i", Utils.INT, 3);
	Resource j = new Resource("j", Utils.INT, 2);
	Resource k = new Resource("k", Utils.INT, 2);
	Resource l = new Resource("l", Utils.INT, 1);
	Resource m = new Resource("m", Utils.INT, 1);
	Resource n = new Resource("n", Utils.INT, 0);
	Resource o = new Resource("o", Utils.INT, 0);
	
	@Test
	void GenerateTest1() {
		MetaDynamicTerm mt1 = new MetaDynamicTerm(
				new MetaTermGenerator() {
					@Override
					public MetaRDLTerm generate(int index, int depth, boolean isLast) {
						if (isLast) {
							return new MetaResource(new Variable("x"));
						}
						return new MetaDynamicTerm(this, new MetaResource(new Variable("y" + depth)));
					}
				}, 
				new MetaResource(new Variable("y"))
		);
		Dependency d1 = new Dependency(a, b);
		Dependency d2 = new Dependency(d1, c);
		Dependency d3 = new Dependency(d2, d);
		assertTrue(! mt1.isMatchedBy(d1).isEmpty());
		assertTrue(! mt1.isMatchedBy(d2).isEmpty());
		assertTrue(! mt1.isMatchedBy(d3).isEmpty());
		
		MatchConstraint result = mt1.isMatchedBy(d3).iterator().next();
		MetaRDLTerm mt2 = mt1.dependencyGenerate(result.getBinding());
		assertTrue(mt2.isMatchedBy(d3, result).contains(result));
		
		assertEquals(mt1.substitute(result.getBinding()), d3);
	}

	@Test
	void GenerateTest2() {
		MetaDynamicTerm mt1 = new MetaDynamicTerm(
				new MetaResource(new Variable("x")),
				(MetaTermGenerator) (i, d, l) -> new MetaResource(new Variable("y" + i))
		);
		Dependency d1 = new Dependency(a, b);
		Dependency d2 = new Dependency(a, b, c);
		Dependency d3 = new Dependency(a, b, c, d);
		
		assertTrue(! mt1.isMatchedBy(d1).isEmpty());
		assertTrue(! mt1.isMatchedBy(d2).isEmpty());
		assertTrue(! mt1.isMatchedBy(d3).isEmpty());
	}
	
	@Test
	void GenerateTest3() {
		MetaDynamicTerm mt1 = new MetaDynamicTerm(
				new MetaTermGenerator() {
					@Override
					public MetaRDLTerm generate(int index, int depth, boolean isLast) {
						if (isLast) {
							return new MetaResource(new Variable("x"));
						}
						return new MetaDynamicTerm(this, (MetaTermGenerator) (i, d, l) -> new MetaResource(new Variable("y" + (d+1) + "," + i)));
					}
				}, 
				(MetaTermGenerator) (i, d, l) -> new MetaResource(new Variable("y0," + i))
		);
		Dependency d1 = new Dependency(a, b, c, d);
		Dependency d2 = new Dependency(d1, c, d);
		assertTrue(! mt1.isMatchedBy(d1).isEmpty());
		assertTrue(! mt1.isMatchedBy(d2).isEmpty());
		
		MatchConstraint result = mt1.isMatchedBy(d2).iterator().next();
		MetaRDLTerm mt2 = mt1.dependencyGenerate(result.getBinding());
		assertTrue(mt2.isMatchedBy(d2, result).contains(result));
		
		assertEquals(mt1.substitute(result.getBinding()), d2);
	}

	@Test
	void GenerateTest4() {
		MetaDynamicTerm mt1 = new MetaDynamicTerm(
				new MetaTermGenerator() {
					@Override
					public MetaRDLTerm generate(int index, int depth, boolean isLast) {
						if (isLast) {
							return new MetaResource(new Variable("x"));
						}
						return new MetaDynamicTerm(this, new MetaResource(new Variable("y" + depth)), new MetaResource(new Variable("z" + depth)));
					}
				}, 
				new MetaResource(new Variable("y")),
				new MetaResource(new Variable("z"))
		);
		
		DependencyTerm t1 = new DependencyTerm(a, b, c);
		DependencyTerm t2 = new DependencyTerm(t1, d, e);
		DependencyTerm t3 = new DependencyTerm(t2, d, e);
		assertTrue(! mt1.isMatchedBy(t1).isEmpty());
		assertTrue(! mt1.isMatchedBy(t2).isEmpty());
		assertTrue(! mt1.isMatchedBy(t3).isEmpty());
		
		MatchConstraint result = mt1.isMatchedBy(t3).iterator().next();
		assertTrue(! mt1.dependencyTermGenerate(result.getBinding()).isMatchedBy(t3, result).isEmpty());
		assertEquals(mt1.substitute(result.getBinding()), t3);
		
	}
	
	@Test
	void GenerateTest5() {
		MetaDynamicTerm mt1 = new MetaDynamicTerm(
				new MetaTermGenerator() {
					@Override
					public MetaRDLTerm generate(int index, int depth, boolean isLast) {
						if (isLast) {
							return new MetaResource(new Variable("x"), new Variable("m"));
						}
						return new MetaDynamicTerm(this, new MetaResource(new Variable("y" + depth), new Variable("m-" + (depth+1))), new MetaResource(new Variable("z" + depth), new Variable("m-" + (depth+1))));
					}
				}, 
				new MetaResource(new Variable("y"), new Constant("0")),
				new MetaResource(new Variable("z"), new Constant("0"))
		);
		
		DependencyTerm t1 = new DependencyTerm(i, j, k);
		DependencyTerm t2 = new DependencyTerm(t1, l, m);
		DependencyTerm t3 = new DependencyTerm(t2, n, o);
		assertTrue(! mt1.isMatchedBy(t3).isEmpty());
		
		MatchConstraint result = mt1.isMatchedBy(t3).iterator().next();
		assertTrue(! mt1.dependencyTermGenerate(result.getBinding()).isMatchedBy(t3, result).isEmpty());
		assertEquals(mt1.substitute(result.getBinding()), t3);
	}

}
