package linkedbuffer

import (
	
	
)

// LinkedBuffer implements an unbounded generic buffer that can be written to and read from concurrently.
// It is implemented using a linked list of buffers.
type LinkedBuffer[ any] struct {
	// Reader points to the buffer that is currently being read
	readBuffer *buffer[]

	// Writer points to the buffer that is currently being written
	writeBuffer *buffer[]

	maxCapacity int
	writeCount  atomic.Uint64
	readCount   atomic.Uint64
}

func [ any](,  int) *LinkedBuffer[] {
	 := newBuffer[]()

	 := &LinkedBuffer[]{
		readBuffer:  ,
		writeBuffer: ,
		maxCapacity: ,
	}

	return 
}

// Write writes values to the buffer
func ( *LinkedBuffer[]) ( ) {

	// Write elements
	 := .writeBuffer.Write()

	if  == ErrEOF {
		// Increase next buffer capacity
		var  int
		 := .writeBuffer.Cap()
		if  < 1024 {
			 =  * 2
		} else {
			 =  + /2
		}
		if  > .maxCapacity {
			 = .maxCapacity
		}

		if .writeBuffer.next == nil {
			.writeBuffer.next = newBuffer[]()
			.writeBuffer = .writeBuffer.next
		}

		// Retry writing
		.()
		return
	}

	// Increment written count
	.writeCount.Add(1)
}

// Read reads values from the buffer and returns the number of elements read
func ( *LinkedBuffer[]) () ( ,  error) {
	// Read element
	,  = .readBuffer.Read()

	if  == ErrEOF {
		if .readBuffer.next == nil {
			// No more elements to read
			return
		}
		// Move to next read buffer
		if .readBuffer != .readBuffer.next {
			.readBuffer = .readBuffer.next
		}

		// Retry reading
		return .()
	}

	// Increment read count
	.readCount.Add(1)

	return
}

// WriteCount returns the number of elements written to the buffer since it was created
func ( *LinkedBuffer[]) () uint64 {
	return .writeCount.Load()
}

// ReadCount returns the number of elements read from the buffer since it was created
func ( *LinkedBuffer[]) () uint64 {
	return .readCount.Load()
}

// Len returns the number of elements in the buffer that haven't yet been read
func ( *LinkedBuffer[]) () uint64 {
	 := .writeCount.Load()
	 := .readCount.Load()

	if  <  {
		// The writeCount counter wrapped around
		return math.MaxUint64 -  + 
	}

	return  - 
}