// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.

// ast.go
package expr

import (
	"errors"
	"strings"
)

type Expr interface {
	Eval(ctx ExprContext) (result any, err error)
	eval(ctx ExprContext, preset bool) (result any, err error)
}

//-------- ast

type ast struct {
	forest []*term
	root   *term
}

func NewAst() *ast {
	return &ast{}
}

func (self *ast) ToForest() {
	if self.root != nil {
		if self.forest == nil {
			self.forest = make([]*term, 0)
		}
		self.forest = append(self.forest, self.root)
		self.root = nil
	}
}

func (self *ast) String() string {
	var sb strings.Builder
	if self.root == nil {
		sb.WriteString("(nil)")
	} else {
		self.root.toString(&sb)
	}
	return sb.String()
}

func (self *ast) addTokens(tokens ...*Token) (err error) {
	for _, tk := range tokens {
		if err = self.addToken(tk); err != nil {
			break
		}
	}
	return
}

func (self *ast) addToken(tk *Token) (err error) {
	_, err = self.addToken2(tk)
	return
}

func (self *ast) addToken2(tk *Token) (t *term, err error) {
	if t = newTerm(tk, nil); t != nil {
		err = self.addTerm(t)
	} else {
		err = tk.Errorf("No term constructor for token %q", tk.String())
	}
	return
}

func (self *ast) addTerm(node *term) (err error) {
	if self.root == nil {
		self.root = node
	} else {
		self.root, err = self.insert(self.root, node)
	}
	return
}

func (self *ast) insert(tree, node *term) (root *term, err error) {
	if tree.getPriority() < node.getPriority() {
		root = tree
		if tree.isComplete() {
			var subRoot *term
			last := tree.removeLastChild()
			subRoot, err = self.insert(last, node)
			subRoot.setParent(tree)
		} else {
			node.setParent(tree)
		}
	} else if !node.isLeaf() {
		root = node
		tree.setParent(node)
	} else {
		err = node.Errorf("two adjacent operators: %q and %q", tree, node)
	}
	return
}

func (self *ast) Finish() {
	if self.root == nil && self.forest != nil && len(self.forest) >= 1 {
		self.root = self.forest[len(self.forest)-1]
		self.forest = self.forest[0 : len(self.forest)-1]
	}
}

func (self *ast) Eval(ctx ExprContext) (result any, err error) {
	return self.eval(ctx, true)
}

func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) {
	self.Finish()

	if self.root != nil {
		if preset {
			initDefaultVars(ctx)
		}
		if self.forest != nil {
			for _, root := range self.forest {
				if result, err = root.compute(ctx); err == nil {
					ctx.SetVar(control_last_result, result)
				} else {
					//err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
					break
				}
			}
		}
		if err == nil {
			result, err = self.root.compute(ctx)
		}
	} else {
		err = errors.New("empty expression")
	}
	return
}