package parquet

import (
	
	
)

var (
	ErrMissingColumnIndex = errors.New("missing column index")
	ErrMissingOffsetIndex = errors.New("missing offset index")
)

// The ColumnChunk interface represents individual columns of a row group.
type ColumnChunk interface {
	// Returns the column type.
	Type() Type

	// Returns the index of this column in its parent row group.
	Column() int

	// Returns a reader exposing the pages of the column.
	Pages() Pages

	// Returns the components of the page index for this column chunk,
	// containing details about the content and location of pages within the
	// chunk.
	//
	// Note that the returned value may be the same across calls to these
	// methods, programs must treat those as read-only.
	//
	// If the column chunk does not have a column or offset index, the methods return
	// ErrMissingColumnIndex or ErrMissingOffsetIndex respectively.
	//
	// Prior to v0.20, these methods did not return an error because the page index
	// for a file was either fully read when the file was opened, or skipped
	// completely using the parquet.SkipPageIndex option. Version v0.20 introduced a
	// change that the page index can be read on-demand at any time, even if a file
	// was opened with the parquet.SkipPageIndex option. Since reading the page index
	// can fail, these methods now return an error.
	ColumnIndex() (ColumnIndex, error)
	OffsetIndex() (OffsetIndex, error)
	BloomFilter() BloomFilter

	// Returns the number of values in the column chunk.
	//
	// This quantity may differ from the number of rows in the parent row group
	// because repeated columns may hold zero or more values per row.
	NumValues() int64
}

type pageAndValueWriter interface {
	PageWriter
	ValueWriter
}

type readRowsFunc func(*rowGroupRows, []Row, byte) (int, error)

func readRowsFuncOf( Node,  int,  byte) (int, readRowsFunc) {
	var  readRowsFunc

	if .Repeated() {
		++
	}

	if .Leaf() {
		,  = readRowsFuncOfLeaf(, )
	} else {
		,  = readRowsFuncOfGroup(, , )
	}

	if .Repeated() {
		 = readRowsFuncOfRepeated(, )
	}

	return , 
}

//go:noinline
func readRowsFuncOfRepeated( readRowsFunc,  byte) readRowsFunc {
	return func( *rowGroupRows,  []Row,  byte) (int, error) {
		for  := range  {
			// Repeated columns have variable number of values, we must process
			// them one row at a time because we cannot predict how many values
			// need to be consumed in each iteration.
			 := [ : +1]

			// The first pass looks for values marking the beginning of a row by
			// having a repetition level equal to the current level.
			,  := (, , )
			if  != nil {
				// The error here may likely be io.EOF, the read function may
				// also have successfully read a row, which is indicated by a
				// non-zero count. In this case, we increment the index to
				// indicate to the caller than rows up to i+1 have been read.
				if  > 0 {
					++
				}
				return , 
			}

			// The read function may return no errors and also read no rows in
			// case where it had more values to read but none corresponded to
			// the current repetition level. This is an indication that we will
			// not be able to read more rows at this stage, we must return to
			// the caller to let it set the repetition level to its current
			// depth, which may allow us to read more values when called again.
			if  == 0 {
				return , nil
			}

			// When we reach this stage, we have successfully read the first
			// values of a row of repeated columns. We continue consuming more
			// repeated values until we get the indication that we consumed
			// them all (the read function returns zero and no errors).
			for {
				,  := (, , )
				if  != nil {
					return  + 1, 
				}
				if  == 0 {
					break
				}
			}
		}
		return len(), nil
	}
}

//go:noinline
func readRowsFuncOfGroup( Node,  int,  byte) (int, readRowsFunc) {
	 := .Fields()

	if len() == 0 {
		return , func(*rowGroupRows, []Row, byte) (int, error) {
			return 0, io.EOF
		}
	}

	if len() == 1 {
		// Small optimization for a somewhat common case of groups with a single
		// column (like nested list elements for example); there is no need to
		// loop over the group of a single element, we can simply skip to calling
		// the inner read function.
		return readRowsFuncOf([0], , )
	}

	 := make([]readRowsFunc, len())
	for  := range  {
		, [] = readRowsFuncOf([], , )
	}

	return , func( *rowGroupRows,  []Row,  byte) (int, error) {
		// When reading a group, we use the first column as an indicator of how
		// may rows can be read during this call.
		,  := [0](, , )

		if  > 0 {
			// Read values for all rows that the group is able to consume.
			// Getting io.EOF from calling the read functions indicate that
			// we consumed all values of that particular column, but there may
			// be more to read in other columns, therefore we must always read
			// all columns and cannot stop on the first error.
			for ,  := range [1:] {
				,  := (, [:], )
				if  != nil &&  != io.EOF {
					return 0, 
				}
			}
		}

		return , 
	}
}

//go:noinline
func readRowsFuncOfLeaf( int,  byte) (int, readRowsFunc) {
	var  readRowsFunc

	if  == 0 {
		 = func( *rowGroupRows,  []Row,  byte) (int, error) {
			// When the repetition depth is zero, we know that there is exactly
			// one value per row for this column, and therefore we can consume
			// as many values as there are rows to fill.
			 := &.columns[]
			 := .buffer()

			for  := range  {
				if .offset == .length {
					,  := .values.ReadValues()
					.offset = 0
					.length = int32()
					if  == 0 &&  != nil {
						return 0, 
					}
				}

				[] = append([], [.offset])
				.offset++
			}

			return len(), nil
		}
	} else {
		 = func( *rowGroupRows,  []Row,  byte) (int, error) {
			// When the repetition depth is not zero, we know that we will be
			// called with a single row as input. We attempt to read at most one
			// value of a single row and return to the caller.
			 := &.columns[]
			 := .buffer()

			if .offset == .length {
				,  := .values.ReadValues()
				.offset = 0
				.length = int32()
				if  == 0 &&  != nil {
					return 0, 
				}
			}

			if [.offset].repetitionLevel !=  {
				return 0, nil
			}

			[0] = append([0], [.offset])
			.offset++
			return 1, nil
		}
	}

	return  + 1, 
}