package gol.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import gol.execution.Result;

import java.util.regex.Pattern;

public class OutputVerifier {

	private static class OutputMetrics {
		int stepsCount = 0;
		int height = 0;
		int width = 0;
	}

	private static final Pattern STEP_PATTERN = Pattern.compile("step \\d+.*");
	private static final Pattern START_PATTERN = Pattern.compile("start");

	public void verify(Expectation expectation, Result result) {

		assertEquals(0, result.getExitValue());

		if (expectation.executionTimeMin != Expectation.IGNORE) {
			verifyExecutionTimeIsInRange(expectation, result);
		}

		verify(expectation, result.getOutputLines(), result.getErrorLines());

	}

	private void verify(Expectation expects, Iterable<String> outputLines,
			Iterable<String> errorLines) {

		String errorText = toSingleString(errorLines);
		assertEquals(Expectation.NOTHING, errorText);

		if (expects.output != Expectation.NOTHING) {
			String outText = toSingleString(outputLines);
			assertEquals("Output missmatch", expects.output, outText);
		}

		OutputMetrics actual = interpretOutput(outputLines);
		assertNonIgnored("Step count", expects.stepCount, actual.stepsCount);
		assertNonIgnored("Viewport width", expects.width, actual.width);
		assertNonIgnored("Viewport height", expects.height, actual.height);
	}

	private String toSingleString(Iterable<String> lines) {
		if (!lines.iterator().hasNext())
			return Expectation.NOTHING;

		StringBuilder sb = new StringBuilder();
		for (String line : lines) {
			sb.append(line);
			sb.append(System.lineSeparator());
		}

		return sb.toString();
	}

	private OutputMetrics interpretOutput(Iterable<String> lines) {

		OutputMetrics metrics = new OutputMetrics();

		for (String line : lines)
			metrics.stepsCount += isStepLine(line) ? 1 : 0;

		for (String line : lines) {
			boolean isStartLine = isStartLine(line);
			if (isStartLine)
				break;

			metrics.width = line.length();
			metrics.height++;
		}

		return metrics;
	}

	private boolean isStartLine(String line) {
		return START_PATTERN.matcher(line).matches();
	}

	private boolean isStepLine(String line) {
		return STEP_PATTERN.matcher(line).matches();
	}

	private void assertNonIgnored(String message, int expected, int actual) {
		if (expected != Expectation.IGNORE)
			assertEquals(message, expected, actual);
	}



	private void verifyExecutionTimeIsInRange(Expectation expectation,
			Result result) {
		long actual = result.getExecutionTimeMillis();
		long expectedMin = expectation.executionTimeMin;
		long expectedMax = expectation.executionTimeMax;

		assertTrue("Expected execution time >= " + expectedMin
				+ " ms, but was " + actual, actual >= expectedMin);

		assertTrue("Expected execution time < " + expectedMax + " ms, but was "
				+ actual, actual < expectedMax);
	}
}

@@ -510,51 +510,8 @@
  %7B%0A%0A
-%09%09assertEquals(0, result.getExitValue());%0A%0A
 %09%09if
@@ -637,45 +637,122 @@
 rify
-(expectation, result.getOutputLines()
+ErrorStream(result.getErrorLines());%0A%09%09verifyOutputStream(result.getOutputLines(), expectation);%0A%0A%09%09assertEquals(0
 , re
@@ -764,23 +764,21 @@
 getE
-rrorLines
+xitValue
 ());%0A
-%0A
 %09%7D%0A%0A
@@ -801,63 +801,20 @@
 rify
-(Expectation expects, Iterable%3CString%3E outputLines,%0A%09%09%09
+ErrorStream(
 Iter
@@ -840,17 +840,16 @@
 ines) %7B%0A
-%0A
 %09%09String
@@ -904,16 +904,38 @@
 tEquals(
+%22Program err-stream%22, 
 Expectat
@@ -958,16 +958,109 @@
 orText);
+%0A%09%7D%0A%0A%09private void verifyOutputStream(Iterable%3CString%3E outputLines,%0A%09%09%09Expectation expects) %7B
 %0A%0A%09%09if (
@@ -1170,24 +1170,26 @@
 ls(%22
-Output missmatch
+Program out-stream
 %22, e
@@ -2506,18 +2506,16 @@
 l);%0A%09%7D%0A%0A
-%0A%0A
 %09private

Improve assertion failure information in OutputVerifier