package parse

import (
	"git.makaay.nl/mauricem/go-parsekit/parse"
	"git.makaay.nl/mauricem/go-toml/ast"
)

// Arrays are square brackets with values inside. Whitespace is ignored.
// Elements are separated by commas. Data types may not be mixed (different
// ways to define strings should be considered the same type, and so should
// arrays with different element types).
//
//     arr1 = [ 1, 2, 3 ]
//     arr2 = [ "red", "yellow", "green" ]
//     arr3 = [ [ 1, 2 ], [3, 4, 5] ]
//     arr4 = [ "all", 'strings', """are the same""", '''type''']
//     arr5 = [ [ 1, 2 ], ["a", "b", "c"] ]
//
//     arr6 = [ 1, 2.0 ] # INVALID
//
// Arrays can also be multiline. A terminating comma (also called trailing
// comma) is ok after the last value of the array. There can be an arbitrary
// number of newlines and comments before a value and before the closing bracket.
//
//     arr7 = [
//       1, 2, 3
//     ]
//
//     arr8 = [
//       1,
//       2, # this is ok
//     ]
var (
	arrayWhitespaceComment = c.OneOrMore(whitespaceInclNewlines.Or(comment))
	arrayOpen              = a.SquareOpen.Then(arrayWhitespaceComment.Optional())
	arraySeparator         = c.Seq(whitespace, a.Comma, arrayWhitespaceComment.Optional())
	arrayClose             = c.Seq(whitespace, a.Comma.Optional(), arrayWhitespaceComment.Optional(), a.SquareClose)
)

func (t *parser) parseArray(p *parse.API) (*ast.Value, bool) {
	// Check for the start of the array.
	if !p.Accept(arrayOpen) {
		p.Expected("an array")
		return nil, false
	}

	a := ast.NewArray()

	// Check for an empty array.
	if p.Accept(arrayClose) {
		return ast.NewValue(ast.TypeArray, a), true
	}

	// Not an empty array, parse the array values.
	for {
		// Check for a value item.
		value, ok := t.parseValue(p)
		if !ok {
			return nil, false
		}

		// Data types may not be mixed (different ways to define strings should be
		// considered the same type, and so should arrays with different element types).
		// This constraint is checked by ast.Array.Append().
		if err := a.Append(value); err != nil {
			p.Error(err.Error())
			return nil, false
		}

		// Check for the end of the array.
		if p.Accept(arrayClose) {
			return ast.NewValue(ast.TypeArray, a), true
		}

		// Not the end of the array? Then we should find an array separator.
		if !p.Accept(arraySeparator) {
			p.Expected("an array separator")
			return nil, false
		}
	}
}
