package pgproto3

import (
	

	
)

// chunkReader is a io.Reader wrapper that minimizes IO reads and memory allocations. It allocates memory in chunks and
// will read as much as will fit in the current buffer in a single call regardless of how large a read is actually
// requested. The memory returned via Next is only valid until the next call to Next.
//
// This is roughly equivalent to a bufio.Reader that only uses Peek and Discard to never copy bytes.
type chunkReader struct {
	r io.Reader

	buf    *[]byte
	rp, wp int // buf read position and write position

	minBufSize int
}

// newChunkReader creates and returns a new chunkReader for r with default configuration. If minBufSize is <= 0 it uses
// a default value.
func newChunkReader( io.Reader,  int) *chunkReader {
	if  <= 0 {
		// By historical reasons Postgres currently has 8KB send buffer inside,
		// so here we want to have at least the same size buffer.
		// @see https://github.com/postgres/postgres/blob/249d64999615802752940e017ee5166e726bc7cd/src/backend/libpq/pqcomm.c#L134
		// @see https://www.postgresql.org/message-id/0cdc5485-cb3c-5e16-4a46-e3b2f7a41322%40ya.ru
		//
		// In addition, testing has found no benefit of any larger buffer.
		 = 8192
	}

	return &chunkReader{
		r:          ,
		minBufSize: ,
		buf:        iobufpool.Get(),
	}
}

// Next returns buf filled with the next n bytes. buf is only valid until next call of Next. If an error occurs, buf
// will be nil.
func ( *chunkReader) ( int) ( []byte,  error) {
	// Reset the buffer if it is empty
	if .rp == .wp {
		if len(*.buf) != .minBufSize {
			iobufpool.Put(.buf)
			.buf = iobufpool.Get(.minBufSize)
		}
		.rp = 0
		.wp = 0
	}

	// n bytes already in buf
	if (.wp - .rp) >=  {
		 = (*.buf)[.rp : .rp+ : .rp+]
		.rp += 
		return , 
	}

	// buf is smaller than requested number of bytes
	if len(*.buf) <  {
		 := iobufpool.Get()
		.wp = copy((*), (*.buf)[.rp:.wp])
		.rp = 0
		iobufpool.Put(.buf)
		.buf = 
	}

	// buf is large enough, but need to shift filled area to start to make enough contiguous space
	 :=  - (.wp - .rp)
	if (len(*.buf) - .wp) <  {
		.wp = copy((*.buf), (*.buf)[.rp:.wp])
		.rp = 0
	}

	// Read at least the required number of bytes from the underlying io.Reader
	,  := io.ReadAtLeast(.r, (*.buf)[.wp:], )
	.wp += 
	// fmt.Println("read", n)
	if  != nil {
		return nil, 
	}

	 = (*.buf)[.rp : .rp+ : .rp+]
	.rp += 
	return , nil
}