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

// operator-range.go
package expr

import (
	"fmt"

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

// -------- range term
type intPair struct {
	a, b int
}

func (p *intPair) TypeName() string {
	return kern.TypePair
}

func (p *intPair) ToString(opt kern.FmtOpt) string {
	return fmt.Sprintf("(%d, %d)", p.a, p.b)
}

func isIntPair(v any) bool {
	_, ok := v.(*intPair)
	return ok
}

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

func changeColonToRange(t *scan.Term) {
	if t.Tk.IsSymbol(scan.SymColon) {
		t.Tk.Sym = scan.SymRange
		t.EvalFunc = evalRange
	}
}

func evalRange(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
	var leftValue, rightValue any

	if len(opTerm.Children) == 0 {
		leftValue = int64(0)
		rightValue = int64(-1)
	} else if len(opTerm.Children) == 1 {
		if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
			return
		}
		rightValue = int64(kern.ConstLastIndex)
	} else if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
		return
	}
	if !(kern.IsInteger(leftValue) && kern.IsInteger(rightValue)) {
		// err = opTerm.errIncompatibleTypes(leftValue, rightValue)
		err = errRangeInvalidSpecification(opTerm)
		return
	}

	startIndex, _ := leftValue.(int64)
	endIndex, _ := rightValue.(int64)

	v = &intPair{int(startIndex), int(endIndex)}
	return
}

func errRangeInvalidSpecification(t *scan.Term) error {
	return t.Errorf("invalid range specification")
}

func errRangeUnexpectedExpression(t *scan.Term) error {
	return t.Errorf("unexpected range expression")
}

// init
func init() {
	scan.RegisterTermConstructor(scan.SymRange, newRangeTerm)
}
