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

// operator-interval.go
package expr

import (
	"git.portale-stac.it/go-pkg/expr/kern"
	"git.portale-stac.it/go-pkg/expr/scan"
	"git.portale-stac.it/go-pkg/expr/sym"
	"git.portale-stac.it/go-pkg/expr/types"
	"git.portale-stac.it/go-pkg/expr/types/interval"
)

// -------- interval term

func newIntervalTerm(tk *scan.Token) (inst *scan.Term) {
	return &scan.Term{
		Tk:       *tk,
		Children: make([]*scan.Term, 0, 2),
		Position: scan.PosInfix,
		Priority: scan.PriInterval,
		EvalFunc: evalInterval,
	}
}

func changeColonToInterval(t *scan.Term) {
	if t.IsSymbol(sym.SymDoubleDot) {
		t.SetSymbol(sym.SymInterval)
		t.EvalFunc = evalInterval
		if child := t.GetLeftChild(); child != nil && child.IsSymbol(sym.SymDoubleDot) {
			child.SetSymbol(sym.SymInterval)
		}
	}
}

func evalInterval(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
	var begin, end int64

	step := int64(1)
	switch opTerm.GetChildCount() {
	case 0:
		begin = 0
		end = int64(kern.ConstLastIndex)
	case 1:
		var leftValue any
		if leftValue, err = opTerm.GetChild(0).Compute(ctx); err != nil {
			return
		}
		if begin, err = checkIntervalComponent(opTerm, leftValue); err != nil {
			return
		}
		end = int64(kern.ConstLastIndex)
	default:
		var beginValue, endValue, stepValue any
		subTerm := opTerm.GetLeftChild()
		if subTerm.IsOneOf(sym.SymInterval, sym.SymDoubleDot) {
			if beginValue, endValue, err = subTerm.EvalInfix(ctx); err != nil {
				return
			}
			if stepValue, err = opTerm.GetRightChild().Compute(ctx); err != nil {
				return
			}
			step, err = checkIntervalComponent(opTerm.GetRightChild(), stepValue)
		} else if beginValue, endValue, err = opTerm.EvalInfix(ctx); err != nil {
			return
		}
		if begin, err = checkIntervalComponent(opTerm, beginValue); err != nil {
			return
		}
		if end, err = checkIntervalComponent(opTerm, endValue); err != nil {
			return
		}
	}

	if step <= 0 {
		err = opTerm.Errorf("invalid %s specification: step must be positive", interval.TypeName)
	} else {
		v = interval.NewInterval(begin, end, step)
	}
	return
}

func checkIntervalComponent(t kern.Term, value any) (component int64, err error) {
	if component, err = types.ToGoInt64(value, "%s expression"); err != nil {
		err = t.Errorf(err.Error(), interval.TypeName)
	}
	return
}

// func errIntervalInvalidSpecification(t kern.Term) error {
// 	return t.Errorf("invalid %s specification", interval.TypeName)
// }

func errIntervalUnexpectedExpression(t *scan.Term) error {
	return t.Errorf("unexpected %s expression", interval.TypeName)
}

// init
func init() {
	scan.RegisterTermConstructor(sym.SymInterval, newIntervalTerm)
	scan.RegisterTermConstructor(sym.SymDoubleDot, newIntervalTerm)
}
