package function.builtin.special;

import static testutil.TestUtilities.assertSExpressionsMatch;
import static testutil.TestUtilities.evaluateString;
import static testutil.TestUtilities.parseString;

import org.junit.Test;

import function.ArgumentValidator.BadArgumentTypeException;
import function.ArgumentValidator.DottedArgumentListException;
import testutil.SymbolAndFunctionCleaner;

public class CONDTest extends SymbolAndFunctionCleaner {

    @Test
    public void condWithNoArguments() {
        String input = "(cond)";

        assertSExpressionsMatch(parseString("nil"), evaluateString(input));
    }

    @Test
    public void condWithTrue() {
        String input = "(cond (T))";

        assertSExpressionsMatch(parseString("T"), evaluateString(input));
    }

    @Test
    public void condWithNumber() {
        String input = "(cond ((+ 1 2)))";

        assertSExpressionsMatch(parseString("3"), evaluateString(input));
    }

    @Test
    public void condWithSingleClause() {
        String input = "(cond (T \"true\"))";

        assertSExpressionsMatch(parseString("\"true\""), evaluateString(input));
    }

    @Test
    public void condWithMultipleClauses() {
        String input = "(cond ((= 1 2) 2) ((= 1 2) 2) ((= 1 1) 3))";

        assertSExpressionsMatch(parseString("3"), evaluateString(input));
    }

    @Test
    public void condWithMultipleTrueTests_ReturnsFirstOne() {
        String input = "(cond ((= 1 1) 2) ((= 1 1) 3))";

        assertSExpressionsMatch(parseString("2"), evaluateString(input));
    }

    @Test
    public void condWithMultipleTrueTests_OnlyEvaluatesFirstOne() {
        String input = "(cond ((= 1 1) 2) ((= 1 1) x))";

        assertSExpressionsMatch(parseString("2"), evaluateString(input));
    }

    @Test
    public void condWithMultipleValuesInConsequent_OnlyReturnsLast() {
        String input = "(cond ((= 1 1) 2 3 4))";

        assertSExpressionsMatch(parseString("4"), evaluateString(input));
    }

    @Test
    public void condWithNoTrueTest_ReturnsNil() {
        String input = "(cond ((= 1 2) T) ((= 1 3) T))";

        assertSExpressionsMatch(parseString("nil"), evaluateString(input));
    }

    @Test(expected = BadArgumentTypeException.class)
    public void condWithEmptyListArgument_ThrowsException() {
        evaluateString("(cond ())");
    }

    @Test(expected = BadArgumentTypeException.class)
    public void condWithNilArgument_ThrowsException() {
        evaluateString("(cond nil)");
    }

    @Test(expected = BadArgumentTypeException.class)
    public void condWithNonListArgument_ThrowsException() {
        evaluateString("(cond o)");
    }

    @Test(expected = DottedArgumentListException.class)
    public void condWithDottedArgumentList_ThrowsException() {
        evaluateString("(apply 'cond (cons '(nil T) 'b))");
    }

}
