package scanner;

import static org.junit.Assert.assertTrue;
import static testutil.TestUtilities.createInputStreamFromString;

import java.io.InputStream;

import org.junit.Test;

import file.FilePosition;
import token.Token;

public class LispScannerLineColumnTest {

    private static class LineColumn {

        private int line;
        private int column;

        public static LineColumn create(int line, int column) {
            LineColumn lineColumn = new LineColumn();
            lineColumn.line = line;
            lineColumn.column = column;

            return lineColumn;
        }

        public boolean isEqual(FilePosition position) {
            return (this.line == position.getLineNumber()) && (this.column == position.getColumnNumber());
        }
    }

    private void assertTokenLineAndColumnsMatch(String input, LineColumn[] expectedLineColumnList) {
        InputStream stringInputStream = createInputStreamFromString(input);
        LispScanner lispScanner = new LispScanner(stringInputStream, "testFile");

        for (LineColumn lineColumn : expectedLineColumnList) {
            Token nextToken = lispScanner.getNextToken();
            assertTrue(lineColumn.isEqual(nextToken.getPosition()));
        }
    }

    @Test
    public void givenNothing_RecordsCorrectEofLocation() {
        String input = "";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 0) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

    @Test
    public void givenSimpleString_RecordsCorrectLocation() {
        String input = "\"string\"";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 1) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

    @Test
    public void givenStringWithTrailingSpace_RecordsCorrectLocation() {
        String input = "\"string\" ";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 1) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

    @Test
    public void givenIdentifier_RecordsCorrectLocation() {
        String input = "identifier";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 1) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

    @Test
    public void givenIdentifierWithTrailingSpace_RecordsCorrectLocation() {
        String input = "identifier ";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 1) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

    @Test
    public void givenNumber_RecordsCorrectLocation() {
        String input = "123456789";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 1) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

    @Test
    public void givenNumberWithTrailingSpace_RecordsCorrectLocation() {
        String input = "123456789 ";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 1) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

    @Test
    public void givenMultipleStrings_RecordsCorrectLocations() {
        String input = "\"string1\" \n \"string2 \n with newline\" \n  \"string3\"";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 1), LineColumn.create(2, 2),
                                                 LineColumn.create(4, 3) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

    @Test
    public void givenQuotedList_RecordsCorrectLocations() {
        String input = "'(1 2 3 4 5)";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 1), LineColumn.create(1, 2),
                                                 LineColumn.create(1, 3), LineColumn.create(1, 5),
                                                 LineColumn.create(1, 7), LineColumn.create(1, 9),
                                                 LineColumn.create(1, 11), LineColumn.create(1, 12) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

    @Test
    public void givenListSpanningMultipleLines_RecordsCorrectLocations() {
        String input = " ( 1 2 \n 3 4 \n5 ) ";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 2), LineColumn.create(1, 4),
                                                 LineColumn.create(1, 6), LineColumn.create(2, 2),
                                                 LineColumn.create(2, 4), LineColumn.create(3, 1),
                                                 LineColumn.create(3, 3) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

    @Test
    public void givenCommentImmediatelyFollowingNumber_RecordsCorrectLocations() {
        String input = "12;comment\n34";
        LineColumn[] expectedLinesAndColumns = { LineColumn.create(1, 1), LineColumn.create(2, 1) };

        assertTokenLineAndColumnsMatch(input, expectedLinesAndColumns);
    }

}
