package parse

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

// The primary building block of a TOML document is the table.
// Tables are filled with key/value pairs.

var (
	// Keys are on the left of the equals sign and values are on the right.
	// Blank is ignored around key names and values. The key, equals
	// sign, and value must be on the same line (though some values can be
	// broken over multiple lines).

	keyAssignment = c.Seq(whitespace, a.Equal, whitespace)

	// A key may be either bare, quoted or dotted. Bare keys may only
	// contain ASCII letters, ASCII digits, underscores, and dashes
	// (A-Za-z0-9_-). Note that bare keys are allowed to be composed of only
	// ASCII digits, e.g. 1234, but are always interpreted as strings.
	//
	// Quoted keys follow the exact same rules as either basic strings or
	// literal strings and allow you to use a much broader set of key names.
	// Best practice is to use bare keys except when absolutely necessary.
	// A bare key must be non-empty, but an empty quoted key is allowed
	// (though discouraged).

	bareKeyRune = c.Any(a.ASCIILower, a.ASCIIUpper, a.Digit, a.Underscore, a.Minus)
	bareKey     = c.OneOrMore(bareKeyRune)

	// Dotted keys are a sequence of bare or quoted keys joined with a dot.
	// This allows for grouping similar properties together. Whitespace
	// around dot-separated parts is ignored, however, best practice is to
	// not use any extraneous whitespace.

	keySeparatorDot = c.Seq(whitespace, a.Dot, whitespace)
)

func (t *parser) startKeyValuePair(p *parse.API) {
	key, ok := t.parseKey(p, ast.NewKey())
	if ok && p.Handle(t.startAssignment) {
		if value, ok := t.parseValue(p); ok {
			err := t.doc.SetKeyValuePair(key, value)
			if err != nil {
				p.Error("%s", err)
			} else if !p.Accept(endOfLineOrComment) {
				p.Expected("end of line")
			}
		}
	}
}

// Bare keys may only contain ASCII letters, ASCII digits, underscores, and
// dashes (A-Za-z0-9_-). Note that bare keys are allowed to be composed of only
// ASCII digits, e.g. 1234, but are always interpreted as strings.
//
// Quoted keys follow the exact same rules as either basic strings or literal
// strings and allow you to use a much broader set of key names. Best practice
// is to use bare keys except when absolutely necessary.
// A bare key must be non-empty, but an empty quoted key is allowed (though
// discouraged).

func (t *parser) parseKey(p *parse.API, key ast.Key) (ast.Key, bool) {
	var keyPart string
	var ok bool
	switch {
	case p.Accept(bareKey):
		keyPart, ok = p.Result().String(), true
	case p.Peek(a.SingleQuote):
		keyPart, ok = t.parseLiteralString("key", p)
	case p.Peek(a.DoubleQuote):
		keyPart, ok = t.parseBasicString("key", p)
	default:
		p.Expected("a key name")
		return nil, false
	}
	if !ok {
		return nil, false
	}
	key = append(key, keyPart)
	return t.parseEndOfKeyOrDot(p, key)
}

// Dotted keys are a sequence of bare or quoted keys joined with a dot.
// This allows for grouping similar properties together.
// Whitespace around dot-separated parts is ignored, however, best
// practice is to not use any extraneous whitespace.

func (t *parser) parseEndOfKeyOrDot(p *parse.API, key ast.Key) (ast.Key, bool) {
	if p.Accept(keySeparatorDot) {
		return t.parseKey(p, key)
	}
	return key, true
}

func (t *parser) startAssignment(p *parse.API) {
	if !p.Accept(keyAssignment) {
		p.Expected("a value assignment")
	}
}
