package parquet

import (
	
	
	
	

	
	
	
	
	
)

// Column represents a column in a parquet file.
//
// Methods of Column values are safe to call concurrently from multiple
// goroutines.
//
// Column instances satisfy the Node interface.
type Column struct {
	typ         Type
	file        *File
	schema      *format.SchemaElement
	order       *format.ColumnOrder
	path        columnPath
	columns     []*Column
	chunks      []*format.ColumnChunk
	columnIndex []*format.ColumnIndex
	offsetIndex []*format.OffsetIndex
	encoding    encoding.Encoding
	compression compress.Codec

	depth              int8
	maxRepetitionLevel byte
	maxDefinitionLevel byte
	index              int16
}

// Type returns the type of the column.
//
// The returned value is unspecified if c is not a leaf column.
func ( *Column) () Type { return .typ }

// Optional returns true if the column is optional.
func ( *Column) () bool { return schemaRepetitionTypeOf(.schema) == format.Optional }

// Repeated returns true if the column may repeat.
func ( *Column) () bool { return schemaRepetitionTypeOf(.schema) == format.Repeated }

// Required returns true if the column is required.
func ( *Column) () bool { return schemaRepetitionTypeOf(.schema) == format.Required }

// Leaf returns true if c is a leaf column.
func ( *Column) () bool { return .index >= 0 }

// Fields returns the list of fields on the column.
func ( *Column) () []Field {
	 := make([]Field, len(.columns))
	for ,  := range .columns {
		[] = 
	}
	return 
}

// Encoding returns the encodings used by this column.
func ( *Column) () encoding.Encoding { return .encoding }

// Compression returns the compression codecs used by this column.
func ( *Column) () compress.Codec { return .compression }

// Path of the column in the parquet schema.
func ( *Column) () []string { return .path[1:] }

// Name returns the column name.
func ( *Column) () string { return .schema.Name }

// ID returns column field id
func ( *Column) () int { return int(.schema.FieldID) }

// Columns returns the list of child columns.
//
// The method returns the same slice across multiple calls, the program must
// treat it as a read-only value.
func ( *Column) () []*Column { return .columns }

// Column returns the child column matching the given name.
func ( *Column) ( string) *Column {
	for ,  := range .columns {
		if .Name() ==  {
			return 
		}
	}
	return nil
}

// Pages returns a reader exposing all pages in this column, across row groups.
func ( *Column) () Pages {
	if .index < 0 {
		return emptyPages{}
	}
	 := &columnPages{
		pages: make([]filePages, len(.file.rowGroups)),
	}
	for  := range .pages {
		.pages[].init(.file.rowGroups[].(*fileRowGroup).columns[.index].(*fileColumnChunk))
	}
	return 
}

type columnPages struct {
	pages []filePages
	index int
}

func ( *columnPages) () (Page, error) {
	for {
		if .index >= len(.pages) {
			return nil, io.EOF
		}
		,  := .pages[.index].ReadPage()
		if  == nil ||  != io.EOF {
			return , 
		}
		.index++
	}
}

func ( *columnPages) ( int64) error {
	.index = 0

	for .index < len(.pages) && .pages[.index].chunk.rowGroup.NumRows <  {
		 -= .pages[.index].chunk.rowGroup.NumRows
		.index++
	}

	if .index < len(.pages) {
		if  := .pages[.index].SeekToRow();  != nil {
			return 
		}
		for  := .index + 1;  < len(.pages); ++ {
			 := &.pages[]
			if  := .SeekToRow(0);  != nil {
				return 
			}
		}
	}
	return nil
}

func ( *columnPages) () error {
	var  error

	for  := range .pages {
		if  := .pages[].Close();  != nil {
			 = 
		}
	}

	.pages = nil
	.index = 0
	return 
}

// Depth returns the position of the column relative to the root.
func ( *Column) () int { return int(.depth) }

// MaxRepetitionLevel returns the maximum value of repetition levels on this
// column.
func ( *Column) () int { return int(.maxRepetitionLevel) }

// MaxDefinitionLevel returns the maximum value of definition levels on this
// column.
func ( *Column) () int { return int(.maxDefinitionLevel) }

// Index returns the position of the column in a row. Only leaf columns have a
// column index, the method returns -1 when called on non-leaf columns.
func ( *Column) () int { return int(.index) }

// GoType returns the Go type that best represents the parquet column.
func ( *Column) () reflect.Type { return goTypeOf() }

// Value returns the sub-value in base for the child column at the given
// index.
func ( *Column) ( reflect.Value) reflect.Value {
	return .MapIndex(reflect.ValueOf(&.schema.Name).Elem())
}

// String returns a human-readable string representation of the column.
func ( *Column) () string { return .path.String() + ": " + sprint(.Name(), ) }

func ( *Column) ( func(*Column)) {
	if len(.columns) == 0 {
		()
	} else {
		for ,  := range .columns {
			.()
		}
	}
}

func openColumns( *File) (*Column, error) {
	 := columnLoader{}

	,  := .open(, nil)
	if  != nil {
		return nil, 
	}

	// Validate that there aren't extra entries in the row group columns,
	// which would otherwise indicate that there are dangling data pages
	// in the file.
	for ,  := range .metadata.RowGroups {
		if .rowGroupColumnIndex != len(.Columns) {
			return nil, fmt.Errorf("row group at index %d contains %d columns but %d were referenced by the column schemas",
				, len(.Columns), .rowGroupColumnIndex)
		}
	}

	_,  = .setLevels(0, 0, 0, 0)
	return , 
}

func ( *Column) (, , ,  int) (int, error) {
	if  > MaxColumnDepth {
		return -1, fmt.Errorf("cannot represent parquet columns with more than %d nested levels: %s", MaxColumnDepth, .path)
	}
	if  > MaxColumnIndex {
		return -1, fmt.Errorf("cannot represent parquet rows with more than %d columns: %s", MaxColumnIndex, .path)
	}
	if  > MaxRepetitionLevel {
		return -1, fmt.Errorf("cannot represent parquet columns with more than %d repetition levels: %s", MaxRepetitionLevel, .path)
	}
	if  > MaxDefinitionLevel {
		return -1, fmt.Errorf("cannot represent parquet columns with more than %d definition levels: %s", MaxDefinitionLevel, .path)
	}

	switch schemaRepetitionTypeOf(.schema) {
	case format.Optional:
		++
	case format.Repeated:
		++
		++
	}

	.depth = int8()
	.maxRepetitionLevel = byte()
	.maxDefinitionLevel = byte()
	++

	if len(.columns) > 0 {
		.index = -1
	} else {
		.index = int16()
		++
	}

	var  error
	for ,  := range .columns {
		if ,  = .(, , , );  != nil {
			return -1, 
		}
	}
	return , nil
}

type columnLoader struct {
	schemaIndex         int
	columnOrderIndex    int
	rowGroupColumnIndex int
}

func ( *columnLoader) ( *File,  []string) (*Column, error) {
	 := &Column{
		file:   ,
		schema: &.metadata.Schema[.schemaIndex],
	}
	.path = columnPath().append(.schema.Name)

	.schemaIndex++
	 := int(.schema.NumChildren)

	if  == 0 {
		.typ = schemaElementTypeOf(.schema)

		if .columnOrderIndex < len(.metadata.ColumnOrders) {
			.order = &.metadata.ColumnOrders[.columnOrderIndex]
			.columnOrderIndex++
		}

		 := .metadata.RowGroups
		 := .rowGroupColumnIndex
		.rowGroupColumnIndex++

		.chunks = make([]*format.ColumnChunk, 0, len())
		.columnIndex = make([]*format.ColumnIndex, 0, len())
		.offsetIndex = make([]*format.OffsetIndex, 0, len())

		for ,  := range  {
			if  >= len(.Columns) {
				return nil, fmt.Errorf("row group at index %d does not have enough columns", )
			}
			.chunks = append(.chunks, &.Columns[])
		}

		if len(.columnIndexes) > 0 {
			for  := range  {
				if  >= len(.columnIndexes) {
					return nil, fmt.Errorf("row group at index %d does not have enough column index pages", )
				}
				.columnIndex = append(.columnIndex, &.columnIndexes[])
			}
		}

		if len(.offsetIndexes) > 0 {
			for  := range  {
				if  >= len(.offsetIndexes) {
					return nil, fmt.Errorf("row group at index %d does not have enough offset index pages", )
				}
				.offsetIndex = append(.offsetIndex, &.offsetIndexes[])
			}
		}

		if len(.chunks) > 0 {
			// Pick the encoding and compression codec of the first chunk.
			//
			// Technically each column chunk may use a different compression
			// codec, and each page of the column chunk might have a different
			// encoding. Exposing these details does not provide a lot of value
			// to the end user.
			//
			// Programs that wish to determine the encoding and compression of
			// each page of the column should iterate through the pages and read
			// the page headers to determine which compression and encodings are
			// applied.
			for ,  := range .chunks[0].MetaData.Encoding {
				if .encoding == nil {
					.encoding = LookupEncoding()
				}
				if  != format.Plain &&  != format.RLE {
					.encoding = LookupEncoding()
					break
				}
			}
			.compression = LookupCompressionCodec(.chunks[0].MetaData.Codec)
		}

		return , nil
	}

	.typ = &groupType{}
	if  := .schema.LogicalType;  != nil && .Map != nil {
		.typ = &mapType{}
	} else if  != nil && .List != nil {
		.typ = &listType{}
	}
	.columns = make([]*Column, )

	for  := range .columns {
		if .schemaIndex >= len(.metadata.Schema) {
			return nil, fmt.Errorf("column %q has more children than there are schemas in the file: %d > %d",
				.schema.Name, .schemaIndex+1, len(.metadata.Schema))
		}

		var  error
		.columns[],  = .(, .path)
		if  != nil {
			return nil, fmt.Errorf("%s: %w", .schema.Name, )
		}
	}

	return , nil
}

func schemaElementTypeOf( *format.SchemaElement) Type {
	if  := .LogicalType;  != nil {
		// A logical type exists, the Type interface implementations in this
		// package are all based on the logical parquet types declared in the
		// format sub-package so we can return them directly via a pointer type
		// conversion.
		switch {
		case .UTF8 != nil:
			return (*stringType)(.UTF8)
		case .Map != nil:
			return (*mapType)(.Map)
		case .List != nil:
			return (*listType)(.List)
		case .Enum != nil:
			return (*enumType)(.Enum)
		case .Decimal != nil:
			// A parquet decimal can be one of several different physical types.
			if  := .Type;  != nil {
				var  Type
				switch  := Kind(*.Type);  {
				case Int32:
					 = Int32Type
				case Int64:
					 = Int64Type
				case FixedLenByteArray:
					if .TypeLength == nil {
						panic("DECIMAL using FIXED_LEN_BYTE_ARRAY must specify a length")
					}
					 = FixedLenByteArrayType(int(*.TypeLength))
				default:
					panic("DECIMAL must be of type INT32, INT64, or FIXED_LEN_BYTE_ARRAY but got " + .String())
				}
				return &decimalType{
					decimal: *.Decimal,
					Type:    ,
				}
			}
		case .Date != nil:
			return (*dateType)(.Date)
		case .Time != nil:
			return (*timeType)(.Time)
		case .Timestamp != nil:
			return (*timestampType)(.Timestamp)
		case .Integer != nil:
			return (*intType)(.Integer)
		case .Unknown != nil:
			return (*nullType)(.Unknown)
		case .Json != nil:
			return (*jsonType)(.Json)
		case .Bson != nil:
			return (*bsonType)(.Bson)
		case .UUID != nil:
			return (*uuidType)(.UUID)
		}
	}

	if  := .ConvertedType;  != nil {
		// This column contains no logical type but has a converted type, it
		// was likely created by an older parquet writer. Convert the legacy
		// type representation to the equivalent logical parquet type.
		switch * {
		case deprecated.UTF8:
			return &stringType{}
		case deprecated.Map:
			return &mapType{}
		case deprecated.MapKeyValue:
			return &groupType{}
		case deprecated.List:
			return &listType{}
		case deprecated.Enum:
			return &enumType{}
		case deprecated.Decimal:
			if .Scale != nil && .Precision != nil {
				// A parquet decimal can be one of several different physical types.
				if  := .Type;  != nil {
					var  Type
					switch  := Kind(*.Type);  {
					case Int32:
						 = Int32Type
					case Int64:
						 = Int64Type
					case FixedLenByteArray:
						if .TypeLength == nil {
							panic("DECIMAL using FIXED_LEN_BYTE_ARRAY must specify a length")
						}
						 = FixedLenByteArrayType(int(*.TypeLength))
					case ByteArray:
						 = ByteArrayType
					default:
						panic("DECIMAL must be of type INT32, INT64, BYTE_ARRAY or FIXED_LEN_BYTE_ARRAY but got " + .String())
					}
					return &decimalType{
						decimal: format.DecimalType{
							Scale:     *.Scale,
							Precision: *.Precision,
						},
						Type: ,
					}
				}
			}
		case deprecated.Date:
			return &dateType{}
		case deprecated.TimeMillis:
			return &timeType{IsAdjustedToUTC: true, Unit: Millisecond.TimeUnit()}
		case deprecated.TimeMicros:
			return &timeType{IsAdjustedToUTC: true, Unit: Microsecond.TimeUnit()}
		case deprecated.TimestampMillis:
			return &timestampType{IsAdjustedToUTC: true, Unit: Millisecond.TimeUnit()}
		case deprecated.TimestampMicros:
			return &timestampType{IsAdjustedToUTC: true, Unit: Microsecond.TimeUnit()}
		case deprecated.Uint8:
			return &unsignedIntTypes[0]
		case deprecated.Uint16:
			return &unsignedIntTypes[1]
		case deprecated.Uint32:
			return &unsignedIntTypes[2]
		case deprecated.Uint64:
			return &unsignedIntTypes[3]
		case deprecated.Int8:
			return &signedIntTypes[0]
		case deprecated.Int16:
			return &signedIntTypes[1]
		case deprecated.Int32:
			return &signedIntTypes[2]
		case deprecated.Int64:
			return &signedIntTypes[3]
		case deprecated.Json:
			return &jsonType{}
		case deprecated.Bson:
			return &bsonType{}
		case deprecated.Interval:
			// TODO
		}
	}

	if  := .Type;  != nil {
		// The column only has a physical type, convert it to one of the
		// primitive types supported by this package.
		switch  := Kind(*);  {
		case Boolean:
			return BooleanType
		case Int32:
			return Int32Type
		case Int64:
			return Int64Type
		case Int96:
			return Int96Type
		case Float:
			return FloatType
		case Double:
			return DoubleType
		case ByteArray:
			return ByteArrayType
		case FixedLenByteArray:
			if .TypeLength != nil {
				return FixedLenByteArrayType(int(*.TypeLength))
			}
		}
	}

	// If we reach this point, we are likely reading a parquet column that was
	// written with a non-standard type or is in a newer version of the format
	// than this package supports.
	return &nullType{}
}

func schemaRepetitionTypeOf( *format.SchemaElement) format.FieldRepetitionType {
	if .RepetitionType != nil {
		return *.RepetitionType
	}
	return format.Required
}

func ( *Column) ( []byte,  int32) ( *buffer,  error) {
	 = buffers.get(int())
	.data,  = .compression.Decode(.data, )
	if  != nil {
		.unref()
		 = nil
	}
	return , 
}

// DecodeDataPageV1 decodes a data page from the header, compressed data, and
// optional dictionary passed as arguments.
func ( *Column) ( DataPageHeaderV1,  []byte,  Dictionary) (Page, error) {
	return .decodeDataPageV1(, &buffer{data: }, , -1)
}

func ( *Column) ( DataPageHeaderV1,  *buffer,  Dictionary,  int32) (Page, error) {
	var  = .data
	var  error

	if isCompressed(.compression) {
		if ,  = .decompress(, );  != nil {
			return nil, fmt.Errorf("decompressing data page v1: %w", )
		}
		defer .unref()
		 = .data
	}

	var  = int(.NumValues())
	var  *buffer
	var  *buffer

	if .maxRepetitionLevel > 0 {
		 := lookupLevelEncoding(.RepetitionLevelEncoding(), .maxRepetitionLevel)
		, ,  = decodeLevelsV1(, , )
		if  != nil {
			return nil, fmt.Errorf("decoding repetition levels of data page v1: %w", )
		}
		defer .unref()
	}

	if .maxDefinitionLevel > 0 {
		 := lookupLevelEncoding(.DefinitionLevelEncoding(), .maxDefinitionLevel)
		, ,  = decodeLevelsV1(, , )
		if  != nil {
			return nil, fmt.Errorf("decoding definition levels of data page v1: %w", )
		}
		defer .unref()

		// Data pages v1 did not embed the number of null values,
		// so we have to compute it from the definition levels.
		 -= countLevelsNotEqual(.data, .maxDefinitionLevel)
	}

	return .decodeDataPage(, , , , , , )
}

// DecodeDataPageV2 decodes a data page from the header, compressed data, and
// optional dictionary passed as arguments.
func ( *Column) ( DataPageHeaderV2,  []byte,  Dictionary) (Page, error) {
	return .decodeDataPageV2(, &buffer{data: }, , -1)
}

func ( *Column) ( DataPageHeaderV2,  *buffer,  Dictionary,  int32) (Page, error) {
	var  = int(.NumValues())
	var  = .data
	var  error
	var  *buffer
	var  *buffer

	if  := .RepetitionLevelsByteLength();  > 0 {
		if .maxRepetitionLevel == 0 {
			// In some cases we've observed files which have a non-zero
			// repetition level despite the column not being repeated
			// (nor nested within a repeated column).
			//
			// See https://github.com/apache/parquet-testing/pull/24
			,  = skipLevelsV2(, )
		} else {
			 := lookupLevelEncoding(.RepetitionLevelEncoding(), .maxRepetitionLevel)
			, ,  = decodeLevelsV2(, , , )
		}
		if  != nil {
			return nil, fmt.Errorf("decoding repetition levels of data page v2: %w", io.ErrUnexpectedEOF)
		}
		if  != nil {
			defer .unref()
		}
	}

	if  := .DefinitionLevelsByteLength();  > 0 {
		if .maxDefinitionLevel == 0 {
			,  = skipLevelsV2(, )
		} else {
			 := lookupLevelEncoding(.DefinitionLevelEncoding(), .maxDefinitionLevel)
			, ,  = decodeLevelsV2(, , , )
		}
		if  != nil {
			return nil, fmt.Errorf("decoding definition levels of data page v2: %w", io.ErrUnexpectedEOF)
		}
		if  != nil {
			defer .unref()
		}
	}

	if isCompressed(.compression) && .IsCompressed() {
		if ,  = .decompress(, );  != nil {
			return nil, fmt.Errorf("decompressing data page v2: %w", )
		}
		defer .unref()
		 = .data
	}

	 -= int(.NumNulls())
	return .decodeDataPage(, , , , , , )
}

func ( *Column) ( DataPageHeader,  int, , ,  *buffer,  []byte,  Dictionary) (Page, error) {
	 := LookupEncoding(.Encoding())
	 := .Type()

	if isDictionaryEncoding() {
		// In some legacy configurations, the PLAIN_DICTIONARY encoding is used
		// on data page headers to indicate that the page contains indexes into
		// the dictionary page, but the page is still encoded using the RLE
		// encoding in this case, so we convert it to RLE_DICTIONARY.
		 = &RLEDictionary
		 = indexedPageType{newIndexedType(, )}
	}

	var ,  *buffer
	var  []byte
	var  []uint32

	if .CanDecodeInPlace() {
		 = 
		 = 
	} else {
		 = buffers.get(.EstimateDecodeSize(, , ))
		defer .unref()
		 = .data
	}

	// Page offsets not needed when dictionary-encoded
	if .Kind() == ByteArray && !isDictionaryEncoding() {
		 = buffers.get(4 * ( + 1))
		defer .unref()
		 = unsafecast.Slice[uint32](.data)
	}

	 := .NewValues(, )
	,  := .Decode(, , )
	if  != nil {
		return nil, 
	}

	 := .NewPage(.Index(), , )
	switch {
	case .maxRepetitionLevel > 0:
		 = newRepeatedPage(
			,
			.maxRepetitionLevel,
			.maxDefinitionLevel,
			.data,
			.data,
		)
	case .maxDefinitionLevel > 0:
		 = newOptionalPage(
			,
			.maxDefinitionLevel,
			.data,
		)
	}

	return newBufferedPage(, , , , ), nil
}

func decodeLevelsV1( encoding.Encoding,  int,  []byte) (*buffer, []byte, error) {
	if len() < 4 {
		return nil, , io.ErrUnexpectedEOF
	}
	 := 4
	 := 4 + int(binary.LittleEndian.Uint32())
	if  > len() {
		return nil, , io.ErrUnexpectedEOF
	}
	,  := decodeLevels(, , [:])
	return , [:], 
}

func decodeLevelsV2( encoding.Encoding,  int,  []byte,  int64) (*buffer, []byte, error) {
	,  := decodeLevels(, , [:])
	return , [:], 
}

func decodeLevels( encoding.Encoding,  int,  []byte) ( *buffer,  error) {
	 = buffers.get()
	.data,  = .DecodeLevels(.data, )
	if  != nil {
		.unref()
		 = nil
	} else {
		switch {
		case len(.data) < :
			 = fmt.Errorf("decoding level expected %d values but got only %d", , len(.data))
		case len(.data) > :
			.data = .data[:]
		}
	}
	return , 
}

func skipLevelsV2( []byte,  int64) ([]byte, error) {
	if  >= int64(len()) {
		return , io.ErrUnexpectedEOF
	}
	return [:], nil
}

// DecodeDictionary decodes a data page from the header and compressed data
// passed as arguments.
func ( *Column) ( DictionaryPageHeader,  []byte) (Dictionary, error) {
	return .decodeDictionary(, &buffer{data: }, -1)
}

func ( *Column) ( DictionaryPageHeader,  *buffer,  int32) (Dictionary, error) {
	 := .data

	if isCompressed(.compression) {
		var  error
		if ,  = .decompress(, );  != nil {
			return nil, fmt.Errorf("decompressing dictionary page: %w", )
		}
		defer .unref()
		 = .data
	}

	 := .Type()
	 := .Encoding()
	if  == format.PlainDictionary {
		 = format.Plain
	}

	// Dictionaries always have PLAIN encoding, so we need to allocate offsets for the decoded page.
	 := int(.NumValues())
	 := .EstimateDecodeSize(, , LookupEncoding())
	 := .NewValues(make([]byte, 0, ), make([]uint32, 0, ))
	,  := .Decode(, , LookupEncoding())
	if  != nil {
		return nil, 
	}
	return .NewDictionary(int(.index), , ), nil
}

var (
	_ Node = (*Column)(nil)
)