package environment;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.HashSet;
import java.util.Set;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import error.ErrorManager;

public class RuntimeEnvironmentTest {

    private static final String TERMINATED_SUCCESSFULLY = "TERMINATED_SUCCESSFULLY";
    private static final String TERMINATED_EXCEPTIONALLY = "TERMINATED_EXCEPTIONALLY";

    private RuntimeEnvironment environment;
    private Set<String> indicatorSet;

    public RuntimeEnvironmentTest() {
        this.environment = RuntimeEnvironment.getInstance();
    }

    @Before
    public void setUp() {
        indicatorSet = new HashSet<>();
        environment.reset();
    }

    @After
    public void tearDown() {
        environment.reset();
    }

    @Test
    public void assignInputName() {
        environment.setInputName("test");

        assertEquals("test", environment.getInputName());
    }

    @Test
    public void assignInput() {
        environment.setInput(System.in);

        assertEquals(System.in, environment.getInput());
    }

    @Test
    public void assignOutput() {
        environment.setOutput(System.out);

        assertEquals(System.out, environment.getOutput());
    }

    @Test
    public void assignErrorOutput() {
        environment.setErrorOutput(System.err);

        assertEquals(System.err, environment.getErrorOutput());
    }

    @Test
    public void assignErrorManager() {
        ErrorManager errorManager = new ErrorManager();
        environment.setErrorManager(errorManager);

        assertEquals(errorManager, environment.getErrorManager());
    }

    @Test
    public void assignPath() {
        environment.setPath("testpath/");

        assertEquals("testpath/", environment.getPath());
    }

    @Test
    public void assignTerminationFunction() {
        environment.setTerminationFunction(() -> indicatorSet.add(TERMINATED_SUCCESSFULLY));
        environment.terminateSuccessfully();

        assertTrue(indicatorSet.contains(TERMINATED_SUCCESSFULLY));
    }

    @Test
    public void assignErrorTerminationFunction() {
        environment.setErrorTerminationFunction(() -> indicatorSet.add(TERMINATED_EXCEPTIONALLY));
        environment.terminateExceptionally();

        assertTrue(indicatorSet.contains(TERMINATED_EXCEPTIONALLY));
    }

    @Test
    public void assignPromptDecorator() {
        environment.setPromptDecorator(s -> "[" + s + "]");

        assertEquals("[test]", environment.decoratePrompt("test"));
    }

    @Test
    public void assignValueOutputDecorator() {
        environment.setValueOutputDecorator(s -> "(" + s + ")");

        assertEquals("(test)", environment.decorateValueOutput("test"));
    }

    @Test
    public void assignWarningOutputDecorator() {
        environment.setWarningOutputDecorator(s -> "|" + s + "|");

        assertEquals("|test|", environment.decorateWarningOutput("test"));
    }

    @Test
    public void assignErrorOutputDecorator() {
        environment.setErrorOutputDecorator(s -> "{" + s + "}");

        assertEquals("{test}", environment.decorateErrorOutput("test"));
    }

    @Test
    public void assignCriticalOutputDecorator() {
        environment.setCriticalOutputDecorator(s -> "/" + s + "/");

        assertEquals("/test/", environment.decorateCriticalOutput("test"));
    }

    @Test
    public void resetWorks() {
        environment.setInputName("test");
        environment.setInput(System.in);
        environment.setOutput(System.out);
        environment.setErrorOutput(System.err);
        environment.setErrorManager(new ErrorManager());
        environment.setPath("testpath/");
        environment.setTerminationFunction(() -> indicatorSet.add(TERMINATED_SUCCESSFULLY));
        environment.setErrorTerminationFunction(() -> indicatorSet.add(TERMINATED_EXCEPTIONALLY));
        environment.setPromptDecorator(s -> "[" + s + "]");
        environment.setValueOutputDecorator(s -> "(" + s + ")");
        environment.setWarningOutputDecorator(s -> "|" + s + "|");
        environment.setErrorOutputDecorator(s -> "{" + s + "}");
        environment.setCriticalOutputDecorator(s -> "/" + s + "/");
        environment.reset();

        assertNull(environment.getInputName());
        assertNull(environment.getInput());
        assertNull(environment.getOutput());
        assertNull(environment.getErrorOutput());
        assertNull(environment.getErrorManager());
        assertNull(environment.getPath());

        try {
            environment.terminateSuccessfully();
            fail("terminateSuccessfully");
        } catch (NullPointerException e) {}

        try {
            environment.terminateExceptionally();
            fail("terminateExceptionally");
        } catch (NullPointerException e) {}

        try {
            environment.decoratePrompt("");
            fail("decoratePrompt");
        } catch (NullPointerException e) {}

        try {
            environment.decorateValueOutput("");
            fail("decorateValueOutput");
        } catch (NullPointerException e) {}

        try {
            environment.decorateWarningOutput("");
            fail("decorateWarningOutput");
        } catch (NullPointerException e) {}

        try {
            environment.decorateErrorOutput("");
            fail("decorateErrorOutput");
        } catch (NullPointerException e) {}

        try {
            environment.decorateCriticalOutput("");
            fail("decorateCriticalOutput");
        } catch (NullPointerException e) {}
    }

}
