package pp.s1184725.boppi.test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import org.junit.Test;

/**
 * Tests for function use and simple function passing (i.e. no closures required).
 * 
 * @author Frank Wibbelink
 */
public class SimpleFunctionTest {

	/**
	 * Correct function declaration and calls
	 */
	@Test
	public void correctFunctionParsing() {
		BoppiTests.parseString("function constantProcedure() 0; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("function procedure(int a) a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("function int id(int a) a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("function int id(int a) a; id(1)");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("var int a; a := 1; function int constant() a; constant()");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("function char constant(char a, char b) a; print(constant('A', 'T'))");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.parseString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}");
		assertThat(BoppiTests.log, is(empty()));
	}

	/**
	 * Function use with syntax errors
	 */
	@Test
	public void wrongFunctionParsing() {
		BoppiTests.parseString("function char add(int a, int b); 'c'");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.parseString("function char add(a, b); 1");
		assertThat(BoppiTests.log, is(not(empty())));

		BoppiTests.parseString("function int add(int a, int b) a+b; add(4,)");
		assertThat(BoppiTests.log, hasSize(1));
	}

	/**
	 * Correct function declaration and calls
	 */
	@Test
	public void correctFunctionChecking() {
		BoppiTests.checkString("function int id(int a) a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("function int id(int a) a; id(1); 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("var int a; function int constant() a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("function char constant(char a, char b) a; print(constant('A', 'T'))");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}; 0");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.checkString("var int a; a := 1; function int id(int a) a; 1");
		assertThat(BoppiTests.log, is(empty()));
	}

	/**
	 * Function use with type errors
	 */
	@Test
	public void wrongFunctionChecking() {
		BoppiTests.checkString("var int add; add := 1; add(4, 2)");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("function int add(int a, char b) a+b; print(add(4, 'T')+1)");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("function char add(int a, int b) a+b; print(add(4, 5))");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("function int add(int a, int b) a+b; print(add(4, 'T')+1)");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("function int id(int a) a; print(id(4, 2)+1)");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("function nothing(int a) a; print(nothing(2))");
		assertThat(BoppiTests.log, hasSize(1));

		BoppiTests.checkString("function int add(int a, int b) a+b; print(add(4)+1)");
		assertThat(BoppiTests.log, hasSize(1));
	}

	/**
	 * Correct evaluation of basic functions and procedures
	 */
	@Test
	public void correctSimpleFunctionGeneration() {
		BoppiTests.compileAndRunString("function int id(int a) a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.compileAndRunString("function int id(int a) a; id(1); 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.compileAndRunString("var int a; function int constant(int b) a; 1");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.compileAndRunString("var int a; a := 1; function int constant(int b) a; print(constant(4))");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("1")));

		BoppiTests.compileAndRunString("var int a; a := 1; function int id(int a) a; print(id(4))");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("4")));

		BoppiTests.compileAndRunString("function int add(int a, int b) a+b; print(add(4, 5)+1)");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("10")));

		BoppiTests.compileAndRunString("function char constant(char a, char b) a; print(constant('A', 'T'))");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("A")));

		BoppiTests.compileAndRunFile("programs/simpleProcedure.boppi", "8");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("5", "16")));
	}

	/**
	 * Correct evaluation of recursive functions, nested functions and function
	 * passing
	 */
	@Test
	public void correctComplexFunctionGeneration() {
		BoppiTests.compileAndRunString("function int factorial(int n) {if (n > 1) then n*factorial(n-1) else 1 fi}; 0");
		assertThat(BoppiTests.log, is(empty()));

		BoppiTests.compileAndRunFile("programs/recursiveFactorial.boppi", "5");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("120")));

		BoppiTests.compileAndRunFile("programs/simpleFunctionPassing.boppi");
		assertThat(BoppiTests.log, is(empty()));
		assertThat(BoppiTests.out, is(arrayContaining("40", "104", "1", "2", "8")));
	}

}
