package tokenizer;

import static org.junit.Assert.*;
import static tokenizer.TokenType.*;

import java.util.NoSuchElementException;

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

public class OfficialTokenizerTest2 {
    private Tokenizer t;

    @Before
    public void setUp() throws Exception {
    }

    @Test
    public final void testHasNextWithNoInput() {
        t = new Tokenizer("");
        assertFalse(t.hasNext());
        t = new Tokenizer("  \t  ");
        assertFalse(t.hasNext());
    }

    @Test
    public final void testHasNext() {
        t = new Tokenizer("x y");
        assertTrue(t.hasNext());
        t.next();
        assertTrue(t.hasNext());
        t.next();
        assertFalse(t.hasNext());

        t = new Tokenizer(" x ");
        assertTrue(t.hasNext());
        t.next();
        assertFalse(t.hasNext());
    }

    @Test
    public final void testNextName() {
        t = new Tokenizer("x xyz abc123 abc_123 a1b2_c3 _");
        assertEquals(new Token(NAME, "x"), t.next());
        assertEquals(new Token(NAME, "xyz"), t.next());
        assertEquals(new Token(NAME, "abc123"), t.next());
        assertEquals(new Token(NAME, "abc_123"), t.next());
        assertEquals(new Token(NAME, "a1b2_c3"), t.next());
        assertEquals(new Token(NAME, "_"), t.next());
    }
    
    @Test
    public final void testNextKeyword() {
        String keywords =
                "program set repeat while if call turn take drop stop " +
                "forward back right left around seeing holding not def " +
                "row column distance";
        t = new Tokenizer(keywords);
        assertEquals(new Token(KEYWORD, "program"), t.next());
        while (t.hasNext()) {
            assertEquals(KEYWORD, t.next().type);
        }
    }

    @Test
    public final void testNextInt() {
        t = new Tokenizer("5 1234567890 0");
        assertEquals(new Token(INT, "5"), t.next());
        assertEquals(new Token(INT, "1234567890"), t.next());
        assertEquals(new Token(INT, "0"), t.next());
    }

    @Test
    public final void testNextFloatWithoutExponent() {
        t = new Tokenizer("123.45 123. .45");
        assertEquals(new Token(FLOAT, "123.45"), t.next());
        assertEquals(new Token(FLOAT, "123."), t.next());
        assertEquals(new Token(FLOAT, ".45"), t.next());
    }

    @Test
    public final void testNextFloatWithExponentAndDecimalPoint() {
        t = new Tokenizer("123.45e6 123.E6 .45e12");
        assertEquals(new Token(FLOAT, "123.45e6"), t.next());
        assertEquals(new Token(FLOAT, "123.E6"), t.next());
        assertEquals(new Token(FLOAT, ".45e12"), t.next());

        t = new Tokenizer("123.45e+6 123.E-6 .45e12");
        assertEquals(new Token(FLOAT, "123.45e+6"), t.next());
        assertEquals(new Token(FLOAT, "123.E-6"), t.next());
    }

    @Test
    public final void testNextFloatWithSignedExponentAndDecimalPoint() {
        t = new Tokenizer("123.45e+6 123.E-6 .45e12");
        assertEquals(new Token(FLOAT, "123.45e+6"), t.next());
        assertEquals(new Token(FLOAT, "123.E-6"), t.next());
    }

    @Test
    public final void testNextFloatWithExponentAndNoDecimalPoint() {
        t = new Tokenizer("123e6 123E+6 123e-6");
        assertEquals(new Token(FLOAT, "123e6"), t.next());
        assertEquals(new Token(FLOAT, "123E+6"), t.next());
        assertEquals(new Token(FLOAT, "123e-6"), t.next());
    }

    @Test
    public final void testNextFloatWithTooLongExponent() {
        t = new Tokenizer("123.45e67890");
        assertEquals(new Token(FLOAT, "123.45e678"), t.next());
        assertEquals(new Token(INT, "90"), t.next());
    }

    @Test
    public final void testNextGroupingSymbol() {
        t = new Tokenizer("() [] {}");
        assertEquals(new Token(GROUPING_SYMBOL, "("), t.next());
        assertEquals(new Token(GROUPING_SYMBOL, ")"), t.next());
        assertEquals(new Token(GROUPING_SYMBOL, "["), t.next());
        assertEquals(new Token(GROUPING_SYMBOL, "]"), t.next());
        assertEquals(new Token(GROUPING_SYMBOL, "{"), t.next());
        assertEquals(new Token(GROUPING_SYMBOL, "}"), t.next());
    }

    @Test
    public final void testNextSingleCharacterOperator() {
        String input = "` ~ ! @ $ % ^ & * - = + * / % \\ | ; : , < . > ?";
        String[] operators = input.split(" ");
        t = new Tokenizer(input);
        for (String operator : operators) {
            assertEquals(new Token(OPERATOR, operator), t.next());
        }
    }

    @Test
    public final void testNextTwoCharacterOperator() {
        String input = "-= += *= /= %= == != <= >= ";
        String[] operators = input.split(" ");
        t = new Tokenizer(input);
        for (String operator : operators) {
            assertEquals(new Token(OPERATOR, operator), t.next());
        }
    }

    @Test
    public final void testNextSimpleQuotedStrings() {
        t = new Tokenizer("'abc' \"abc\" '' \"\"");
        assertEquals(new Token(STRING, "'abc'"), t.next());
        assertEquals(new Token(STRING, "\"abc\""), t.next());
        assertEquals(new Token(STRING, "''"), t.next());
        assertEquals(new Token(STRING, "\"\""), t.next());
    }

    @Test
    public final void testNextSinglyQuotedString() {
        t = new Tokenizer("'abc\"123' '\"abc\"'");
        assertEquals(new Token(STRING, "'abc\"123'"), t.next());
        assertEquals(new Token(STRING, "'\"abc\"'"), t.next());
    }

    @Test
    public final void testNextDoublyQuotedString() {
        t = new Tokenizer(" \"abc'abc\" \"'abc'\"");
        assertEquals(new Token(STRING,"\"abc'abc\""), t.next());
        assertEquals(new Token(STRING, "\"'abc'\""), t.next());
    }

    @Test
    public final void testNextIncompleteString() {
        t = new Tokenizer(" \"abc\nabc\"");
        assertEquals(new Token(STRING,"\"abc\nabc\""), t.next());
    }

    @Test
    public final void testNextComment() {
        t = new Tokenizer("x #comment 1\n#comment 2");
        assertEquals(new Token(TokenType.NAME,"x"), t.next());
        assertEquals(new Token(COMMENT,"#comment 1"), t.next());
        assertEquals(new Token(COMMENT, "#comment 2"), t.next());
    }

    @Test
    public final void testNextError() {
        Token x = new Token(TokenType.NAME,"x");
        t = new Tokenizer("x\u0000x");
        assertEquals(x, t.next());
        assertEquals(TokenType.ERROR, t.next().type);
        assertEquals(x, t.next());
    }

    @Test(expected=UnsupportedOperationException.class)
    public final void testRemove() {
        t = new Tokenizer("x y");
        t.next();
        t.remove();
    }

    @Test(expected=NoSuchElementException.class)
    public final void testNextAfterLastToken() {
        t = new Tokenizer("");
        t.next();
        t.remove();
    }

    @Test
    public final void testBackUp() {
        t = new Tokenizer("abc defg hi");
        assertEquals(new Token(NAME, "abc"), t.next());
        t.backUp();
        assertEquals(new Token(NAME, "abc"), t.next());
        assertEquals(new Token(NAME, "defg"), t.next());
        t.backUp();
        t.backUp();
        assertEquals(new Token(NAME, "defg"), t.next());
        assertEquals(new Token(NAME, "hi"), t.next());
    }

}
