class_name Iterator extends RefCounted

static func repeat(value: Variant) -> RepeatIterator:
    return RepeatIterator.new(value)

func _to_string() -> String:
    return "Iterator"

func clone() -> Iterator:
    assert(false, "can't clone a abstract base class")
    return null

func next() -> Option:
    return Option.none

static func empty() -> Iterator:
    return EmptyIterator.new()

func advance_by(amount: int) -> Iterator:
    for _i in range(amount):
        if self.next().is_none():
            return empty()
    return self

func all(predicate: Callable) -> bool:
    for value in self:
        if not predicate.call(value):
            return false
    return true

func any(predicate: Callable) -> bool:
    for value in self:
        if predicate.call(value):
            return true
    return false

func collect() -> Array:
    var result = []
    for item in self:
        result.append(item)
    return result

func count() -> int:
    var length = 0
    for _value in self:
        length += 1
    return length

func enumerate() -> IndexedIterator:
    return IndexedIterator.new(self)

func filter(predicate: Callable) -> FilterIterator:
    return FilterIterator.new(self, predicate)

func find(predicate: Callable) -> Option:
    for value in self:
        if predicate.call(value):
            return Option.some(value)
    return Option.none

func fuse(other: Iterator) -> FusedIterator:
    return FusedIterator.new([self, other])

func inspect(fn: Callable) -> Iterator:
    return InspectIterator.new(self, fn)

func last() -> Option:
    var last = Option.none
    for value in self:
        last = Option.some(value)
    return last

func nth(index: int) -> Option:
    advance_by(index - 1)
    return self.next()

func map(fn: Callable) -> MapIterator:
    return MapIterator.new(self, fn)

func peekable() -> PeekableIter:
    return PeekableIter.new(self)

func position(predicate: Callable) -> Option:
    for indexed_value in self.enumerate():
        if predicate.call(indexed_value.value):
            return Option.some(indexed_value.index)
    return Option.none

func reduce(reducer: Callable, initial_value: Variant) -> Option:
    return scan(reducer, initial_value).last()

func scan(reducer: Callable, initial_value: Variant) -> ReduceIterator:
    return ReduceIterator.new(self, reducer, initial_value)

func skip(amount: int) -> SkipIterator:
    return SkipIterator.new(self, amount)

func skip_while(predicate: Callable) -> SkipWhileIterator:
    return SkipWhileIterator.new(self, predicate)

func take(limit: int) -> TakeIterator:
    return TakeIterator.new(self, limit)

func take_while(predicate: Callable) -> TakeWhileIter:
    return TakeWhileIter.new(self, predicate)

func zip(a: Iterator, b: Iterator) -> ZipIterator:
    return ZipIterator.new(a, b)

func _iter_init(iter: Array) -> bool:
    return _iter_next(iter)

func _iter_next(iter: Array) -> bool:
    iter[0] = next()
    return iter[0].is_some()

func _iter_get(iter: Variant) -> Variant:
    return iter.unwrap()
