package testutil;

import static error.ErrorManager.Severity.ERROR;
import static function.builtin.EVAL.eval;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static sexpression.Nil.NIL;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

import error.LispException;
import parser.LispParser;
import sexpression.Cons;
import sexpression.SExpression;

public final class TestUtilities {

    public static InputStream createInputStreamFromString(String string) {
        return new ByteArrayInputStream(string.getBytes());
    }

    public static InputStream createIOExceptionThrowingInputStream() {
        return new InputStream() {

            @Override
            public int read() throws IOException {
                throw new IOException("read()");
            }

            @Override
            public void close() throws IOException {
                throw new IOException("close()");
            }
        };
    }

    public static OutputStream createIOExceptionThrowingOutputStream() {
        return new OutputStream() {

            @Override
            public void write(byte[] b) throws IOException {
                throw new IOException("write(byte[])");
            }

            @Override
            public void flush() throws IOException {
                throw new IOException("flush()");
            }

            @Override
            public void close() throws IOException {
                throw new IOException("close()");
            }

            @Override
            public void write(int arg0) throws IOException {
                throw new IOException("write(int)");
            }
        };
    }

    public static SExpression evaluateString(String input) {
        return eval(parseString(input));
    }

    public static SExpression parseString(String input) {
        InputStream stringInputStream = TestUtilities.createInputStreamFromString(input);

        return new LispParser(stringInputStream, "testFile").getNextSExpression();
    }

    public static Cons makeList(SExpression... expressionList) {
        if (expressionList.length == 0)
            return NIL;

        Cons rest = makeList(Arrays.copyOfRange(expressionList, 1, expressionList.length));

        return new Cons(expressionList[0], rest);
    }

    public static void assertSExpressionsMatch(SExpression one, SExpression two) {
        assertEquals(one.toString(), two.toString());
    }

    public static void assertSExpressionsDoNotMatch(SExpression one, SExpression two) {
        assertNotEquals(one.toString(), two.toString());
    }

    public static void assertIsErrorWithMessage(LispException e) {
        assertEquals(ERROR, e.getSeverity());
        assertNotNull(e.getMessage());
        assertTrue(e.getMessage().length() > 0);
    }

}
