package pp.s1184725.boppi.test;

import java.util.List;
import java.util.logging.*;

import org.antlr.v4.runtime.CharStream;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import pp.s1184725.boppi.*;

/**
 * Test cases for the parsing stage for all language features.
 */
public class ParserTest {
	private List<LogRecord> log;

	private void parseString(String code) {
		parse(ToolChain.getCharStream(code));
	}

	private void parseFile(String file) {
		parse(ToolChain.getCharStream(AllTests.directory.resolve(file)));
	}

	private void parse(CharStream stream) {
		Logger logger = Logger.getAnonymousLogger();
		log = ToolChain.makeListLog(logger);
		logger.setLevel(AllTests.logLevel);

		ToolChain.getParser(ToolChain.getLexer(stream, logger), logger).program();
	}

	/**
	 * Correct basic expressions
	 */
	@Test
	public void correctExpressionTest() {
		parseFile("simpleExpression.boppi");
		assertThat(log, empty());
	}

	/**
	 * Basic expressions with syntax errors
	 */
	@Test
	public void wrongExpressionTest() {
		parseString("");
		assertThat(log, not(empty()));

		parseString("~");
		assertThat(log, not(empty()));

		parseString("0A");
		assertThat(log, hasSize(1));

		parseString("do");
		assertThat(log, hasSize(1));

		parseString("true true");
		assertThat(log, hasSize(1));
	}

	/**
	 * Correct variable use
	 */
	@Test
	public void correctVariableTest() {
		parseFile("simpleVariable.boppi");
		assertThat(log, empty());
	}

	/**
	 * Variable use with syntax errors
	 */
	@Test
	public void wrongVariableTest() {
		parseString("var");
		assertThat(log, hasSize(1));

		parseString("var bool 5");
		assertThat(log, hasSize(1));

		parseString("var 'c' varname");
		assertThat(log, not(empty()));

		parseString("var bool; true true;");
		assertThat(log, hasSize(2));
	}

	/**
	 * Correct variable scoping
	 */
	@Test
	public void correctScopeTest() {
		parseFile("simpleScope.boppi");
		assertThat(log, empty());
	}

	/**
	 * Correct if-else and loop use
	 */
	@Test
	public void correctConditionalTest() {
		parseFile("if.boppi");
		assertThat(log, empty());

		parseFile("while.boppi");
		assertThat(log, empty());
	}

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

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

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

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

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

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

	/**
	 * Function use with syntax errors
	 */
	@Test
	public void wrongFunctionTest() {
		parseString("function doNothing(int a, char b) a+b; print(add(4, 'T')+1)");
		assertThat(log, hasSize(1));

		parseString("function char add(int a, int b);");
		assertThat(log, hasSize(1));

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