package migl.lisp;

import static org.junit.Assert.assertEquals;

import java.math.BigInteger;

import org.junit.Test;

import migl.util.ConsList;
import migl.util.ConsListFactory;

/**
 * Those tests represent incorrect input for our lisp interpreter.
 * 
 * @author leberre
 *
 */
public class MoreLispParserTest extends AbstractTDD {

    @Test(expected = LispError.class)
    public void testNoEndingParenthesis() throws LispError {
        lisp.parse("(r");
    }

    @Test(expected = LispError.class)
    public void testNoOpeningingParenthesis() throws LispError {
        lisp.parse("r)");
    }

    @Test(expected = LispError.class)
    public void testMissingEndOfList() throws LispError {
        lisp.parse("(< (+ 1 3) 4");
    }

    @Test(expected = LispError.class)
    public void testAlternativeMissingEndOfList() throws LispError {
        lisp.parse("(< (+ 1 3 4)");
    }

    @Test(expected = LispError.class)
    public void testMissingBeginningOfList() throws LispError {
        lisp.parse("< (+ 1 3 4))");
    }

    @Test(expected = LispError.class)
    public void testAlternativeMissingBeginningOfList() throws LispError {
        lisp.parse("(< + 1 3 4))");
    }

    @Test(expected = LispError.class)
    public void testCountingIsNotEnoughAkaTestDessaint() throws LispError {
        lisp.parse(")< + 1 3 4(");
    }

    @Test(expected = LispError.class)
    public void testAlternativeCountingIsNotEnoughAkaTestDessaint() throws LispError {
        lisp.parse("(< )+ 1 3 4()");
    }

    @Test
    public void testSpacesInExpressionShouldNotMatterAkaDessaintAgain() throws LispError {
        ConsList<Object> l1 = ConsListFactory.nil().prepend("r").prepend("r").prepend("+");
        ConsList<Object> expected = ConsListFactory.nil().prepend(BigInteger.valueOf(10)).prepend(l1).prepend("*");
        assertEquals(expected, lisp.parse("(*  (+ r r) 10)"));
        assertEquals(expected, lisp.parse("(* ( + r r ) 10)"));
        assertEquals(expected, lisp.parse("(* (+ r r) 10 ) "));
        assertEquals(expected, lisp.parse(" (* (+ r r) 10) "));
        assertEquals(expected, lisp.parse(" ( * ( + r r ) 10 ) "));
    }

    @Test
    public void testTabsInExpressionShouldNotMatterAkaTestVrand() throws LispError {
        ConsList<Object> l1 = ConsListFactory.nil().prepend("r").prepend("r").prepend("+");
        ConsList<Object> expected = ConsListFactory.nil().prepend(BigInteger.valueOf(10)).prepend(l1).prepend("*");
        assertEquals(expected, lisp.parse("(*\t(+\tr\tr)\t10)"));
        assertEquals(expected, lisp.parse("(*\t\t(\t+\tr\tr\t)\t10)"));
        assertEquals(expected, lisp.parse("(*\t(+\tr\tr)\t10\t)\t"));
        assertEquals(expected, lisp.parse("\t(*\t(+\tr\tr)\t10)\t"));
        assertEquals(expected, lisp.parse("\t(\t* (\t+\tr\tr\t)\t10\t)\t"));
    }

    @Test(expected = LispError.class)
    public void testThatEmptyStringCannotBeEvaluatedAkaVrandIsBack() throws LispError {
        lisp.parse("");
    }

    @Test(expected = LispError.class)
    public void testThatOnlySpacesCannotBeEvaluatedAkaVrandIsBack() throws LispError {
        lisp.parse("   ");
    }

    @Test(expected = LispError.class)
    public void testThatOnlyTabsCannotBeEvaluatedAkaVrandIsBack() throws LispError {
        lisp.parse("\t\t");
    }

    @Test(expected = LispError.class)
    public void testThatTwoElementsIsNotCorrect() throws LispError {
        lisp.parse("foo bar");
    }

    @Test(expected = LispError.class)
    public void testThatRemainingDataCannotBeIgnored() throws LispError {
        lisp.parse("(* x y) foo");
    }

    @Test(expected = LispError.class)
    public void testThatRemainingDataCannotBeIgnoredTwoLists() throws LispError {
        lisp.parse("(* x y)(- y 20)");
    }

    @Test
    public void testParsingSingleExpressionHachinLovesSpaces() throws LispError {
        assertEquals("r", lisp.parse("r   "));
        ConsList<Object> expected = ConsListFactory.nil().prepend("+");
        assertEquals(expected, lisp.parse("(+        ) "));
    }
}
