package ring_buffer

//import "core:fmt"

Ring_Buffer :: struct {
	buf: []u8,
	off: int,
	len: int,
	cap: int,
}

init :: proc(rb: ^Ring_Buffer, initial_size := 4096) {
	rb.buf = make([]u8, initial_size)
	rb.cap = initial_size
}

destroy :: proc(rb: ^Ring_Buffer) {
	if rb.buf != nil {
		delete(rb.buf)
	}
}

append :: proc(rb: ^Ring_Buffer, buf: []u8, allocator := context.allocator) {
	for rb.len + len(buf) > rb.cap {
		grow(rb)
	}

	for i in 0 ..< len(buf) {
		rb.buf[(rb.off + i) % rb.cap] = buf[i]
		rb.len += 1
	}

	//fmt.printf("%v\nappended %d bytes, off: %d, len: %d\n", buf, len(buf), rb.off, rb.len)
}

grow :: proc(rb: ^Ring_Buffer, grow_mul := 2, allocator := context.allocator) {
	cap := rb.cap * grow_mul
	buf := make([]u8, cap)
	if rb.off + rb.len <= rb.cap {
		copy(buf, rb.buf[rb.off:rb.off + rb.len])
	} else {
		copied := copy(buf[0:], rb.buf[rb.off:])
		copy(buf[copied:], rb.buf[:rb.len - copied])
	}

	rb.cap = cap
	rb.off = 0
	delete(rb.buf)
	rb.buf = buf

	//fmt.printf("grew by %d bytes, off: %d, len: %d, cap: %d\n", cap, rb.off, rb.len, rb.cap)
}

consume_front :: proc(rb: ^Ring_Buffer, n: int, allocator := context.allocator) -> []u8 {
	//fmt.printf("consume %d\n", n)
	if n <= 0 {
		return nil
	}
	if n > rb.len {
		rb.off = 0
		rb.len = 0
		return nil
	}

	result := make([]u8, rb.len)
	if rb.off + n <= rb.cap {
		copy(result, rb.buf[rb.off:rb.off + n])
	} else {
		copied := copy(result[0:], rb.buf[rb.off:])
		copy(result[copied:], rb.buf[:n - copied])
	}

	rb.off = (rb.off + n) % rb.cap
	rb.len -= n

	//fmt.printf("%v\nconsumed %d bytes, off: %d, len: %d\n", result, n, rb.off, rb.len)

	return result
}