package conf

import "testing"

// Test to make sure we get what we expect.
func expect(t *testing.T, lx *lexer, items []item) {
	t.Helper()
	for i := 0; i < len(items); i++ {
		item := lx.nextItem()
		_ = item.String()
		if item.typ == itemEOF {
			break
		}
		if item != items[i] {
			t.Fatalf("Testing: '%s'\nExpected %q, received %q\n",
				lx.input, items[i], item)
		}
		if item.typ == itemError {
			break
		}
	}
}

func TestPlainValue(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo")
	expect(t, lx, expectedItems)
}

func TestSimpleKeyStringValues(t *testing.T) {
	// Double quotes
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "bar", 1, 7},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = \"bar\"")
	expect(t, lx, expectedItems)

	// Single quotes
	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemString, "bar", 1, 7},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = 'bar'")
	expect(t, lx, expectedItems)

	// No spaces
	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemString, "bar", 1, 5},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo='bar'")
	expect(t, lx, expectedItems)

	// NL
	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemString, "bar", 1, 5},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo='bar'\r\n")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemString, "bar", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo=\t'bar'\t")
	expect(t, lx, expectedItems)
}

func TestComplexStringValues(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "bar\\r\\n  \\t", 1, 7},
		{itemEOF, "", 2, 0},
	}

	lx := lex("foo = 'bar\\r\\n  \\t'")
	expect(t, lx, expectedItems)
}

func TestStringStartingWithNumber(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "3xyz", 1, 6},
		{itemEOF, "", 2, 0},
	}

	lx := lex(`foo = 3xyz`)
	expect(t, lx, expectedItems)

	lx = lex(`foo = 3xyz,`)
	expect(t, lx, expectedItems)

	lx = lex(`foo = 3xyz;`)
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 2, 9},
		{itemString, "3xyz", 2, 15},
		{itemEOF, "", 2, 0},
	}
	content := `
        foo = 3xyz
        `
	lx = lex(content)
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "map", 2, 9},
		{itemMapStart, "", 2, 14},
		{itemKey, "foo", 3, 11},
		{itemString, "3xyz", 3, 17},
		{itemMapEnd, "", 3, 22},
		{itemEOF, "", 2, 0},
	}
	content = `
        map {
          foo = 3xyz}
        `
	lx = lex(content)
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "map", 2, 9},
		{itemMapStart, "", 2, 14},
		{itemKey, "foo", 3, 11},
		{itemString, "3xyz", 3, 17},
		{itemMapEnd, "", 4, 10},
		{itemEOF, "", 2, 0},
	}
	content = `
        map {
          foo = 3xyz;
        }
        `
	lx = lex(content)
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "map", 2, 9},
		{itemMapStart, "", 2, 14},
		{itemKey, "foo", 3, 11},
		{itemString, "3xyz", 3, 17},
		{itemKey, "bar", 4, 11},
		{itemString, "4wqs", 4, 17},
		{itemMapEnd, "", 5, 10},
		{itemEOF, "", 2, 0},
	}
	content = `
        map {
          foo = 3xyz,
          bar = 4wqs
        }
        `
	lx = lex(content)
	expect(t, lx, expectedItems)
}

func TestBinaryString(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "e", 1, 9},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = \\x65")
	expect(t, lx, expectedItems)
}

func TestBinaryStringLatin1(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "\xe9", 1, 9},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = \\xe9")
	expect(t, lx, expectedItems)
}

func TestSimpleKeyIntegerValues(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = 123")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 4},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo=123")
	expect(t, lx, expectedItems)
	lx = lex("foo=123\r\n")
	expect(t, lx, expectedItems)
}

func TestSimpleKeyNegativeIntegerValues(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "-123", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = -123")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "-123", 1, 4},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo=-123")
	expect(t, lx, expectedItems)
	lx = lex("foo=-123\r\n")
	expect(t, lx, expectedItems)
}

func TestConvenientIntegerValues(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "1k", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = 1k")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "1K", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = 1K")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "1m", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = 1m")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "1M", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = 1M")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "1g", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = 1g")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "1G", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = 1G")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "1MB", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = 1MB")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "1Gb", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = 1Gb")
	expect(t, lx, expectedItems)

	// Negative versions
	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "-1m", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = -1m")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "-1GB", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = -1GB ")
	expect(t, lx, expectedItems)
}

func TestSimpleKeyFloatValues(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemFloat, "22.2", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = 22.2")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemFloat, "22.2", 1, 4},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo=22.2")
	expect(t, lx, expectedItems)
	lx = lex("foo=22.2\r\n")
	expect(t, lx, expectedItems)
}

func TestBadBinaryStringEndingAfterZeroHexChars(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemError, "Expected two hexadecimal digits after '\\x', but hit end of line", 2, 1},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = xyz\\x\n")
	expect(t, lx, expectedItems)
}

func TestBadBinaryStringEndingAfterOneHexChar(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemError, "Expected two hexadecimal digits after '\\x', but hit end of line", 2, 1},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = xyz\\xF\n")
	expect(t, lx, expectedItems)
}

func TestBadBinaryStringWithZeroHexChars(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemError, "Expected two hexadecimal digits after '\\x', but got ']\"'", 1, 12},
		{itemEOF, "", 1, 0},
	}
	lx := lex(`foo = "[\x]"`)
	expect(t, lx, expectedItems)
}

func TestBadBinaryStringWithOneHexChar(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemError, "Expected two hexadecimal digits after '\\x', but got 'e]'", 1, 12},
		{itemEOF, "", 1, 0},
	}
	lx := lex(`foo = "[\xe]"`)
	expect(t, lx, expectedItems)
}

func TestBadFloatValues(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemError, "Floats must start with a digit", 1, 7},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = .2")
	expect(t, lx, expectedItems)
}

func TestBadKey(t *testing.T) {
	expectedItems := []item{
		{itemError, "Unexpected key separator ':'", 1, 1},
		{itemEOF, "", 1, 0},
	}
	lx := lex(" :foo = 22")
	expect(t, lx, expectedItems)
}

func TestSimpleKeyBoolValues(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemBool, "true", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = true")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemBool, "true", 1, 4},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo=true")
	expect(t, lx, expectedItems)
	lx = lex("foo=true\r\n")
	expect(t, lx, expectedItems)
}

func TestComments(t *testing.T) {
	expectedItems := []item{
		{itemCommentStart, "", 1, 1},
		{itemText, " This is a comment", 1, 1},
		{itemEOF, "", 1, 0},
	}
	lx := lex("# This is a comment")
	expect(t, lx, expectedItems)
	lx = lex("# This is a comment\r\n")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemCommentStart, "", 1, 2},
		{itemText, " This is a comment", 1, 2},
		{itemEOF, "", 1, 0},
	}
	lx = lex("// This is a comment\r\n")
	expect(t, lx, expectedItems)
}

func TestTopValuesWithComments(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 6},
		{itemCommentStart, "", 1, 12},
		{itemText, " This is a comment", 1, 12},
		{itemEOF, "", 1, 0},
	}

	lx := lex("foo = 123 // This is a comment")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 4},
		{itemCommentStart, "", 1, 12},
		{itemText, " This is a comment", 1, 12},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo=123    # This is a comment")
	expect(t, lx, expectedItems)
}

func TestRawString(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "bar", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = bar")
	expect(t, lx, expectedItems)
	lx = lex(`foo = bar' `)
	expect(t, lx, expectedItems)
}

func TestDateValues(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemDatetime, "2016-05-04T18:53:41Z", 1, 6},
		{itemEOF, "", 1, 0},
	}

	lx := lex("foo = 2016-05-04T18:53:41Z")
	expect(t, lx, expectedItems)
}

func TestVariableValues(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemVariable, "bar", 1, 7},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = $bar")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemVariable, "bar", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo =$bar")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemVariable, "bar", 1, 5},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo $bar")
	expect(t, lx, expectedItems)
}

func TestArrays(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemArrayStart, "", 1, 7},
		{itemInteger, "1", 1, 7},
		{itemInteger, "2", 1, 10},
		{itemInteger, "3", 1, 13},
		{itemString, "bar", 1, 17},
		{itemArrayEnd, "", 1, 22},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = [1, 2, 3, 'bar']")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemArrayStart, "", 1, 7},
		{itemInteger, "1", 1, 7},
		{itemInteger, "2", 1, 9},
		{itemInteger, "3", 1, 11},
		{itemString, "bar", 1, 14},
		{itemArrayEnd, "", 1, 19},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = [1,2,3,'bar']")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemArrayStart, "", 1, 7},
		{itemInteger, "1", 1, 7},
		{itemInteger, "2", 1, 10},
		{itemInteger, "3", 1, 12},
		{itemString, "bar", 1, 15},
		{itemArrayEnd, "", 1, 20},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo = [1, 2,3,'bar']")
	expect(t, lx, expectedItems)
}

var mlArray = `
# top level comment
foo = [
 1, # One
 2, // Two
 3 # Three
 'bar'     ,
 "bar"
]
`

func TestMultilineArrays(t *testing.T) {
	expectedItems := []item{
		{itemCommentStart, "", 2, 2},
		{itemText, " top level comment", 2, 2},
		{itemKey, "foo", 3, 1},
		{itemArrayStart, "", 3, 8},
		{itemInteger, "1", 4, 2},
		{itemCommentStart, "", 4, 6},
		{itemText, " One", 4, 6},
		{itemInteger, "2", 5, 2},
		{itemCommentStart, "", 5, 7},
		{itemText, " Two", 5, 7},
		{itemInteger, "3", 6, 2},
		{itemCommentStart, "", 6, 5},
		{itemText, " Three", 6, 5},
		{itemString, "bar", 7, 3},
		{itemString, "bar", 8, 3},
		{itemArrayEnd, "", 9, 2},
		{itemEOF, "", 9, 0},
	}
	lx := lex(mlArray)
	expect(t, lx, expectedItems)
}

var mlArrayNoSep = `
# top level comment
foo = [
 1 // foo
 2
 3
 'bar'
 "bar"
]
`

func TestMultilineArraysNoSep(t *testing.T) {
	expectedItems := []item{
		{itemCommentStart, "", 2, 2},
		{itemText, " top level comment", 2, 2},
		{itemKey, "foo", 3, 1},
		{itemArrayStart, "", 3, 8},
		{itemInteger, "1", 4, 2},
		{itemCommentStart, "", 4, 6},
		{itemText, " foo", 4, 6},
		{itemInteger, "2", 5, 2},
		{itemInteger, "3", 6, 2},
		{itemString, "bar", 7, 3},
		{itemString, "bar", 8, 3},
		{itemArrayEnd, "", 9, 2},
		{itemEOF, "", 9, 0},
	}
	lx := lex(mlArrayNoSep)
	expect(t, lx, expectedItems)
}

func TestSimpleMap(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemMapStart, "", 1, 7},
		{itemKey, "ip", 1, 7},
		{itemString, "127.0.0.1", 1, 11},
		{itemKey, "port", 1, 23},
		{itemInteger, "4242", 1, 30},
		{itemMapEnd, "", 1, 35},
		{itemEOF, "", 1, 0},
	}

	lx := lex("foo = {ip='127.0.0.1', port = 4242}")
	expect(t, lx, expectedItems)
}

var mlMap = `
foo = {
  ip = '127.0.0.1' # the IP
  port= 4242 // the port
}
`

func TestMultilineMap(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 2, 1},
		{itemMapStart, "", 2, 8},
		{itemKey, "ip", 3, 3},
		{itemString, "127.0.0.1", 3, 9},
		{itemCommentStart, "", 3, 21},
		{itemText, " the IP", 3, 21},
		{itemKey, "port", 4, 3},
		{itemInteger, "4242", 4, 9},
		{itemCommentStart, "", 4, 16},
		{itemText, " the port", 4, 16},
		{itemMapEnd, "", 5, 2},
		{itemEOF, "", 5, 0},
	}

	lx := lex(mlMap)
	expect(t, lx, expectedItems)
}

var nestedMap = `
foo = {
  host = {
    ip = '127.0.0.1'
    port= 4242
  }
}
`

func TestNestedMaps(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 2, 1},
		{itemMapStart, "", 2, 8},
		{itemKey, "host", 3, 3},
		{itemMapStart, "", 3, 11},
		{itemKey, "ip", 4, 5},
		{itemString, "127.0.0.1", 4, 11},
		{itemKey, "port", 5, 5},
		{itemInteger, "4242", 5, 11},
		{itemMapEnd, "", 6, 4},
		{itemMapEnd, "", 7, 2},
		{itemEOF, "", 7, 0},
	}

	lx := lex(nestedMap)
	expect(t, lx, expectedItems)
}

func TestQuotedKeys(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo : 123")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 1},
		{itemInteger, "123", 1, 8},
		{itemEOF, "", 1, 0},
	}
	lx = lex("'foo' : 123")
	expect(t, lx, expectedItems)
	lx = lex("\"foo\" : 123")
	expect(t, lx, expectedItems)
}

func TestQuotedKeysWithSpace(t *testing.T) {
	expectedItems := []item{
		{itemKey, " foo", 1, 1},
		{itemInteger, "123", 1, 9},
		{itemEOF, "", 1, 0},
	}
	lx := lex("' foo' : 123")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, " foo", 1, 1},
		{itemInteger, "123", 1, 9},
		{itemEOF, "", 1, 0},
	}
	lx = lex("\" foo\" : 123")
	expect(t, lx, expectedItems)
}

func TestColonKeySep(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo : 123")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 4},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo:123")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 5},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo: 123")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 6},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo:  123\r\n")
	expect(t, lx, expectedItems)
}

func TestWhitespaceKeySep(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 4},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo 123")
	expect(t, lx, expectedItems)
	lx = lex("foo 123")
	expect(t, lx, expectedItems)
	lx = lex("foo\t123")
	expect(t, lx, expectedItems)
	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemInteger, "123", 1, 5},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo\t\t123\r\n")
	expect(t, lx, expectedItems)
}

var escString = `
foo  = \t
bar  = \r
baz  = \n
q    = \"
bs   = \\
`

func TestEscapedString(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 2, 1},
		{itemString, "\t", 2, 9},
		{itemKey, "bar", 3, 1},
		{itemString, "\r", 3, 9},
		{itemKey, "baz", 4, 1},
		{itemString, "\n", 4, 9},
		{itemKey, "q", 5, 1},
		{itemString, "\"", 5, 9},
		{itemKey, "bs", 6, 1},
		{itemString, "\\", 6, 9},
		{itemEOF, "", 6, 0},
	}
	lx := lex(escString)
	expect(t, lx, expectedItems)
}

func TestCompoundStringES(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "\\end", 1, 8},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = "\\end"`)
	expect(t, lx, expectedItems)
}

func TestCompoundStringSE(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "start\\", 1, 8},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = "start\\"`)
	expect(t, lx, expectedItems)
}

func TestCompoundStringEE(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "Eq", 1, 12},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = \x45\x71`)
	expect(t, lx, expectedItems)
}

func TestCompoundStringSEE(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "startEq", 1, 12},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = start\x45\x71`)
	expect(t, lx, expectedItems)
}

func TestCompoundStringSES(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "start|end", 1, 9},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = start\x7Cend`)
	expect(t, lx, expectedItems)
}

func TestCompoundStringEES(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "<>end", 1, 12},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = \x3c\x3eend`)
	expect(t, lx, expectedItems)
}

func TestCompoundStringESE(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "<middle>", 1, 12},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = \x3cmiddle\x3E`)
	expect(t, lx, expectedItems)
}

func TestBadStringEscape(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemError, "Invalid escape character 'y'. Only the following escape characters are allowed: \\xXX, \\t, \\n, \\r, \\\", \\\\.", 1, 8},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = \y`)
	expect(t, lx, expectedItems)
}

func TestNonBool(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "\\true", 1, 7},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = \\true`)
	expect(t, lx, expectedItems)
}

func TestNonVariable(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "\\$var", 1, 7},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = \\$var`)
	expect(t, lx, expectedItems)
}

func TestEmptyStringDQ(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "", 1, 7},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = ""`)
	expect(t, lx, expectedItems)
}

func TestEmptyStringSQ(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "", 1, 7},
		{itemEOF, "", 2, 0},
	}
	lx := lex(`foo = ''`)
	expect(t, lx, expectedItems)
}

var nestedWhitespaceMap = `
foo  {
  host  {
    ip = '127.0.0.1'
    port= 4242
  }
}
`

func TestNestedWhitespaceMaps(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 2, 1},
		{itemMapStart, "", 2, 7},
		{itemKey, "host", 3, 3},
		{itemMapStart, "", 3, 10},
		{itemKey, "ip", 4, 5},
		{itemString, "127.0.0.1", 4, 11},
		{itemKey, "port", 5, 5},
		{itemInteger, "4242", 5, 11},
		{itemMapEnd, "", 6, 4},
		{itemMapEnd, "", 7, 2},
		{itemEOF, "", 7, 0},
	}

	lx := lex(nestedWhitespaceMap)
	expect(t, lx, expectedItems)
}

var semicolons = `
foo = 123;
bar = 'baz';
baz = 'boo'
map {
 id = 1;
}
`

func TestOptionalSemicolons(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 2, 1},
		{itemInteger, "123", 2, 7},
		{itemKey, "bar", 3, 1},
		{itemString, "baz", 3, 8},
		{itemKey, "baz", 4, 1},
		{itemString, "boo", 4, 8},
		{itemKey, "map", 5, 1},
		{itemMapStart, "", 5, 6},
		{itemKey, "id", 6, 2},
		{itemInteger, "1", 6, 7},
		{itemMapEnd, "", 7, 2},
		{itemEOF, "", 8, 0},
	}

	lx := lex(semicolons)
	expect(t, lx, expectedItems)
}

func TestSemicolonChaining(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemString, "1", 1, 5},
		{itemKey, "bar", 1, 9},
		{itemFloat, "2.2", 1, 13},
		{itemKey, "baz", 1, 18},
		{itemBool, "true", 1, 22},
		{itemEOF, "", 1, 0},
	}

	lx := lex("foo='1'; bar=2.2; baz=true;")
	expect(t, lx, expectedItems)
}

var noquotes = `
foo = 123
bar = baz
baz=boo
map {
 id:one
 id2 : onetwo
}
t true
f false
tstr "true"
tkey = two
fkey = five # This should be a string
`

func TestNonQuotedStrings(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 2, 1},
		{itemInteger, "123", 2, 7},
		{itemKey, "bar", 3, 1},
		{itemString, "baz", 3, 7},
		{itemKey, "baz", 4, 1},
		{itemString, "boo", 4, 5},
		{itemKey, "map", 5, 1},
		{itemMapStart, "", 5, 6},
		{itemKey, "id", 6, 2},
		{itemString, "one", 6, 5},
		{itemKey, "id2", 7, 2},
		{itemString, "onetwo", 7, 8},
		{itemMapEnd, "", 8, 2},
		{itemKey, "t", 9, 1},
		{itemBool, "true", 9, 3},
		{itemKey, "f", 10, 1},
		{itemBool, "false", 10, 3},
		{itemKey, "tstr", 11, 1},
		{itemString, "true", 11, 7},
		{itemKey, "tkey", 12, 1},
		{itemString, "two", 12, 8},
		{itemKey, "fkey", 13, 1},
		{itemString, "five", 13, 8},
		{itemCommentStart, "", 13, 14},
		{itemText, " This should be a string", 13, 14},
		{itemEOF, "", 14, 0},
	}
	lx := lex(noquotes)
	expect(t, lx, expectedItems)
}

var danglingquote = `
listen: "localhost:4242

http: localhost:8222
`

func TestDanglingQuotedString(t *testing.T) {
	expectedItems := []item{
		{itemKey, "listen", 2, 1},
		{itemError, "Unexpected EOF.", 5, 1},
	}
	lx := lex(danglingquote)
	expect(t, lx, expectedItems)
}

var keydanglingquote = `
foo = "
listen: "

http: localhost:8222

"
`

func TestKeyDanglingQuotedString(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 2, 1},
		{itemString, "\nlisten: ", 3, 8},
		{itemKey, "http", 5, 1},
		{itemString, "localhost:8222", 5, 7},
		{itemError, "Unexpected EOF.", 8, 1},
	}
	lx := lex(keydanglingquote)
	expect(t, lx, expectedItems)
}

var danglingsquote = `
listen: 'localhost:4242

http: localhost:8222
`

func TestDanglingSingleQuotedString(t *testing.T) {
	expectedItems := []item{
		{itemKey, "listen", 2, 1},
		{itemError, "Unexpected EOF.", 5, 1},
	}
	lx := lex(danglingsquote)
	expect(t, lx, expectedItems)
}

var keydanglingsquote = `
foo = '
listen: '

http: localhost:8222

'
`

func TestKeyDanglingSingleQuotedString(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 2, 1},
		{itemString, "\nlisten: ", 3, 8},
		{itemKey, "http", 5, 1},
		{itemString, "localhost:8222", 5, 7},
		{itemError, "Unexpected EOF.", 8, 1},
	}
	lx := lex(keydanglingsquote)
	expect(t, lx, expectedItems)
}

var mapdanglingbracket = `
listen = 4222

cluster = {

  foo = bar

`

func TestMapDanglingBracket(t *testing.T) {
	expectedItems := []item{
		{itemKey, "listen", 2, 1},
		{itemInteger, "4222", 2, 10},
		{itemKey, "cluster", 4, 1},
		{itemMapStart, "", 4, 12},
		{itemKey, "foo", 6, 3},
		{itemString, "bar", 6, 9},
		{itemError, "Unexpected EOF processing map.", 8, 1},
	}
	lx := lex(mapdanglingbracket)
	expect(t, lx, expectedItems)
}

var blockdanglingparens = `
listen = 4222

quote = (

  foo = bar

`

func TestBlockDanglingParens(t *testing.T) {
	expectedItems := []item{
		{itemKey, "listen", 2, 1},
		{itemInteger, "4222", 2, 10},
		{itemKey, "quote", 4, 1},
		{itemError, "Unexpected EOF processing block.", 8, 1},
	}
	lx := lex(blockdanglingparens)
	expect(t, lx, expectedItems)
}

func TestMapQuotedKeys(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemMapStart, "", 1, 7},
		{itemKey, "bar", 1, 8},
		{itemInteger, "4242", 1, 15},
		{itemMapEnd, "", 1, 20},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = {'bar' = 4242}")
	expect(t, lx, expectedItems)
	lx = lex("foo = {\"bar\" = 4242}")
	expect(t, lx, expectedItems)
}

func TestSpecialCharsMapQuotedKeys(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemMapStart, "", 1, 7},
		{itemKey, "bar-1.2.3", 1, 8},
		{itemMapStart, "", 1, 22},
		{itemKey, "port", 1, 23},
		{itemInteger, "4242", 1, 28},
		{itemMapEnd, "", 1, 34},
		{itemMapEnd, "", 1, 35},
		{itemEOF, "", 1, 0},
	}
	lx := lex("foo = {'bar-1.2.3' = { port:4242 }}")
	expect(t, lx, expectedItems)
	lx = lex("foo = {\"bar-1.2.3\" = { port:4242 }}")
	expect(t, lx, expectedItems)
}

var mlnestedmap = `
systems {
  allinone {
    description: "This is a description."
  }
}
`

func TestDoubleNestedMapsNewLines(t *testing.T) {
	expectedItems := []item{
		{itemKey, "systems", 2, 1},
		{itemMapStart, "", 2, 10},
		{itemKey, "allinone", 3, 3},
		{itemMapStart, "", 3, 13},
		{itemKey, "description", 4, 5},
		{itemString, "This is a description.", 4, 19},
		{itemMapEnd, "", 5, 4},
		{itemMapEnd, "", 6, 2},
		{itemEOF, "", 7, 0},
	}
	lx := lex(mlnestedmap)
	expect(t, lx, expectedItems)
}

var blockexample = `
numbers (
1234567890
)
`

func TestBlockString(t *testing.T) {
	expectedItems := []item{
		{itemKey, "numbers", 2, 1},
		{itemString, "\n1234567890\n", 4, 10},
	}
	lx := lex(blockexample)
	expect(t, lx, expectedItems)
}

func TestBlockStringEOF(t *testing.T) {
	expectedItems := []item{
		{itemKey, "numbers", 2, 1},
		{itemString, "\n1234567890\n", 4, 10},
	}
	blockbytes := []byte(blockexample[0 : len(blockexample)-1])
	blockbytes = append(blockbytes, 0)
	lx := lex(string(blockbytes))
	expect(t, lx, expectedItems)
}

var mlblockexample = `
numbers (
  12(34)56
  (
    7890
  )
)
`

func TestBlockStringMultiLine(t *testing.T) {
	expectedItems := []item{
		{itemKey, "numbers", 2, 1},
		{itemString, "\n  12(34)56\n  (\n    7890\n  )\n", 7, 10},
	}
	lx := lex(mlblockexample)
	expect(t, lx, expectedItems)
}

func TestUnquotedIPAddr(t *testing.T) {
	expectedItems := []item{
		{itemKey, "listen", 1, 0},
		{itemString, "127.0.0.1:4222", 1, 8},
		{itemEOF, "", 1, 0},
	}
	lx := lex("listen: 127.0.0.1:4222")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "listen", 1, 0},
		{itemString, "127.0.0.1", 1, 8},
		{itemEOF, "", 1, 0},
	}
	lx = lex("listen: 127.0.0.1")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "listen", 1, 0},
		{itemString, "apcera.me:80", 1, 8},
		{itemEOF, "", 1, 0},
	}
	lx = lex("listen: apcera.me:80")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "listen", 1, 0},
		{itemString, "nats.io:-1", 1, 8},
		{itemEOF, "", 1, 0},
	}
	lx = lex("listen: nats.io:-1")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "listen", 1, 0},
		{itemInteger, "-1", 1, 8},
		{itemEOF, "", 1, 0},
	}
	lx = lex("listen: -1")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "listen", 1, 0},
		{itemString, ":-1", 1, 8},
		{itemEOF, "", 1, 0},
	}
	lx = lex("listen: :-1")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "listen", 1, 0},
		{itemString, ":80", 1, 9},
		{itemEOF, "", 1, 0},
	}
	lx = lex("listen = :80")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "listen", 1, 0},
		{itemArrayStart, "", 1, 10},
		{itemString, "localhost:4222", 1, 10},
		{itemString, "localhost:4333", 1, 26},
		{itemArrayEnd, "", 1, 41},
		{itemEOF, "", 1, 0},
	}
	lx = lex("listen = [localhost:4222, localhost:4333]")
	expect(t, lx, expectedItems)
}

var arrayOfMaps = `
authorization {
    users = [
      {user: alice, password: foo}
      {user: bob,   password: bar}
    ]
    timeout: 0.5
}
`

func TestArrayOfMaps(t *testing.T) {
	expectedItems := []item{
		{itemKey, "authorization", 2, 1},
		{itemMapStart, "", 2, 16},
		{itemKey, "users", 3, 5},
		{itemArrayStart, "", 3, 14},
		{itemMapStart, "", 4, 8},
		{itemKey, "user", 4, 8},
		{itemString, "alice", 4, 14},
		{itemKey, "password", 4, 21},
		{itemString, "foo", 4, 31},
		{itemMapEnd, "", 4, 35},
		{itemMapStart, "", 5, 8},
		{itemKey, "user", 5, 8},
		{itemString, "bob", 5, 14},
		{itemKey, "password", 5, 21},
		{itemString, "bar", 5, 31},
		{itemMapEnd, "", 5, 35},
		{itemArrayEnd, "", 6, 6},
		{itemKey, "timeout", 7, 5},
		{itemFloat, "0.5", 7, 14},
		{itemMapEnd, "", 8, 2},
		{itemEOF, "", 9, 0},
	}
	lx := lex(arrayOfMaps)
	expect(t, lx, expectedItems)
}

func TestInclude(t *testing.T) {
	expectedItems := []item{
		{itemInclude, "users.conf", 1, 9},
		{itemEOF, "", 1, 0},
	}
	lx := lex("include \"users.conf\"")
	expect(t, lx, expectedItems)

	lx = lex("include 'users.conf'")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemInclude, "users.conf", 1, 8},
		{itemEOF, "", 1, 0},
	}
	lx = lex("include users.conf")
	expect(t, lx, expectedItems)
}

func TestMapInclude(t *testing.T) {
	expectedItems := []item{
		{itemKey, "foo", 1, 0},
		{itemMapStart, "", 1, 5},
		{itemInclude, "users.conf", 1, 14},
		{itemMapEnd, "", 1, 26},
		{itemEOF, "", 1, 0},
	}

	lx := lex("foo { include users.conf }")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemMapStart, "", 1, 5},
		{itemInclude, "users.conf", 1, 13},
		{itemMapEnd, "", 1, 24},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo {include users.conf}")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemMapStart, "", 1, 5},
		{itemInclude, "users.conf", 1, 15},
		{itemMapEnd, "", 1, 28},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo { include 'users.conf' }")
	expect(t, lx, expectedItems)

	expectedItems = []item{
		{itemKey, "foo", 1, 0},
		{itemMapStart, "", 1, 5},
		{itemInclude, "users.conf", 1, 15},
		{itemMapEnd, "", 1, 27},
		{itemEOF, "", 1, 0},
	}
	lx = lex("foo { include \"users.conf\"}")
	expect(t, lx, expectedItems)
}

func TestJSONCompat(t *testing.T) {
	for _, test := range []struct {
		name     string
		input    string
		expected []item
	}{
		{
			name: "should omit initial and final brackets at top level with a single item",
			input: `
                        {
                          "http_port": 8223
                        }
                        `,
			expected: []item{
				{itemKey, "http_port", 3, 28},
				{itemInteger, "8223", 3, 40},
			},
		},
		{
			name: "should omit trailing commas at top level with two items",
			input: `
                        {
                          "http_port": 8223,
                          "port": 4223
                        }
                        `,
			expected: []item{
				{itemKey, "http_port", 3, 28},
				{itemInteger, "8223", 3, 40},
				{itemKey, "port", 4, 28},
				{itemInteger, "4223", 4, 35},
			},
		},
		{
			name: "should omit trailing commas at top level with multiple items",
			input: `
                        {
                          "http_port": 8223,
                          "port": 4223,
                          "max_payload": "5MB",
                          "debug": true,
                          "max_control_line": 1024
                        }
                        `,
			expected: []item{
				{itemKey, "http_port", 3, 28},
				{itemInteger, "8223", 3, 40},
				{itemKey, "port", 4, 28},
				{itemInteger, "4223", 4, 35},
				{itemKey, "max_payload", 5, 28},
				{itemString, "5MB", 5, 43},
				{itemKey, "debug", 6, 28},
				{itemBool, "true", 6, 36},
				{itemKey, "max_control_line", 7, 28},
				{itemInteger, "1024", 7, 47},
			},
		},
		{
			name: "should support JSON not prettified",
			input: `{"http_port": 8224,"port": 4224}
                        `,
			expected: []item{
				{itemKey, "http_port", 1, 2},
				{itemInteger, "8224", 1, 14},
				{itemKey, "port", 1, 20},
				{itemInteger, "4224", 1, 27},
			},
		},
		{
			name: "should support JSON not prettified with final bracket after newline",
			input: `{"http_port": 8225,"port": 4225
                        }
                        `,
			expected: []item{
				{itemKey, "http_port", 1, 2},
				{itemInteger, "8225", 1, 14},
				{itemKey, "port", 1, 20},
				{itemInteger, "4225", 1, 27},
			},
		},
		{
			name: "should support uglified JSON with inner blocks",
			input: `{"http_port": 8227,"port": 4227,"write_deadline": "1h","cluster": {"port": 6222,"routes": ["nats://127.0.0.1:4222","nats://127.0.0.1:4223","nats://127.0.0.1:4224"]}}
                        `,
			expected: []item{
				{itemKey, "http_port", 1, 2},
				{itemInteger, "8227", 1, 14},
				{itemKey, "port", 1, 20},
				{itemInteger, "4227", 1, 27},
				{itemKey, "write_deadline", 1, 33},
				{itemString, "1h", 1, 51},
				{itemKey, "cluster", 1, 56},
				{itemMapStart, "", 1, 67},
				{itemKey, "port", 1, 68},
				{itemInteger, "6222", 1, 75},
				{itemKey, "routes", 1, 81},
				{itemArrayStart, "", 1, 91},
				{itemString, "nats://127.0.0.1:4222", 1, 92},
				{itemString, "nats://127.0.0.1:4223", 1, 116},
				{itemString, "nats://127.0.0.1:4224", 1, 140},
				{itemArrayEnd, "", 1, 163},
				{itemMapEnd, "", 1, 164},
			},
		},
		{
			name: "should support prettified JSON with inner blocks",
			input: `
                        {
                          "http_port": 8227,
                          "port": 4227,
                          "write_deadline": "1h",
                          "cluster": {
                            "port": 6222,
                            "routes": [
                              "nats://127.0.0.1:4222",
                              "nats://127.0.0.1:4223",
                              "nats://127.0.0.1:4224"
                            ]
                          }
                        }
                        `,
			expected: []item{
				{itemKey, "http_port", 3, 28},
				{itemInteger, "8227", 3, 40},
				{itemKey, "port", 4, 28},
				{itemInteger, "4227", 4, 35},
				{itemKey, "write_deadline", 5, 28},
				{itemString, "1h", 5, 46},
				{itemKey, "cluster", 6, 28},
				{itemMapStart, "", 6, 39},
				{itemKey, "port", 7, 30},
				{itemInteger, "6222", 7, 37},
				{itemKey, "routes", 8, 30},
				{itemArrayStart, "", 8, 40},
				{itemString, "nats://127.0.0.1:4222", 9, 32},
				{itemString, "nats://127.0.0.1:4223", 10, 32},
				{itemString, "nats://127.0.0.1:4224", 11, 32},
				{itemArrayEnd, "", 12, 30},
				{itemMapEnd, "", 13, 28},
			},
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			lx := lex(test.input)
			expect(t, lx, test.expected)
		})
	}
}
