package parquet

import (
	
	
	
	
	
	

	
)

// GenericBuffer is similar to a Buffer but uses a type parameter to define the
// Go type representing the schema of rows in the buffer.
//
// See GenericWriter for details about the benefits over the classic Buffer API.
type GenericBuffer[ any] struct {
	base  Buffer
	write bufferFunc[]
}

// NewGenericBuffer is like NewBuffer but returns a GenericBuffer[T] suited to write
// rows of Go type T.
//
// The type parameter T should be a map, struct, or any. Any other types will
// cause a panic at runtime. Type checking is a lot more effective when the
// generic parameter is a struct type, using map and interface types is somewhat
// similar to using a Writer.  If using an interface type for the type parameter,
// then providing a schema at instantiation is required.
//
// If the option list may explicitly declare a schema, it must be compatible
// with the schema generated from T.
func [ any]( ...RowGroupOption) *GenericBuffer[] {
	,  := NewRowGroupConfig(...)
	if  != nil {
		panic()
	}

	 := typeOf[]()
	if .Schema == nil &&  != nil {
		.Schema = schemaOf(dereference())
	}

	if .Schema == nil {
		panic("generic buffer must be instantiated with schema or concrete type.")
	}

	 := &GenericBuffer[]{
		base: Buffer{config: },
	}
	.base.configure(.Schema)
	.write = bufferFuncOf[](, .Schema)
	return 
}

func typeOf[ any]() reflect.Type {
	var  
	return reflect.TypeOf()
}

type bufferFunc[ any] func(*GenericBuffer[], []) (int, error)

func bufferFuncOf[ any]( reflect.Type,  *Schema) bufferFunc[] {
	if  == nil {
		return (*GenericBuffer[]).writeRows
	}
	switch .Kind() {
	case reflect.Interface, reflect.Map:
		return (*GenericBuffer[]).writeRows

	case reflect.Struct:
		return makeBufferFunc[](, )

	case reflect.Pointer:
		if  := .Elem(); .Kind() == reflect.Struct {
			return makeBufferFunc[](, )
		}
	}
	panic("cannot create buffer for values of type " + .String())
}

func makeBufferFunc[ any]( reflect.Type,  *Schema) bufferFunc[] {
	 := writeRowsFuncOf(, , nil)
	return func( *GenericBuffer[],  []) ( int,  error) {
		 = (.base.columns, makeArrayOf(), columnLevels{})
		if  == nil {
			 = len()
		}
		return , 
	}
}

func ( *GenericBuffer[]) () int64 {
	return .base.Size()
}

func ( *GenericBuffer[]) () int64 {
	return .base.NumRows()
}

func ( *GenericBuffer[]) () []ColumnChunk {
	return .base.ColumnChunks()
}

func ( *GenericBuffer[]) () []ColumnBuffer {
	return .base.ColumnBuffers()
}

func ( *GenericBuffer[]) () []SortingColumn {
	return .base.SortingColumns()
}

func ( *GenericBuffer[]) () int {
	return .base.Len()
}

func ( *GenericBuffer[]) (,  int) bool {
	return .base.Less(, )
}

func ( *GenericBuffer[]) (,  int) {
	.base.Swap(, )
}

func ( *GenericBuffer[]) () {
	.base.Reset()
}

func ( *GenericBuffer[]) ( []) (int, error) {
	if len() == 0 {
		return 0, nil
	}
	return .write(, )
}

func ( *GenericBuffer[]) ( []Row) (int, error) {
	return .base.WriteRows()
}

func ( *GenericBuffer[]) ( RowGroup) (int64, error) {
	return .base.WriteRowGroup()
}

func ( *GenericBuffer[]) () Rows {
	return .base.Rows()
}

func ( *GenericBuffer[]) () *Schema {
	return .base.Schema()
}

func ( *GenericBuffer[]) ( []) (int, error) {
	if cap(.base.rowbuf) < len() {
		.base.rowbuf = make([]Row, len())
	} else {
		.base.rowbuf = .base.rowbuf[:len()]
	}
	defer clearRows(.base.rowbuf)

	 := .base.Schema()
	for  := range  {
		.base.rowbuf[] = .Deconstruct(.base.rowbuf[], &[])
	}

	return .base.WriteRows(.base.rowbuf)
}

var (
	_ RowGroup       = (*GenericBuffer[any])(nil)
	_ RowGroupWriter = (*GenericBuffer[any])(nil)
	_ sort.Interface = (*GenericBuffer[any])(nil)

	_ RowGroup       = (*GenericBuffer[struct{}])(nil)
	_ RowGroupWriter = (*GenericBuffer[struct{}])(nil)
	_ sort.Interface = (*GenericBuffer[struct{}])(nil)

	_ RowGroup       = (*GenericBuffer[map[struct{}]struct{}])(nil)
	_ RowGroupWriter = (*GenericBuffer[map[struct{}]struct{}])(nil)
	_ sort.Interface = (*GenericBuffer[map[struct{}]struct{}])(nil)
)

// Buffer represents an in-memory group of parquet rows.
//
// The main purpose of the Buffer type is to provide a way to sort rows before
// writing them to a parquet file. Buffer implements sort.Interface as a way
// to support reordering the rows that have been written to it.
type Buffer struct {
	config  *RowGroupConfig
	schema  *Schema
	rowbuf  []Row
	colbuf  [][]Value
	chunks  []ColumnChunk
	columns []ColumnBuffer
	sorted  []ColumnBuffer
}

// NewBuffer constructs a new buffer, using the given list of buffer options
// to configure the buffer returned by the function.
//
// The function panics if the buffer configuration is invalid. Programs that
// cannot guarantee the validity of the options passed to NewBuffer should
// construct the buffer configuration independently prior to calling this
// function:
//
//	config, err := parquet.NewRowGroupConfig(options...)
//	if err != nil {
//		// handle the configuration error
//		...
//	} else {
//		// this call to create a buffer is guaranteed not to panic
//		buffer := parquet.NewBuffer(config)
//		...
//	}
func ( ...RowGroupOption) *Buffer {
	,  := NewRowGroupConfig(...)
	if  != nil {
		panic()
	}
	 := &Buffer{
		config: ,
	}
	if .Schema != nil {
		.configure(.Schema)
	}
	return 
}

func ( *Buffer) ( *Schema) {
	if  == nil {
		return
	}
	 := .config.Sorting.SortingColumns
	.sorted = make([]ColumnBuffer, len())

	forEachLeafColumnOf(, func( leafColumn) {
		 := nullsGoLast
		 := int(.columnIndex)
		 := .node.Type()
		 := .config.ColumnBufferCapacity
		 := (Dictionary)(nil)
		 := encodingOf(.node)

		if isDictionaryEncoding() {
			 := .EstimateSize()
			 := .NewValues(
				make([]byte, 0, ),
				nil,
			)
			 = .NewDictionary(, 0, )
			 = .Type()
		}

		 := searchSortingColumn(, .path)
		if  < len() && [].NullsFirst() {
			 = nullsGoFirst
		}

		 := .NewColumnBuffer(, )
		switch {
		case .maxRepetitionLevel > 0:
			 = newRepeatedColumnBuffer(, .maxRepetitionLevel, .maxDefinitionLevel, )
		case .maxDefinitionLevel > 0:
			 = newOptionalColumnBuffer(, .maxDefinitionLevel, )
		}
		.columns = append(.columns, )

		if  < len() {
			if [].Descending() {
				 = &reversedColumnBuffer{}
			}
			.sorted[] = 
		}
	})

	.schema = 
	.rowbuf = make([]Row, 0, 1)
	.colbuf = make([][]Value, len(.columns))
	.chunks = make([]ColumnChunk, len(.columns))

	for ,  := range .columns {
		.chunks[] = 
	}
}

// Size returns the estimated size of the buffer in memory (in bytes).
func ( *Buffer) () int64 {
	 := int64(0)
	for ,  := range .columns {
		 += .Size()
	}
	return 
}

// NumRows returns the number of rows written to the buffer.
func ( *Buffer) () int64 { return int64(.Len()) }

// ColumnChunks returns the buffer columns.
func ( *Buffer) () []ColumnChunk { return .chunks }

// ColumnBuffer returns the buffer columns.
//
// This method is similar to ColumnChunks, but returns a list of ColumnBuffer
// instead of a ColumnChunk values (the latter being read-only); calling
// ColumnBuffers or ColumnChunks with the same index returns the same underlying
// objects, but with different types, which removes the need for making a type
// assertion if the program needed to write directly to the column buffers.
// The presence of the ColumnChunks method is still required to satisfy the
// RowGroup interface.
func ( *Buffer) () []ColumnBuffer { return .columns }

// Schema returns the schema of the buffer.
//
// The schema is either configured by passing a Schema in the option list when
// constructing the buffer, or lazily discovered when the first row is written.
func ( *Buffer) () *Schema { return .schema }

// SortingColumns returns the list of columns by which the buffer will be
// sorted.
//
// The sorting order is configured by passing a SortingColumns option when
// constructing the buffer.
func ( *Buffer) () []SortingColumn { return .config.Sorting.SortingColumns }

// Len returns the number of rows written to the buffer.
func ( *Buffer) () int {
	if len(.columns) == 0 {
		return 0
	} else {
		// All columns have the same number of rows.
		return .columns[0].Len()
	}
}

// Less returns true if row[i] < row[j] in the buffer.
func ( *Buffer) (,  int) bool {
	for ,  := range .sorted {
		switch {
		case .Less(, ):
			return true
		case .Less(, ):
			return false
		}
	}
	return false
}

// Swap exchanges the rows at indexes i and j.
func ( *Buffer) (,  int) {
	for ,  := range .columns {
		.Swap(, )
	}
}

// Reset clears the content of the buffer, allowing it to be reused.
func ( *Buffer) () {
	for ,  := range .columns {
		.Reset()
	}
}

// Write writes a row held in a Go value to the buffer.
func ( *Buffer) ( interface{}) error {
	if .schema == nil {
		.configure(SchemaOf())
	}

	.rowbuf = .rowbuf[:1]
	defer clearRows(.rowbuf)

	.rowbuf[0] = .schema.Deconstruct(.rowbuf[0], )
	,  := .WriteRows(.rowbuf)
	return 
}

// WriteRows writes parquet rows to the buffer.
func ( *Buffer) ( []Row) (int, error) {
	defer func() {
		for ,  := range .colbuf {
			clearValues()
			.colbuf[] = [:0]
		}
	}()

	if .schema == nil {
		return 0, ErrRowGroupSchemaMissing
	}

	for ,  := range  {
		for ,  := range  {
			 := .Column()
			.colbuf[] = append(.colbuf[], )
		}
	}

	for ,  := range .colbuf {
		if ,  := .columns[].WriteValues();  != nil {
			// TODO: an error at this stage will leave the buffer in an invalid
			// state since the row was partially written. Applications are not
			// expected to continue using the buffer after getting an error,
			// maybe we can enforce it?
			return 0, 
		}
	}

	return len(), nil
}

// WriteRowGroup satisfies the RowGroupWriter interface.
func ( *Buffer) ( RowGroup) (int64, error) {
	 := .Schema()
	switch {
	case  == nil:
		return 0, ErrRowGroupSchemaMissing
	case .schema == nil:
		.configure()
	case !nodesAreEqual(.schema, ):
		return 0, ErrRowGroupSchemaMismatch
	}
	if !sortingColumnsHavePrefix(.SortingColumns(), .SortingColumns()) {
		return 0, ErrRowGroupSortingColumnsMismatch
	}
	 := .NumRows()
	 := .Rows()
	defer .Close()
	,  := CopyRows(bufferWriter{}, )
	return .NumRows() - , 
}

// Rows returns a reader exposing the current content of the buffer.
//
// The buffer and the returned reader share memory. Mutating the buffer
// concurrently to reading rows may result in non-deterministic behavior.
func ( *Buffer) () Rows { return newRowGroupRows(, ReadModeSync) }

// bufferWriter is an adapter for Buffer which implements both RowWriter and
// PageWriter to enable optimizations in CopyRows for types that support writing
// rows by copying whole pages instead of calling WriteRow repeatedly.
type bufferWriter struct{ buf *Buffer }

func ( bufferWriter) ( []Row) (int, error) {
	return .buf.WriteRows()
}

func ( bufferWriter) ( []Value) (int, error) {
	return .buf.columns[[0].Column()].WriteValues()
}

func ( bufferWriter) ( Page) (int64, error) {
	return CopyValues(.buf.columns[.Column()], .Values())
}

var (
	_ RowGroup       = (*Buffer)(nil)
	_ RowGroupWriter = (*Buffer)(nil)
	_ sort.Interface = (*Buffer)(nil)

	_ RowWriter   = (*bufferWriter)(nil)
	_ PageWriter  = (*bufferWriter)(nil)
	_ ValueWriter = (*bufferWriter)(nil)
)

type buffer struct {
	data  []byte
	refc  uintptr
	pool  *bufferPool
	stack []byte
}

func ( *buffer) () int {
	return int(atomic.LoadUintptr(&.refc))
}

func ( *buffer) () {
	atomic.AddUintptr(&.refc, +1)
}

func ( *buffer) () {
	if atomic.AddUintptr(&.refc, ^uintptr(0)) == 0 {
		if .pool != nil {
			.pool.put()
		}
	}
}

func monitorBufferRelease( *buffer) {
	if  := .refCount();  != 0 {
		log.Printf("PARQUETGODEBUG: buffer garbage collected with non-zero reference count\n%s", string(.stack))
	}
}

type bufferPool struct {
	// Buckets are split in two groups for short and large buffers. In the short
	// buffer group (below 256KB), the growth rate between each bucket is 2. The
	// growth rate changes to 1.5 in the larger buffer group.
	//
	// Short buffer buckets:
	// ---------------------
	//   4K, 8K, 16K, 32K, 64K, 128K, 256K
	//
	// Large buffer buckets:
	// ---------------------
	//   364K, 546K, 819K ...
	//
	buckets [bufferPoolBucketCount]sync.Pool
}

func ( *bufferPool) (,  int) *buffer {
	 := &buffer{
		data: make([]byte, , ),
		refc: 1,
		pool: ,
	}
	if debug.TRACEBUF > 0 {
		.stack = make([]byte, 4096)
		runtime.SetFinalizer(, monitorBufferRelease)
	}
	return 
}

// get returns a buffer from the levelled buffer pool. size is used to choose
// the appropriate pool.
func ( *bufferPool) ( int) *buffer {
	,  := bufferPoolBucketIndexAndSizeOfGet()

	 := (*buffer)(nil)
	if  >= 0 {
		, _ = .buckets[].Get().(*buffer)
	}

	if  == nil {
		 = .newBuffer(, )
	} else {
		.data = .data[:]
		.ref()
	}

	if debug.TRACEBUF > 0 {
		.stack = .stack[:runtime.Stack(.stack[:cap(.stack)], false)]
	}
	return 
}

func ( *bufferPool) ( *buffer) {
	if .pool !=  {
		panic("BUG: buffer returned to a different pool than the one it was allocated from")
	}
	if .refCount() != 0 {
		panic("BUG: buffer returned to pool with a non-zero reference count")
	}
	if ,  := bufferPoolBucketIndexAndSizeOfPut(cap(.data));  >= 0 {
		.buckets[].Put()
	}
}

const (
	bufferPoolBucketCount         = 32
	bufferPoolMinSize             = 4096
	bufferPoolLastShortBucketSize = 262144
)

func bufferPoolNextSize( int) int {
	if  < bufferPoolLastShortBucketSize {
		return  * 2
	} else {
		return  + ( / 2)
	}
}

func bufferPoolBucketIndexAndSizeOfGet( int) (int, int) {
	 := bufferPoolMinSize

	for  := 0;  < bufferPoolBucketCount; ++ {
		if  <=  {
			return , 
		}
		 = bufferPoolNextSize()
	}

	return -1, 
}

func bufferPoolBucketIndexAndSizeOfPut( int) (int, int) {
	// When releasing buffers, some may have a capacity that is not one of the
	// bucket sizes (due to the use of append for example). In this case, we
	// have to put the buffer is the highest bucket with a size less or equal
	// to the buffer capacity.
	if  := bufferPoolMinSize;  >=  {
		for  := 0;  < bufferPoolBucketCount; ++ {
			 := bufferPoolNextSize()
			if  <  {
				return , 
			}
			 = 
		}
	}
	return -1, 
}

var (
	buffers bufferPool
)

type bufferedPage struct {
	Page
	values           *buffer
	offsets          *buffer
	repetitionLevels *buffer
	definitionLevels *buffer
}

func newBufferedPage( Page, , , ,  *buffer) *bufferedPage {
	 := &bufferedPage{
		Page:             ,
		values:           ,
		offsets:          ,
		definitionLevels: ,
		repetitionLevels: ,
	}
	bufferRef()
	bufferRef()
	bufferRef()
	bufferRef()
	return 
}

func ( *bufferedPage) (,  int64) Page {
	return newBufferedPage(
		.Page.Slice(, ),
		.values,
		.offsets,
		.definitionLevels,
		.repetitionLevels,
	)
}

func ( *bufferedPage) () {
	bufferRef(.values)
	bufferRef(.offsets)
	bufferRef(.definitionLevels)
	bufferRef(.repetitionLevels)
}

func ( *bufferedPage) () {
	bufferUnref(.values)
	bufferUnref(.offsets)
	bufferUnref(.definitionLevels)
	bufferUnref(.repetitionLevels)
}

func bufferRef( *buffer) {
	if  != nil {
		.ref()
	}
}

func bufferUnref( *buffer) {
	if  != nil {
		.unref()
	}
}

// Retain is a helper function to increment the reference counter of pages
// backed by memory which can be granularly managed by the application.
//
// Usage of this function is optional and with Release, is intended to allow
// finer grain memory management in the application. Most programs should be
// able to rely on automated memory management provided by the Go garbage
// collector instead.
//
// The function should be called when a page lifetime is about to be shared
// between multiple goroutines or layers of an application, and the program
// wants to express "sharing ownership" of the page.
//
// Calling this function on pages that do not embed a reference counter does
// nothing.
func ( Page) {
	if ,  := .(retainable);  != nil {
		.Retain()
	}
}

// Release is a helper function to decrement the reference counter of pages
// backed by memory which can be granularly managed by the application.
//
// Usage of this is optional and with Retain, is intended to allow finer grained
// memory management in the application, at the expense of potentially causing
// panics if the page is used after its reference count has reached zero. Most
// programs should be able to rely on automated memory management provided by
// the Go garbage collector instead.
//
// The function should be called to return a page to the internal buffer pool,
// when a goroutine "releases ownership" it acquired either by being the single
// owner (e.g. capturing the return value from a ReadPage call) or having gotten
// shared ownership by calling Retain.
//
// Calling this function on pages that do not embed a reference counter does
// nothing.
func ( Page) {
	if ,  := .(releasable);  != nil {
		.Release()
	}
}

type retainable interface {
	Retain()
}

type releasable interface {
	Release()
}

var (
	_ retainable = (*bufferedPage)(nil)
	_ releasable = (*bufferedPage)(nil)
)