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

// ast.go
package scan

import (
	"errors"
	"strings"

	"git.portale-stac.it/go-pkg/expr/kern"
)

//-------- ast

type Ast struct {
	forest []*Term
	root   *Term
}

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

func (ast *Ast) TypeName() string {
	return "Expression"
}

func (ast *Ast) Root() *Term {
	return ast.root
}

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

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

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

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

func (ast *Ast) AddToken(tk *Token) (t *Term, err error) {
	if t = NewTerm(tk); t != nil {
		err = ast.AddTerm(t)
	} else {
		err = tk.Errorf("unexpected token %q", tk.String())
	}
	return
}

func (ast *Ast) AddTerm(node *Term) (err error) {
	if ast.root == nil {
		ast.root = node
	} else {
		ast.root, err = ast.insert(ast.root, node)
	}
	return
}

func (ast *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()
			if subRoot, err = ast.insert(last, node); err == nil {
				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.Source())
	}
	return
}

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

func (ast *Ast) Eval(ctx kern.ExprContext) (result any, err error) {
	defer func() {
		if r := recover(); r != nil {
			if errVal, ok := r.(error); ok {
				err = errVal
			} else {
				err = errors.New("unexpected error while evaluating the expression")
			}
		}
	}()

	ast.Finish()

	if ast.root != nil {
		// initDefaultVars(ctx)
		if ast.forest != nil {
			for _, tree := range ast.forest {
				if result, err = tree.Compute(ctx); err == nil {
					ctx.UnsafeSetVar(kern.ControlLastResult, result)
				} else {
					//err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
					break
				}
			}
		}
		if err == nil {
			if result, err = ast.root.Compute(ctx); err == nil {
				ctx.UnsafeSetVar(kern.ControlLastResult, result)
			}
		}
		// } else {
		// 	err = errors.New("empty expression")
	}
	return
}
