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

// helpers.go
package expr

import (
	"fmt"
	"io"
	"os"
	"strings"
)

func EvalString(ctx ExprContext, source string) (result any, err error) {
	var tree *ast

	r := strings.NewReader(source)
	scanner := NewScanner(r, DefaultTranslations())
	parser := NewParser()

	if tree, err = parser.Parse(scanner); err == nil {
		result, err = tree.Eval(ctx)
	}
	return
}

type Arg struct {
	Name  string
	Value any
}

func EvalStringA(source string, args ...Arg) (result any, err error) {
	return EvalStringV(source, args)
}

func EvalStringV(source string, args []Arg) (result any, err error) {
	ctx := NewSimpleStore()
	for _, arg := range args {
		if isFunc(arg.Value) {
			if f, ok := arg.Value.(FuncTemplate); ok {
				functor := NewGolangFunctor(f)
				// ctx.RegisterFunc(arg.Name, functor, 0, -1)
				ctx.RegisterFunc(arg.Name, functor, TypeAny, []ExprFuncParam{
					NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, 0),
				})
			} else {
				err = fmt.Errorf("invalid function specification: %q", arg.Name)
			}
		} else if integer, ok := anyInteger(arg.Value); ok {
			ctx.SetVar(arg.Name, integer)
		} else if float, ok := anyFloat(arg.Value); ok {
			ctx.SetVar(arg.Name, float)
		} else if _, ok := arg.Value.(string); ok {
			ctx.SetVar(arg.Name, arg.Value)
		} else if _, ok := arg.Value.(bool); ok {
			ctx.SetVar(arg.Name, arg.Value)
		} else {
			err = fmt.Errorf("unsupported type %T specified for item %q", arg.Value, arg.Name)
		}
	}

	if err == nil {
		result, err = EvalString(ctx, source)
	}
	return
}

func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) {
	var tree *ast
	scanner := NewScanner(r, DefaultTranslations())
	parser := NewParser()

	if tree, err = parser.Parse(scanner); err == nil {
		result, err = tree.Eval(ctx)
	}
	return
}

func EvalFile(ctx ExprContext, filePath string) (result any, err error) {
	var fh *os.File
	if fh, err = os.Open(filePath); err != nil {
		return nil, err
	}
	defer fh.Close()
	result, err = EvalStream(ctx, fh)
	return
}
