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

// list-iterator.go
package expr

import (
	"fmt"
	"io"
	"slices"

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

type ListIterator struct {
	a     *kern.ListType
	count int64
	index int64
	start int64
	stop  int64
	step  int64
}

func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) {
	var argc int = 0
	listLen := int64(len(([]any)(*list)))
	if args != nil {
		argc = len(args)
	}
	it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
	if argc >= 1 {
		if i, err := kern.ToGoInt64(args[0], "start index"); err == nil {
			if i < 0 {
				i = listLen + i
			}
			it.start = i
		}
		if argc >= 2 {
			if i, err := kern.ToGoInt64(args[1], "stop index"); err == nil {
				if i < 0 {
					i = listLen + i
				}
				it.stop = i
			}
			if argc >= 3 {
				if i, err := kern.ToGoInt64(args[2], "step"); err == nil {
					if i < 0 {
						i = -i
					}
					if it.start > it.stop {
						it.step = -i
					} else {
						it.step = i
					}
				}
			}
		}
	}
	it.index = it.start - it.step
	return
}

func NewArrayIterator(array []any) (it *ListIterator) {
	it = &ListIterator{a: (*kern.ListType)(&array), count: 0, index: -1, start: 0, stop: int64(len(array)) - 1, step: 1}
	return
}

func (it *ListIterator) String() string {
	var l = int64(0)
	if it.a != nil {
		l = int64(len(*it.a))
	}
	return fmt.Sprintf("$([#%d])", l)
}

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

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

func (it *ListIterator) 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 *ListIterator) Current() (item any, err error) {
	a := *(it.a)
	if it.start <= it.stop {
		if it.stop < int64(len(a)) && it.index >= it.start && it.index <= it.stop {
			item = a[it.index]
		} else {
			err = io.EOF
		}
	} else {
		if it.start < int64(len(a)) && it.index >= it.stop && it.index <= it.start {
			item = a[it.index]
		} else {
			err = io.EOF
		}

	}
	return
}

func (it *ListIterator) Next() (item any, err error) {
	it.index += it.step
	if item, err = it.Current(); err != io.EOF {
		it.count++
	}
	return
}

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

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

func (it *ListIterator) Reset() error {
	it.index = it.start - it.step
	it.count = 0
	return nil
}

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