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

// int-iterator.go
package expr

import (
	"fmt"
	"io"
	"slices"

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

type IntIterator struct {
	count   int64
	index   int64
	next    int64
	current any
	start   int64
	stop    int64
	step    int64
}

func NewIntIteratorFromInterval(p *interval.IntervalType) (it *IntIterator, err error) {
	b := p.Begin()
	e := p.End()
	s := p.Step()
	if b > e {
		s = -s
	}
	return NewIntIteratorA(b, e, s)
}

func NewIntIteratorA(args ...any) (it *IntIterator, err error) {
	return NewIntIterator(args)
}

func NewIntIterator(args []any) (it *IntIterator, err error) {
	var argc int = 0
	if args != nil {
		argc = len(args)
	}
	it = &IntIterator{count: 0, index: -1, start: 0, stop: 0, step: 1}
	if argc >= 1 {
		if it.stop, err = types.ToGoInt64(args[0], "start index"); err != nil {
			return
		}
		if argc >= 2 {
			it.start = it.stop
			if it.stop, err = types.ToGoInt64(args[1], "stop index"); err != nil {
				return
			}
			if argc >= 3 {
				if it.step, err = types.ToGoInt64(args[2], "step"); err != nil {
					return
				}
			} else if it.start > it.stop {
				it.step = -1
			}
		}
	}
	if it.step == 0 {
		err = fmt.Errorf("step cannot be zero")
		return
	}
	if it.start < it.stop && it.step < 0 {
		err = fmt.Errorf("step cannot be negative when start < stop")
		return
	}
	if it.start > it.stop && it.step > 0 {
		err = fmt.Errorf("step cannot be positive when start > stop")
		return
	}
	it.Reset()
	return
}

func (it *IntIterator) String() string {
	return fmt.Sprintf("$(%d..%d..%d)", it.start, it.stop, it.step)
}

func (it *IntIterator) TypeName() string {
	return "IntIterator"
}

func (it *IntIterator) HasOperation(name string) bool {
	yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
	return yes
}

func (it *IntIterator) CallOperation(name string, args map[string]any) (v any, err error) {
	switch name {
	case kern.NextName:
		v, err = it.Next()
	case kern.ResetName:
		err = it.Reset()
	case kern.CleanName:
		err = it.Clean()
	case kern.IndexName:
		v = int64(it.Index())
	case kern.CurrentName:
		v, err = it.Current()
	case kern.CountName:
		v = it.count
	default:
		err = kern.ErrNoOperation(name)
	}
	return
}

func (it *IntIterator) Current() (item any, err error) {
	return it.current, nil
}

func (it *IntIterator) Next() (item any, err error) {
	item = it.next
	if it.step > 0 {
		if it.next+it.step > it.stop {
			err = io.EOF
		}
	} else if it.next+it.step < it.stop {
		err = io.EOF
	}
	if err == nil {
		it.index++
		it.count++
		it.next += it.step
		it.current = item
	}
	return
}

func (it *IntIterator) Index() int64 {
	return it.index
}

func (it *IntIterator) Count() int64 {
	return it.count
}

func (it *IntIterator) Reset() error {
	it.index = -1
	it.next = it.start
	it.current = nil
	it.count = 0
	return nil
}

func (it *IntIterator) Clean() error {
	return nil
}
