package parser

import (
	"github.com/a-h/parse"
)

var scriptTemplateParser = parse.Func(func(pi *parse.Input) (r *ScriptTemplate, ok bool, err error) {
	start := pi.Position()

	// Parse the name.
	var se scriptExpression
	if se, ok, err = scriptExpressionParser.Parse(pi); err != nil || !ok {
		pi.Seek(start.Index)
		return
	}
	r = &ScriptTemplate{
		Name:       se.Name,
		Parameters: se.Parameters,
	}
	defer func() {
		r.Range = NewRange(start, pi.Position())
	}()

	// Read code expression.
	var e Expression
	if e, ok, err = exp.Parse(pi); err != nil || !ok {
		pi.Seek(start.Index)
		return
	}
	r.Value = e.Value

	// Try for }
	if _, ok, err = closeBraceWithOptionalPadding.Parse(pi); err != nil || !ok {
		err = parse.Error("script template: missing closing brace", pi.Position())
		return
	}

	return r, true, nil
})

// script Func() {
type scriptExpression struct {
	Name       Expression
	Parameters Expression
}

var scriptExpressionNameParser = ExpressionOf(parse.StringFrom(
	parse.Letter,
	parse.StringFrom(parse.AtMost(1000, parse.Any(parse.Letter, parse.ZeroToNine))),
))

var scriptExpressionParser = parse.Func(func(pi *parse.Input) (r scriptExpression, ok bool, err error) {
	// Check the prefix first.
	if _, ok, err = parse.String("script ").Parse(pi); err != nil || !ok {
		return
	}

	// Once we have the prefix, we must have a name and parameters.
	// Read the name of the function.
	if r.Name, ok, err = scriptExpressionNameParser.Parse(pi); err != nil || !ok {
		err = parse.Error("script expression: invalid name", pi.Position())
		return
	}

	// Eat the open bracket.
	if _, ok, err = openBracket.Parse(pi); err != nil || !ok {
		err = parse.Error("script expression: parameters missing open bracket", pi.Position())
		return
	}

	// Read the parameters.
	// p Person, other Other, t thing.Thing)
	if r.Parameters, ok, err = ExpressionOf(parse.StringUntil(closeBracket)).Parse(pi); err != nil || !ok {
		err = parse.Error("script expression: parameters missing close bracket", pi.Position())
		return
	}

	// Eat ") {".
	if _, ok, err = expressionFuncEnd.Parse(pi); err != nil || !ok {
		err = parse.Error("script expression: unterminated (missing ') {')", pi.Position())
		return
	}

	// Expect a newline.
	if _, ok, err = parse.NewLine.Parse(pi); err != nil || !ok {
		err = parse.Error("script expression: missing terminating newline", pi.Position())
		return
	}

	return r, true, nil
})
