package edu.ufl.cise.cop5555.sp12;


import java.util.StringTokenizer;

import org.junit.Test;
import static org.junit.Assert.*;
import edu.ufl.cise.cop5555.sp12.Scanner;
import edu.ufl.cise.cop5555.sp12.TokenStream;
import edu.ufl.cise.cop5555.sp12.TokenStream.IllegalStringLiteralException;
import edu.ufl.cise.cop5555.sp12.TokenStream.Token;
import edu.ufl.cise.cop5555.sp12.TokenStream.TokenStreamIterator;
import static edu.ufl.cise.cop5555.sp12.Kind.*;

/*
 * 	Testsuite: edu.ufl.cise.cop5555.sp12.TestScanner
 * 	Tests run: 130, Failures: 5, Errors: 9, Time elapsed: 6.371 sec
 */
public class TestScanner {

	// This expected values of the text of the input tokens are delineated by '~'.  Thus this method cannot
	// handle programs containing '~' in comments or strings.  Fixing this is not worth the trouble.
	private void compareText(TokenStream stream, String expected)
			throws IllegalStringLiteralException {
		StringTokenizer expectedTokenizer = new StringTokenizer(expected, "~");
		TokenStreamIterator iter = stream.iterator();
		while (iter.hasNext()) {
			assertEquals(expectedTokenizer.nextElement(), iter.next().getText());
		}
		assertFalse(expectedTokenizer.hasMoreElements());
	}

	private void compareKinds(TokenStream stream, Kind[] expected) {
		TokenStreamIterator iter = stream.iterator();
		int i = 0;
		while (iter.hasNext()) {
			assertEquals(expected[i++], iter.next().kind);
		}
		assertTrue(expected.length == i);
	}

	private void compareLineNumbers(TokenStream stream, String expected) {
		StringBuffer sb = new StringBuffer();
		for (Token t : stream.tokens) {
			if (t.kind != EOF)
				sb.append(t.getLineNumber());
		}
		String output = sb.toString();
		assertEquals(expected, output);
	}

	private TokenStream getInitializedTokenStream(String input) {
		TokenStream stream = new TokenStream(input);
		Scanner s = new Scanner(stream);
		s.scan();
		return stream;
	}

	@Test
	public void testScan0() throws IllegalStringLiteralException {
		String input = "\u001a";  //control z at end of file should be ignored, and yield just an EOF token
		TokenStream stream = getInitializedTokenStream(input);
		String expectedText = "EOF";
		Kind[] expectedKinds = { EOF };
		compareText(stream, expectedText);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan1() throws IllegalStringLiteralException {
		String input = "";  //empty input should yield and EOF token
		String expected = "EOF";
		Kind[] expectedKinds = { EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan2() throws IllegalStringLiteralException {
		String input = "prog";  //prog is a keyword, should see a PROG token with text prog, followed by EOF
		String expected = "prog~EOF";
		Kind[] expectedKinds = { PROG, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan3() throws IllegalStringLiteralException {
		String input = "xyz";  //this is an identifier with text xyz followed by an EOF token
		String expected = "xyz~EOF";
		Kind[] expectedKinds = { IDENTIFIER, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan4() throws IllegalStringLiteralException {
		String input = "0";
		String expected = "0~EOF";
		Kind[] expectedKinds = { INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan5() throws IllegalStringLiteralException {
		String input = "01";  //this is a 0 token followed by a 1 token followed by an EOF token.  01 is not legal in the language,
		  //but the error will be found by the parser, not the scanner.
		String expected = "0~1~EOF";
		Kind[] expectedKinds = { INTEGER_LITERAL, INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
		Kind[] kindarray = { INTEGER_LITERAL, INTEGER_LITERAL, EOF };
		compareKinds(stream, kindarray);
	}
	@Test
	public void testScan6() throws IllegalStringLiteralException {
		String input = "10 ";
		String expected = "10~EOF";
		Kind[] expectedKinds = { INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan7() throws IllegalStringLiteralException {
		String input = "10 11 xyz 12";
		String expected = "10~11~xyz~12~EOF";
		Kind[] expectedKinds = { INTEGER_LITERAL, INTEGER_LITERAL, IDENTIFIER,
				INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan8() throws IllegalStringLiteralException {
		String input = "10 \n 11\nxyz \n12";
		String expected = "10~11~xyz~12~EOF";
		Kind[] expectedKinds = { INTEGER_LITERAL, INTEGER_LITERAL, IDENTIFIER,
				INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan9() throws IllegalStringLiteralException {
		String input = "10 \r 11\rxyz \r12";
		String expected = "10~11~xyz~12~EOF";
		Kind[] expectedKinds = { INTEGER_LITERAL, INTEGER_LITERAL, IDENTIFIER,
				INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan10() throws IllegalStringLiteralException {
		String input = "10 \n\r 11\n\rxyz \n\r12";
		String expected = "10~11~xyz~12~EOF";
		Kind[] expectedKinds = { INTEGER_LITERAL, INTEGER_LITERAL, IDENTIFIER,
				INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan11() throws IllegalStringLiteralException {
		String input = "\n\r10 \n\r 11\n\rxyz \n\r12";
		String expected = "10~11~xyz~12~EOF";
		Kind[] expectedKinds = { INTEGER_LITERAL, INTEGER_LITERAL, IDENTIFIER,
				INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan12() throws IllegalStringLiteralException {
		String input = "\n\r  10 \n\r 11\n\rxyz \n\r12\n\r";
		String expected = "10~11~xyz~12~EOF";
		Kind[] expectedKinds = { INTEGER_LITERAL, INTEGER_LITERAL, IDENTIFIER,
				INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan13() throws IllegalStringLiteralException {
		String input = "\r  10 \n\r 11\n\rxyz \n\r12";
		String expected = "10~11~xyz~12~EOF";
		Kind[] expectedKinds = { INTEGER_LITERAL, INTEGER_LITERAL, IDENTIFIER,
				INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan14() throws IllegalStringLiteralException {
		String input = "\n  \n\n ";
		String expected = "EOF";
		Kind[] expectedKinds = { EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan15() throws IllegalStringLiteralException {
		String input = "xyz,123,x12,==";
		String expected = "xyz~,~123~,~x12~,~==~EOF";
		Kind[] expectedKinds = { IDENTIFIER, COMMA, INTEGER_LITERAL, COMMA,
				IDENTIFIER, COMMA, EQUALS, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan16() throws IllegalStringLiteralException {
		String input = "<=<==>";
		String expected = "<=~<=~=~>~EOF";
		Kind[] expectedKinds = { AT_MOST, AT_MOST, ASSIGN, GREATER_THAN, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan17() throws IllegalStringLiteralException {
		String input = "true+false";
		String expected = "true~+~false~EOF";
		Kind[] expectedKinds = { BOOLEAN_LITERAL, PLUS, BOOLEAN_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan18() throws IllegalStringLiteralException {
		String input = "true##+#+##false";
		String expected = "true~false~EOF";
		Kind[] expectedKinds = { BOOLEAN_LITERAL, BOOLEAN_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
	@Test
	public void testScan19() throws IllegalStringLiteralException {
		String input = "true##+##+##false";
		String expected = "true~+~%###false%#~EOF";
		Kind[] expectedKinds = { BOOLEAN_LITERAL, PLUS, MALFORMED_COMMENT, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		compareText(stream, expected);
		compareKinds(stream, expectedKinds);
	}
    @Test
    public void testScan20() throws IllegalStringLiteralException
    {
        String input = " a ";
        String expected = "a~EOF";
        Kind[] expectedKinds = { IDENTIFIER, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
    }
    @Test
    public void testScan21() throws IllegalStringLiteralException
    {
        String input = "\"ab\t\"";
        Kind[] expectedKinds = { STRING_LITERAL, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        // compareText(stream, expected);
        compareKinds(stream, expectedKinds);
    }
    @Test
    public void testScan22() throws IllegalStringLiteralException
    {
        String input = "\"ab\\n\"";
        Kind[] expectedKinds = { STRING_LITERAL, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        // compareText(stream, expected);
        compareKinds(stream, expectedKinds);
    }
    @Test
    public void testScan23() throws IllegalStringLiteralException
    {
        String input = "\"ab\\n\"";
        Kind[] expectedKinds = { STRING_LITERAL, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        // compareText(stream, expected);
        compareKinds(stream, expectedKinds);
    }
    @Test
    public void testScan24() throws IllegalStringLiteralException
    {
        String input = "\"ab\\\\\"";
        Kind[] expectedKinds = { STRING_LITERAL, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        // compareText(stream, expected);
        compareKinds(stream, expectedKinds);
    }
    @Test
    public void testScan25() throws IllegalStringLiteralException
    {
        String input = "\"ab\\r\"";
        Kind[] expectedKinds = { STRING_LITERAL, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        // compareText(stream, expected);
        compareKinds(stream, expectedKinds);
    }
    @Test
    public void testScan26() throws IllegalStringLiteralException
    {
        String input = "1=2==3#k##  \\ ##";
        // String expected = "1~=~2~==~3~#~k";
        Kind[] expectedKinds = { INTEGER_LITERAL, ASSIGN, INTEGER_LITERAL,
                EQUALS, INTEGER_LITERAL, MALFORMED_COMMENT, IDENTIFIER, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        // compareText(stream, expected);
        compareKinds(stream, expectedKinds);
    }
    @Test
    public void testScan27() throws IllegalStringLiteralException
    {
        String input = "\n\n\nabc";
        String expected = "abc~EOF";
        Kind[] expectedKinds = { IDENTIFIER, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "4";
        compareLineNumbers(stream, expectedLineNums);
    }
    //================================================================KHOOSATH BUDDHI CASES======================
    @Test
    public void testScan28() throws IllegalStringLiteralException
    {
        String input = "#(new)#";
        //String expected = "abc~EOF";
        Kind[] expectedKinds = { MALFORMED_COMMENT, LEFT_PAREN, IDENTIFIER, RIGHT_PAREN, MALFORMED_COMMENT, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        //compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "11111";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan29() throws IllegalStringLiteralException
    {
        String input = "\"this is a simple string literal\"";
        String expected = "this is a simple string literal~EOF";
        Kind[] expectedKinds = { STRING_LITERAL, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "1";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan30() throws IllegalStringLiteralException
    {
        String input = "\"this is a simple\\nstring literal\"";
        String expected = "this is a simple\nstring literal~EOF";
        Kind[] expectedKinds = { STRING_LITERAL, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "1";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan31() throws IllegalStringLiteralException
    {
        String input = " ";
        String expected = "EOF";
        Kind[] expectedKinds = { EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        //String expectedLineNums = "1";
        //compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan32() throws IllegalStringLiteralException
    {
        String input = " \"this is a simple\\n\\rstring literal\"";
        String expected = "this is a simple\n\rstring literal~EOF";
        Kind[] expectedKinds = { STRING_LITERAL,EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "1";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan33() throws IllegalStringLiteralException
    {
        String input = " \"this is a simple\\r\\nstring literal\"";
        String expected = "this is a simple\r\nstring literal~EOF";
        Kind[] expectedKinds = { STRING_LITERAL,EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "1";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan34() throws IllegalStringLiteralException
    {
        String input = "\"simple\tstring literal\"";
        String expected = "simple	string literal~EOF";
        Kind[] expectedKinds = {STRING_LITERAL, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "1";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan35() throws IllegalStringLiteralException
    {
        String input = " \"this is a simple\\tstring literal\"";
        String expected = "this is a simple	string literal~EOF";
        Kind[] expectedKinds = { STRING_LITERAL,EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "1";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan36() throws IllegalStringLiteralException
    {
        String input = " \"this is a simple\\fstring literal\"";
        String expected = "this is a simplestring literal~EOF";
        Kind[] expectedKinds = { STRING_LITERAL, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "1";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan37() throws IllegalStringLiteralException
    {
        String input = "\"this is a simple\\rstring literal\"";
        String expected = "this is a simple\rstring literal~EOF";
        Kind[] expectedKinds = { STRING_LITERAL, EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "1";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan38() throws IllegalStringLiteralException
    {
        String input = "\"s\n\ns\"";
        String expected = "%#\"s\n%#~s~%#\"%#~EOF";
        Kind[] expectedKinds = {MALFORMED_STRING, IDENTIFIER, MALFORMED_STRING,EOF};
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "122";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan39() throws IllegalStringLiteralException
    {
        String input = "\"s\ns\"";
        String expected = "%#\"s\n%#~s~%#\"%#~EOF";
        Kind[] expectedKinds = {MALFORMED_STRING, IDENTIFIER, MALFORMED_STRING,EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "111";
        compareLineNumbers(stream, expectedLineNums);
    }
    @Test
    public void testScan40() throws IllegalStringLiteralException
    {
        String input = "\"abc\\def\nrad\"abc"; //"abc\def\nrad"abc
        String expected = "%#\"abc\\%#~def~rad~%#\"abc%#~EOF";
        Kind[] expectedKinds = { MALFORMED_STRING,IDENTIFIER,IDENTIFIER,MALFORMED_STRING,EOF };
        TokenStream stream = getInitializedTokenStream(input);
        compareText(stream, expected);
        compareKinds(stream, expectedKinds);
        String expectedLineNums = "1122";
        compareLineNumbers(stream, expectedLineNums);
    }

	//===========================================================================================================
	@Test
	public void testErrorInput0() throws IllegalStringLiteralException {
		String input = "abc def @ 123"; 
		Kind[] expectedKinds = { IDENTIFIER, IDENTIFIER, ILLEGAL_CHAR, INTEGER_LITERAL, EOF };
		TokenStream stream = getInitializedTokenStream(input);
		// compareText(stream, expected); We won't worry about exactly what text goes into error tokens.
		compareKinds(stream, expectedKinds);
	}
	
	@Test 
	public void testErrorInput1() throws IllegalStringLiteralException {
		String input = "\"abc\\\"";  //This is tricky.  Removing the escapes in the String needed for Java, we have input "abc\"
		                             //including the quotes.  "abc\" is the what we would have if the input were going to be read
		                             // from a file.  However, the final quote is escaped so it is a quote within the string
		                             //
		Kind[] expectedKinds = {MALFORMED_STRING, EOF};
		TokenStream stream = getInitializedTokenStream(input);
		compareKinds(stream, expectedKinds);
	}


	@Test
	public void testLineNumbers0() {
		String input = "abd\ndef++a123\nabd";
		TokenStream stream = getInitializedTokenStream(input);
		String expected = "122223";
		compareLineNumbers(stream, expected);
	}
	
	@Test
	public void testErrorInput2() throws IllegalStringLiteralException {
		String input =   "abc <ctrl-z> bdec";
		Kind[] expectedKinds = {IDENTIFIER, LESS_THAN, IDENTIFIER, MINUS, IDENTIFIER, GREATER_THAN, IDENTIFIER,  EOF };
		TokenStream stream = getInitializedTokenStream(input);
		// compareText(stream, expected); We won't worry about exactly what text goes into error tokens.
		compareKinds(stream, expectedKinds);
	}
	

}

