package hcl

// AbsTraversalForExpr attempts to interpret the given expression as
// an absolute traversal, or returns error diagnostic(s) if that is
// not possible for the given expression.
//
// A particular Expression implementation can support this function by
// offering a method called AsTraversal that takes no arguments and
// returns either a valid absolute traversal or nil to indicate that
// no traversal is possible.
//
// In most cases the calling application is interested in the value
// that results from an expression, but in rarer cases the application
// needs to see the the name of the variable and subsequent
// attributes/indexes itself, for example to allow users to give references
// to the variables themselves rather than to their values. An implementer
// of this function should at least support attribute and index steps.
func AbsTraversalForExpr(expr Expression) (Traversal, Diagnostics) {
	type asTraversal interface {
		AsTraversal() Traversal
	}

	if asT, supported := expr.(asTraversal); supported {
		if traversal := asT.AsTraversal(); traversal != nil {
			return traversal, nil
		}
	}
	return nil, Diagnostics{
		&Diagnostic{
			Severity: DiagError,
			Summary:  "Invalid expression",
			Detail:   "A static variable reference is required.",
			Subject:  expr.Range().Ptr(),
		},
	}
}

// RelTraversalForExpr is similar to AbsTraversalForExpr but it returns
// a relative traversal instead. Due to the nature of ZCL expressions, the
// first element of the returned traversal is always a TraverseAttr, and
// then it will be followed by zero or more other expressions.
//
// Any expression accepted by AbsTraversalForExpr is also accepted by
// RelTraversalForExpr.
func RelTraversalForExpr(expr Expression) (Traversal, Diagnostics) {
	traversal, diags := AbsTraversalForExpr(expr)
	if len(traversal) > 0 {
		root := traversal[0].(TraverseRoot)
		traversal[0] = TraverseAttr{
			Name:     root.Name,
			SrcRange: root.SrcRange,
		}
	}
	return traversal, diags
}
