package terms;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

import exceptions.SyntaxException;
import models.Position;
import models.terms.Dependency;
import models.terms.DependencyTerm;
import models.terms.Resource;

public class DependencyTermTest {

	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);
	Resource h = new Resource("h", 2);
	Resource i = new Resource("i", 2);
	Resource j = new Resource("j", 2);
	Resource k = new Resource("k", 2);
	
	@Test
	void EqualsTest() {
		DependencyTerm t1 = new DependencyTerm(a, b, c);
		DependencyTerm t2 = new DependencyTerm(a, b, c);
		assertEquals(t1, t2);
		
		DependencyTerm t3 = new DependencyTerm(a, b, c, d, e);
		assertNotEquals(t1, t3);
		
		DependencyTerm t4 = new DependencyTerm(a, d, e, b, c);
		assertEquals(t3, t4);
		
		DependencyTerm t5 = new DependencyTerm(a, b, c, b, c);
		assertNotEquals(t1, t5);
		
	}
	
	@Test
	void OrderTest() {
		DependencyTerm t1 = new DependencyTerm(a, b, c);
		assertEquals(t1.getOrder(), 1);
		
		DependencyTerm t2 = new DependencyTerm(h, i, a);
		assertEquals(t2.getOrder(), 1);
		
		DependencyTerm t3 = new DependencyTerm(a, b, c, d, e);
		assertEquals(t3.getOrder(), 1);
		
		DependencyTerm t4 = new DependencyTerm(h, i, a, j , b);
		assertEquals(t4.getOrder(), 1);
		
		DependencyTerm t5 = new DependencyTerm(h, i, a, j, k);
		assertEquals(t5.getOrder(), 2);
		
		assertThrows(SyntaxException.class, () -> {
			 new DependencyTerm(a, b, c, h, i);
		});
	}
	
	@Test
	void StringTest() {
		DependencyTerm t1 = new DependencyTerm(a, b, c);
		assertEquals(t1.toString(), "[a : b -> c]");
		
		DependencyTerm t3 = new DependencyTerm(a, b, c, d, e);
		assertEquals(t3.toString(), "[a : b -> c, d -> e]");
		
		DependencyTerm t4 = new DependencyTerm(a, d, e, b, c);
		assertEquals(t4.toString(), "[a : b -> c, d -> e]");
		
		DependencyTerm t5 = new DependencyTerm(a, b, c, b, c);
		assertEquals(t5.toString(), "[a : b -> c, b -> c]");
	}

	@Test
	void MaxIndexAndMaxDepthTest() {
		DependencyTerm t1 = new DependencyTerm(a, b, c);
		assertEquals(t1.getMaxIndex(), 3);
		assertEquals(t1.getMaxDepth(), 1);
		
		DependencyTerm t2 = new DependencyTerm(a, b, c, d, e);
		assertEquals(t2.getMaxIndex(), 5);
		assertEquals(t2.getMaxDepth(), 1);
		
		DependencyTerm t3 = new DependencyTerm(t2, f, g);
		assertEquals(t3.getMaxIndex(), 3);
		assertEquals(t3.getMaxDepth(), 2);
		
		DependencyTerm t4 = new DependencyTerm(t3, t1, t2);
		assertEquals(t4.getMaxIndex(), 3);
		assertEquals(t4.getMaxDepth(), 3);
		
	}
	
	@Test
	void ReplaceTest() {
		DependencyTerm t1 = new DependencyTerm(a, b, c);
		assertEquals(t1.replace(new Position(0, 1), d), new DependencyTerm(a, d, c));
		
		DependencyTerm t2 = new DependencyTerm(t1, d, e);
		assertEquals(t2.replace(new Position(0, 0, 0), t1), new DependencyTerm(new DependencyTerm(t1, b, c), d, e));
	}
	
	@Test
	void RequiredDependenciesForDefinitionFormulaLeftHandSideTest() {
		DependencyTerm t1 = new DependencyTerm(a, b, c);
		Dependency d1 = new Dependency(a, b);
		assertTrue(t1.requiredDependenciesForDefinitionFormulaLeftHandSide().contains(d1));
		DependencyTerm t2 = new DependencyTerm(t1, d, e);
		Dependency d2 = new Dependency(d1, d);
		Dependency d3 = new Dependency(t1, d);
		assertTrue(t2.requiredDependenciesForDefinitionFormulaLeftHandSide().contains(d2));
		assertTrue(t2.requiredDependenciesForDefinitionFormulaLeftHandSide().contains(d3));
		DependencyTerm t3 = new DependencyTerm(t2, f, g);
		Dependency d4 = new Dependency(d2, f);
		Dependency d5 = new Dependency(d3, f);
		Dependency d6 = new Dependency(t2, f);
		assertTrue(t3.requiredDependenciesForDefinitionFormulaLeftHandSide().contains(d4));
		assertTrue(t3.requiredDependenciesForDefinitionFormulaLeftHandSide().contains(d5));
		assertTrue(t3.requiredDependenciesForDefinitionFormulaLeftHandSide().contains(d6));
	}

	@Test
	void NoArgsDependencyTest() {
		DependencyTerm t1 = new DependencyTerm(a, b, c);
		Dependency d1 = new Dependency(a, b);
		assertEquals(t1.getNoArgumentDependency(), d1);
		DependencyTerm t2 = new DependencyTerm(t1, d, e);
		Dependency d2 = new Dependency(d1, d);
		assertEquals(t2.getNoArgumentDependency(), d2);
		
	}
	
}
