class_name RingBuffer extends RefCounted

enum Direction {
    Left = -1,
    Right = 1
}

signal updated(new_range: IntRange, previous_range: IntRange)
signal rotated(direction: int)

var _source_size: int
var source_size: int:
    get: return _source_size
    set(value): set_source_size(value)

func set_source_size(value: int) -> void:
    assert(value >= 0, "source size must be a non-negative integer")
    _source_size = value

var _capacity: int
var capacity: int:
    get: return _capacity
    set(value): set_capacity(value)

func set_capacity(value: int) -> void:
    _capacity = value

var _offset: int
var offset: int:
    get: return _offset
    set(value): set_offset(value)

func set_offset(value: int) -> void:
    _offset = value

var _index: int

var start_index: int:
    get: return _index
    set(value): set_start_index(value)

func set_start_index(value: int) -> void:
    var previous = self.get_range()
    _index = self.index(value)
    var current = self.get_range()
    updated.emit(current, previous)

var end_index: int:
    get: return start_index + capacity

@warning_ignore("shadowed_variable")
func _init(source_size: int, capacity: int, start_index: int = 0, offset: int = 0) -> void:
    self.source_size = source_size
    self.capacity = capacity
    self.start_index = start_index
    self.offset = offset

func index(value: int) -> int:
    if _source_size == 0:
        return 0
    elif value < 0:
        return _source_size + value % _source_size
    else:
        return value % _source_size

func get_range() -> IntRange:
    return IntRange.new(start_index, end_index)

func iter() -> BoundedRangeIterator:
    return BoundedRangeIterator.new(get_range(), IntRange.new(0, _source_size))

func move(direction: Direction) -> Result:
    if _source_size <= _capacity:
        push_warning("source size is smaller than buffer capacity")
    _index = self.index(_index + direction)
    rotated.emit(direction)
    return Result.Unit

func move_right() -> Result:
    return move(Direction.Right)

func move_left() -> Result:
    return move(Direction.Left)
