package org.junit.contrib.java.lang.system;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat;
import static org.junit.contrib.java.lang.system.TextFromStandardInputStream.emptyStandardInputStream;

import java.io.InputStream;
import java.util.Scanner;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import org.junit.runners.model.Statement;

public class TextFromStandardInputStreamTest {
	private static final String ARBITRARY_TEXT = "arbitrary text";

	@Rule
	public final Timeout timeout = new Timeout(1000);

	private final TextFromStandardInputStream systemInMock = emptyStandardInputStream();

	@Test
	public void provideText() throws Throwable {
		ReadTextFromSystemIn statement = new ReadTextFromSystemIn(ARBITRARY_TEXT);
		executeRuleWithStatement(statement);
		assertThat(statement.textFromSystemIn, is(equalTo(ARBITRARY_TEXT)));
	}

	@Test
	public void providesMultipleTexts() throws Throwable {
		executeRuleWithStatement(new Statement() {
			@Override
			public void evaluate() throws Throwable {
				systemInMock.provideText("first text\n", "second text\n");
				Scanner firstScanner = new Scanner(System.in);
				firstScanner.nextLine();
				Scanner secondScanner = new Scanner(System.in);
				String textFromSystemIn = secondScanner.nextLine();
				assertThat(textFromSystemIn, is(equalTo("second text")));
			}
		});
	}

	@Test
	public void doesNotFailForNoProvidedText() throws Throwable {
		executeRuleWithStatement(new Statement() {
			@Override
			public void evaluate() throws Throwable {
				systemInMock.provideText();
				int character = System.in.read();
				assertThat(character, is(-1));
			}
		});
	}

	@Test
	public void restoreSystemIn() throws Throwable {
		InputStream originalSystemIn = System.in;
		executeRuleWithStatement(new EmptyStatement());
		assertThat(System.in, is(sameInstance(originalSystemIn)));
	}

	private void executeRuleWithStatement(Statement statement) throws Throwable {
		systemInMock.apply(statement, null).evaluate();
	}

	private class ReadTextFromSystemIn extends Statement {
		private final String textProvidedBySystemIn;
		private String textFromSystemIn;

		public ReadTextFromSystemIn(String textProvidedBySystemIn) {
			this.textProvidedBySystemIn = textProvidedBySystemIn;
		}

		@Override
		public void evaluate() throws Throwable {
			systemInMock.provideText(textProvidedBySystemIn);
			Scanner scanner = new Scanner(System.in);
			textFromSystemIn = scanner.nextLine();
		}
	}
}

@@ -547,80 +547,15 @@
 t %7B%0A
-%09private static final String ARBITRARY_TEXT = %22arbitrary text%22;%0A%0A
 %09@Rule%0A
+
 %09pub
@@ -748,122 +748,255 @@
 %7B%0A%09%09
-ReadTextFromSystemIn statement = new ReadTextFromSystemIn(ARBITRARY_TEXT);%0A%09%09executeRuleWithStatement(statement
+executeRuleWithStatement(new Statement() %7B%0A%09%09%09@Override%0A%09%09%09public void evaluate() throws Throwable %7B%0A%09%09%09%09systemInMock.provideText(%22arbitrary text%22);%0A%09%09%09%09Scanner scanner = new Scanner(System.in);%0A%09%09%09%09String textFromSystemIn = scanner.nextLine(
 );%0A
+%09%09
 %09%09as
@@ -1008,18 +1008,8 @@
 hat(
-statement.
 text
@@ -1037,24 +1037,37 @@
 lTo(
-ARBITRARY_TEXT))
+%22arbitrary text%22)));%0A%09%09%09%7D%0A%09%09%7D
 );%0A%09
@@ -2207,475 +2207,7 @@
 ;%0A%09%7D
-%0A%0A%09private class ReadTextFromSystemIn extends Statement %7B%0A%09%09private final String textProvidedBySystemIn;%0A%09%09private String textFromSystemIn;%0A%0A%09%09public ReadTextFromSystemIn(String textProvidedBySystemIn) %7B%0A%09%09%09this.textProvidedBySystemIn = textProvidedBySystemIn;%0A%09%09%7D%0A%0A%09%09@Override%0A%09%09public void evaluate() throws Throwable %7B%0A%09%09%09systemInMock.provideText(textProvidedBySystemIn);%0A%09%09%09Scanner scanner = new Scanner(System.in);%0A%09%09%09textFromSystemIn = scanner.nextLine();%0A%09%09%7D%0A%09%7D
 %0A%7D%0A

Refactor test `provideText`.