// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package iceberg

import (
	
	
	
	
	

	
	
	
)

// ManifestContent indicates the type of data inside of the files
// described by a manifest. This will indicate whether the data files
// contain active data or deleted rows.
type ManifestContent int32

const (
	ManifestContentData    ManifestContent = 0
	ManifestContentDeletes ManifestContent = 1
)

type FieldSummary struct {
	ContainsNull bool    `avro:"contains_null"`
	ContainsNaN  *bool   `avro:"contains_nan"`
	LowerBound   *[]byte `avro:"lower_bound"`
	UpperBound   *[]byte `avro:"upper_bound"`
}

// ManifestV1Builder is a helper for building a V1 manifest file
// struct which will conform to the ManifestFile interface.
type ManifestV1Builder struct {
	m *manifestFileV1
}

// NewManifestV1Builder is passed all of the required fields and then allows
// all of the optional fields to be set by calling the corresponding methods
// before calling [ManifestV1Builder.Build] to construct the object.
func ( string,  int64,  int32,  int64) *ManifestV1Builder {
	return &ManifestV1Builder{
		m: &manifestFileV1{
			Path:            ,
			Len:             ,
			SpecID:          ,
			AddedSnapshotID: ,
		},
	}
}

func ( *ManifestV1Builder) ( int32) *ManifestV1Builder {
	.m.AddedFilesCount = &
	return 
}

func ( *ManifestV1Builder) ( int32) *ManifestV1Builder {
	.m.ExistingFilesCount = &
	return 
}

func ( *ManifestV1Builder) ( int32) *ManifestV1Builder {
	.m.DeletedFilesCount = &
	return 
}

func ( *ManifestV1Builder) ( int64) *ManifestV1Builder {
	.m.AddedRowsCount = &
	return 
}

func ( *ManifestV1Builder) ( int64) *ManifestV1Builder {
	.m.ExistingRowsCount = &
	return 
}

func ( *ManifestV1Builder) ( int64) *ManifestV1Builder {
	.m.DeletedRowsCount = &
	return 
}

func ( *ManifestV1Builder) ( []FieldSummary) *ManifestV1Builder {
	.m.PartitionList = &
	return 
}

func ( *ManifestV1Builder) ( []byte) *ManifestV1Builder {
	.m.Key = 
	return 
}

// Build returns the constructed manifest file, after calling Build this
// builder should not be used further as we avoid copying by just returning
// a pointer to the constructed manifest file. Further calls to the modifier
// methods after calling build would modify the constructed ManifestFile.
func ( *ManifestV1Builder) () ManifestFile {
	return .m
}

type fallbackManifestFileV1 struct {
	manifestFileV1
	AddedSnapshotID *int64 `avro:"added_snapshot_id"`
}

func ( *fallbackManifestFileV1) () *manifestFileV1 {
	.manifestFileV1.AddedSnapshotID = *.AddedSnapshotID
	return &.manifestFileV1
}

type manifestFileV1 struct {
	Path               string          `avro:"manifest_path"`
	Len                int64           `avro:"manifest_length"`
	SpecID             int32           `avro:"partition_spec_id"`
	AddedSnapshotID    int64           `avro:"added_snapshot_id"`
	AddedFilesCount    *int32          `avro:"added_data_files_count"`
	ExistingFilesCount *int32          `avro:"existing_data_files_count"`
	DeletedFilesCount  *int32          `avro:"deleted_data_files_count"`
	AddedRowsCount     *int64          `avro:"added_rows_count"`
	ExistingRowsCount  *int64          `avro:"existing_rows_count"`
	DeletedRowsCount   *int64          `avro:"deleted_rows_count"`
	PartitionList      *[]FieldSummary `avro:"partitions"`
	Key                []byte          `avro:"key_metadata"`
}

func (*manifestFileV1) () int             { return 1 }
func ( *manifestFileV1) () string       { return .Path }
func ( *manifestFileV1) () int64          { return .Len }
func ( *manifestFileV1) () int32 { return .SpecID }
func ( *manifestFileV1) () ManifestContent {
	return ManifestContentData
}
func ( *manifestFileV1) () int64 {
	return .AddedSnapshotID
}

func ( *manifestFileV1) () int32 {
	if .AddedFilesCount == nil {
		return 0
	}
	return *.AddedFilesCount
}

func ( *manifestFileV1) () int32 {
	if .ExistingFilesCount == nil {
		return 0
	}
	return *.ExistingFilesCount
}

func ( *manifestFileV1) () int32 {
	if .DeletedFilesCount == nil {
		return 0
	}
	return *.DeletedFilesCount
}

func ( *manifestFileV1) () int64 {
	if .AddedRowsCount == nil {
		return 0
	}
	return *.AddedRowsCount
}

func ( *manifestFileV1) () int64 {
	if .ExistingRowsCount == nil {
		return 0
	}
	return *.ExistingRowsCount
}

func ( *manifestFileV1) () int64 {
	if .DeletedRowsCount == nil {
		return 0
	}
	return *.DeletedRowsCount
}

func ( *manifestFileV1) () bool {
	return .AddedFilesCount == nil || *.AddedFilesCount > 0
}

func ( *manifestFileV1) () bool {
	return .ExistingFilesCount == nil || *.ExistingFilesCount > 0
}

func ( *manifestFileV1) () int64    { return 0 }
func ( *manifestFileV1) () int64 { return 0 }
func ( *manifestFileV1) () []byte   { return .Key }
func ( *manifestFileV1) () []FieldSummary {
	if .PartitionList == nil {
		return nil
	}
	return *.PartitionList
}

func ( *manifestFileV1) ( objstore.Bucket,  bool) ([]ManifestEntry, *Schema, error) {
	return fetchManifestEntries(, , )
}

// ManifestV2Builder is a helper for building a V2 manifest file
// struct which will conform to the ManifestFile interface.
type ManifestV2Builder struct {
	m *manifestFileV2
}

// NewManifestV2Builder is constructed with the primary fields, with the remaining
// fields set to their zero value unless modified by calling the corresponding
// methods of the builder. Then calling [ManifestV2Builder.Build] to retrieve the
// constructed ManifestFile.
func ( string,  int64,  int32,  ManifestContent,  int64) *ManifestV2Builder {
	return &ManifestV2Builder{
		m: &manifestFileV2{
			Path:            ,
			Len:             ,
			SpecID:          ,
			Content:         ,
			AddedSnapshotID: ,
		},
	}
}

func ( *ManifestV2Builder) (,  int64) *ManifestV2Builder {
	.m.SeqNumber, .m.MinSeqNumber = , 
	return 
}

func ( *ManifestV2Builder) ( int32) *ManifestV2Builder {
	.m.AddedFilesCount = 
	return 
}

func ( *ManifestV2Builder) ( int32) *ManifestV2Builder {
	.m.ExistingFilesCount = 
	return 
}

func ( *ManifestV2Builder) ( int32) *ManifestV2Builder {
	.m.DeletedFilesCount = 
	return 
}

func ( *ManifestV2Builder) ( int64) *ManifestV2Builder {
	.m.AddedRowsCount = 
	return 
}

func ( *ManifestV2Builder) ( int64) *ManifestV2Builder {
	.m.ExistingRowsCount = 
	return 
}

func ( *ManifestV2Builder) ( int64) *ManifestV2Builder {
	.m.DeletedRowsCount = 
	return 
}

func ( *ManifestV2Builder) ( []FieldSummary) *ManifestV2Builder {
	.m.PartitionList = &
	return 
}

func ( *ManifestV2Builder) ( []byte) *ManifestV2Builder {
	.m.Key = 
	return 
}

// Build returns the constructed manifest file, after calling Build this
// builder should not be used further as we avoid copying by just returning
// a pointer to the constructed manifest file. Further calls to the modifier
// methods after calling build would modify the constructed ManifestFile.
func ( *ManifestV2Builder) () ManifestFile {
	return .m
}

type manifestFileV2 struct {
	Path               string          `avro:"manifest_path"`
	Len                int64           `avro:"manifest_length"`
	SpecID             int32           `avro:"partition_spec_id"`
	Content            ManifestContent `avro:"content"`
	SeqNumber          int64           `avro:"sequence_number"`
	MinSeqNumber       int64           `avro:"min_sequence_number"`
	AddedSnapshotID    int64           `avro:"added_snapshot_id"`
	AddedFilesCount    int32           `avro:"added_files_count"`
	ExistingFilesCount int32           `avro:"existing_files_count"`
	DeletedFilesCount  int32           `avro:"deleted_files_count"`
	AddedRowsCount     int64           `avro:"added_rows_count"`
	ExistingRowsCount  int64           `avro:"existing_rows_count"`
	DeletedRowsCount   int64           `avro:"deleted_rows_count"`
	PartitionList      *[]FieldSummary `avro:"partitions"`
	Key                []byte          `avro:"key_metadata"`
}

func (*manifestFileV2) () int { return 2 }

func ( *manifestFileV2) () string                 { return .Path }
func ( *manifestFileV2) () int64                    { return .Len }
func ( *manifestFileV2) () int32           { return .SpecID }
func ( *manifestFileV2) () ManifestContent { return .Content }
func ( *manifestFileV2) () int64 {
	return .AddedSnapshotID
}

func ( *manifestFileV2) () int32 {
	return .AddedFilesCount
}

func ( *manifestFileV2) () int32 {
	return .ExistingFilesCount
}

func ( *manifestFileV2) () int32 {
	return .DeletedFilesCount
}

func ( *manifestFileV2) () int64 {
	return .AddedRowsCount
}

func ( *manifestFileV2) () int64 {
	return .ExistingRowsCount
}

func ( *manifestFileV2) () int64 {
	return .DeletedRowsCount
}

func ( *manifestFileV2) () int64    { return .SeqNumber }
func ( *manifestFileV2) () int64 { return .MinSeqNumber }
func ( *manifestFileV2) () []byte   { return .Key }

func ( *manifestFileV2) () []FieldSummary {
	if .PartitionList == nil {
		return nil
	}
	return *.PartitionList
}

func ( *manifestFileV2) () bool {
	return .AddedFilesCount > 0
}

func ( *manifestFileV2) () bool {
	return .ExistingFilesCount > 0
}

func ( *manifestFileV2) ( objstore.Bucket,  bool) ([]ManifestEntry, *Schema, error) {
	return fetchManifestEntries(, , )
}

func fetchManifestEntries( ManifestFile,  objstore.Bucket,  bool) ([]ManifestEntry, *Schema, error) {
	,  := .Get(context.TODO(), .FilePath())
	if  != nil {
		return nil, nil, 
	}
	defer .Close()

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

	// Extract the table shcmema from the metadata
	 := &Schema{}
	 := .Metadata()
	if  := json.Unmarshal(["schema"], );  != nil {
		return nil, nil, fmt.Errorf("failed to unmarshal schema: %w", )
	}

	,  := true, false
	if string(["format-version"]) == "2" {
		 = false
	} else {
		,  := avro.ParseBytes(.Metadata()["avro.schema"])
		if  != nil {
			return nil, nil, 
		}

		for ,  := range .(*avro.RecordSchema).Fields() {
			if .Name() == "snapshot_id" {
				if .Type().Type() == avro.Union {
					 = true
				}
				break
			}
		}
	}

	 := make([]ManifestEntry, 0)
	for .HasNext() {
		var  ManifestEntry
		if  {
			if  {
				 = &fallbackManifestEntryV1{
					manifestEntryV1: manifestEntryV1{
						Data: &dataFile{},
					},
				}
			} else {
				 = &manifestEntryV1{
					Data: &dataFile{},
				}
			}
		} else {
			 = &manifestEntryV2{
				Data: &dataFile{},
			}
		}

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

		if  {
			 = .(*fallbackManifestEntryV1).toEntry()
		}

		if ! || .Status() != EntryStatusDELETED {
			.inheritSeqNum()
			 = append(, )
		}
	}

	return , , .Error()
}

// ManifestFile is the interface which covers both V1 and V2 manifest files.
type ManifestFile interface {
	// Version returns the version number of this manifest file.
	// It should be 1 or 2.
	Version() int
	// FilePath is the location URI of this manifest file.
	FilePath() string
	// Length is the length in bytes of the manifest file.
	Length() int64
	// PartitionSpecID is the ID of the partition spec used to write
	// this manifest. It must be listed in the table metadata
	// partition-specs.
	PartitionSpecID() int32
	// ManifestContent is the type of files tracked by this manifest,
	// either data or delete files. All v1 manifests track data files.
	ManifestContent() ManifestContent
	// SnapshotID is the ID of the snapshot where this manifest file
	// was added.
	SnapshotID() int64
	// AddedDataFiles returns the number of entries in the manifest that
	// have the status of EntryStatusADDED.
	AddedDataFiles() int32
	// ExistingDataFiles returns the number of entries in the manifest
	// which have the status of EntryStatusEXISTING.
	ExistingDataFiles() int32
	// DeletedDataFiles returns the number of entries in the manifest
	// which have the status of EntryStatusDELETED.
	DeletedDataFiles() int32
	// AddedRows returns the number of rows in all files of the manifest
	// that have status EntryStatusADDED.
	AddedRows() int64
	// ExistingRows returns the number of rows in all files of the manifest
	// which have status EntryStatusEXISTING.
	ExistingRows() int64
	// DeletedRows returns the number of rows in all files of the manifest
	// which have status EntryStatusDELETED.
	DeletedRows() int64
	// SequenceNum returns the sequence number when this manifest was
	// added to the table. Will be 0 for v1 manifest lists.
	SequenceNum() int64
	// MinSequenceNum is the minimum data sequence number of all live data
	// or delete files in the manifest. Will be 0 for v1 manifest lists.
	MinSequenceNum() int64
	// KeyMetadata returns implementation-specific key metadata for encryption
	// if it exists in the manifest list.
	KeyMetadata() []byte
	// Partitions returns a list of field summaries for each partition
	// field in the spec. Each field in the list corresponds to a field in
	// the manifest file's partition spec.
	Partitions() []FieldSummary

	// HasAddedFiles returns true if AddedDataFiles > 0 or if it was null.
	HasAddedFiles() bool
	// HasExistingFiles returns true if ExistingDataFiles > 0 or if it was null.
	HasExistingFiles() bool
	// FetchEntries reads the manifest list file to fetch the list of
	// manifest entries using the provided bucket. It will return the schema of the table
	// when the manifest was written.
	// If discardDeleted is true, entries for files containing deleted rows
	// will be skipped.
	FetchEntries(bucket objstore.Bucket, discardDeleted bool) ([]ManifestEntry, *Schema, error)
}

// ReadManifestList reads in an avro manifest list file and returns a slice
// of manifest files or an error if one is encountered.
func ( io.Reader) ([]ManifestFile, error) {
	,  := ocf.NewDecoder()
	if  != nil {
		return nil, 
	}

	,  := avro.ParseBytes(.Metadata()["avro.schema"])
	if  != nil {
		return nil, 
	}

	var  bool
	for ,  := range .(*avro.RecordSchema).Fields() {
		if .Name() == "added_snapshot_id" {
			if .Type().Type() == avro.Union {
				 = true
			}
			break
		}
	}

	 := make([]ManifestFile, 0)
	for .HasNext() {
		var  ManifestFile
		if string(.Metadata()["format-version"]) == "2" {
			 = &manifestFileV2{}
		} else {
			if  {
				 = &fallbackManifestFileV1{}
			} else {
				 = &manifestFileV1{}
			}
		}

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

		if  {
			 = .(*fallbackManifestFileV1).toManifest()
		}

		 = append(, )
	}

	return , .Error()
}

// ManifestEntryStatus defines constants for the entry status of
// existing, added or deleted.
type ManifestEntryStatus int8

const (
	EntryStatusEXISTING ManifestEntryStatus = 0
	EntryStatusADDED    ManifestEntryStatus = 1
	EntryStatusDELETED  ManifestEntryStatus = 2
)

// ManifestEntryContent defines constants for the type of file contents
// in the file entries. Data, Position based deletes and equality based
// deletes.
type ManifestEntryContent int8

const (
	EntryContentData       ManifestEntryContent = 0
	EntryContentPosDeletes ManifestEntryContent = 1
	EntryContentEqDeletes  ManifestEntryContent = 2
)

// FileFormat defines constants for the format of data files.
type FileFormat string

const (
	AvroFile    FileFormat = "AVRO"
	OrcFile     FileFormat = "ORC"
	ParquetFile FileFormat = "PARQUET"
)

type colMap[,  any] struct {
	Key    `avro:"key"`
	Value  `avro:"value"`
}

// TODO(thor) revisit this I had copilot write it
func avroColMapFromMap[ comparable,  any]( map[]) *[]colMap[, ] {
	if  == nil {
		return nil
	}

	 := make([]colMap[, ], 0, len())
	for ,  := range  {
		 = append(, colMap[, ]{Key: , Value: })
	}
	return &
}

func avroColMapToMap[ comparable,  any]( *[]colMap[, ]) map[] {
	if  == nil {
		return nil
	}

	 := make(map[])
	for ,  := range * {
		[.Key] = .Value
	}
	return 
}

type dataFile struct {
	Content          ManifestEntryContent   `avro:"content"`
	Path             string                 `avro:"file_path"`
	Format           FileFormat             `avro:"file_format"`
	PartitionData    map[string]any         `avro:"partition"`
	RecordCount      int64                  `avro:"record_count"`
	FileSize         int64                  `avro:"file_size_in_bytes"`
	BlockSizeInBytes int64                  `avro:"block_size_in_bytes"`
	ColSizes         *[]colMap[int, int64]  `avro:"column_sizes"`
	ValCounts        *[]colMap[int, int64]  `avro:"value_counts"`
	NullCounts       *[]colMap[int, int64]  `avro:"null_value_counts"`
	NaNCounts        *[]colMap[int, int64]  `avro:"nan_value_counts"`
	DistinctCounts   *[]colMap[int, int64]  `avro:"distinct_counts"`
	LowerBounds      *[]colMap[int, []byte] `avro:"lower_bounds"`
	UpperBounds      *[]colMap[int, []byte] `avro:"upper_bounds"`
	Key              *[]byte                `avro:"key_metadata"`
	Splits           *[]int64               `avro:"split_offsets"`
	EqualityIDs      *[]int                 `avro:"equality_ids"`
	SortOrder        *int                   `avro:"sort_order_id"`

	colSizeMap     map[int]int64
	valCntMap      map[int]int64
	nullCntMap     map[int]int64
	nanCntMap      map[int]int64
	distinctCntMap map[int]int64
	lowerBoundMap  map[int][]byte
	upperBoundMap  map[int][]byte

	initMaps sync.Once
}

func ( *dataFile) () {
	.initMaps.Do(func() {
		.colSizeMap = avroColMapToMap(.ColSizes)
		.valCntMap = avroColMapToMap(.ValCounts)
		.nullCntMap = avroColMapToMap(.NullCounts)
		.nanCntMap = avroColMapToMap(.NaNCounts)
		.distinctCntMap = avroColMapToMap(.DistinctCounts)
		.lowerBoundMap = avroColMapToMap(.LowerBounds)
		.upperBoundMap = avroColMapToMap(.UpperBounds)
	})
}

func ( *dataFile) () ManifestEntryContent { return .Content }
func ( *dataFile) () string                  { return .Path }
func ( *dataFile) () FileFormat            { return .Format }
func ( *dataFile) () map[string]any         { return .PartitionData }
func ( *dataFile) () int64                      { return .RecordCount }
func ( *dataFile) () int64              { return .FileSize }

func ( *dataFile) () map[int]int64 {
	.initializeMapData()
	return .colSizeMap
}

func ( *dataFile) () map[int]int64 {
	.initializeMapData()
	return .valCntMap
}

func ( *dataFile) () map[int]int64 {
	.initializeMapData()
	return .nullCntMap
}

func ( *dataFile) () map[int]int64 {
	.initializeMapData()
	return .nanCntMap
}

func ( *dataFile) () map[int]int64 {
	.initializeMapData()
	return .distinctCntMap
}

func ( *dataFile) () map[int][]byte {
	.initializeMapData()
	return .lowerBoundMap
}

func ( *dataFile) () map[int][]byte {
	.initializeMapData()
	return .upperBoundMap
}

func ( *dataFile) () []byte {
	if .Key == nil {
		return nil
	}
	return *.Key
}

func ( *dataFile) () []int64 {
	if .Splits == nil {
		return nil
	}
	return *.Splits
}

func ( *dataFile) () []int {
	if .EqualityIDs == nil {
		return nil
	}
	return .()
}

func ( *dataFile) () *int { return .SortOrder }

type manifestEntryV1 struct {
	EntryStatus ManifestEntryStatus `avro:"status"`
	Snapshot    int64               `avro:"snapshot_id"`
	SeqNum      *int64
	FileSeqNum  *int64
	Data        DataFile `avro:"data_file"`
}

type fallbackManifestEntryV1 struct {
	manifestEntryV1
	Snapshot *int64 `avro:"snapshot_id"`
}

func ( *fallbackManifestEntryV1) () *manifestEntryV1 {
	.manifestEntryV1.Snapshot = *.Snapshot
	return &.manifestEntryV1
}

func ( *manifestEntryV1) ( ManifestFile) {}

func ( *manifestEntryV1) () ManifestEntryStatus { return .EntryStatus }
func ( *manifestEntryV1) () int64           { return .Snapshot }

func ( *manifestEntryV1) () int64 {
	if .SeqNum == nil {
		return 0
	}
	return *.SeqNum
}

func ( *manifestEntryV1) () *int64 {
	return .FileSeqNum
}

func ( *manifestEntryV1) () DataFile { return .Data }

type manifestEntryV2 struct {
	EntryStatus ManifestEntryStatus `avro:"status"`
	Snapshot    *int64              `avro:"snapshot_id"`
	SeqNum      *int64              `avro:"sequence_number"`
	FileSeqNum  *int64              `avro:"file_sequence_number"`
	Data        DataFile            `avro:"data_file"`
}

func ( *manifestEntryV2) ( ManifestFile) {
	if .Snapshot == nil {
		 := .SnapshotID()
		.Snapshot = &
	}

	 := .SequenceNum()
	if .SeqNum == nil && ( == 0 || .EntryStatus == EntryStatusADDED) {
		.SeqNum = &
	}

	if .FileSeqNum == nil && ( == 0 || .EntryStatus == EntryStatusADDED) {
		.FileSeqNum = &
	}
}

func ( *manifestEntryV2) () ManifestEntryStatus { return .EntryStatus }
func ( *manifestEntryV2) () int64 {
	if .Snapshot == nil {
		return 0
	}
	return *.Snapshot
}

func ( *manifestEntryV2) () int64 {
	if .SeqNum == nil {
		return 0
	}
	return *.SeqNum
}

func ( *manifestEntryV2) () *int64 {
	return .FileSeqNum
}

func ( *manifestEntryV2) () DataFile { return .Data }

// DataFile is the interface for reading the information about a
// given data file indicated by an entry in a manifest list.
type DataFile interface {
	// ContentType is the type of the content stored by the data file,
	// either Data, Equality deletes, or Position deletes. All v1 files
	// are Data files.
	ContentType() ManifestEntryContent
	// FilePath is the full URI for the file, complete with FS scheme.
	FilePath() string
	// FileFormat is the format of the data file, AVRO, Orc, or Parquet.
	FileFormat() FileFormat
	// Partition returns a mapping of field name to partition value for
	// each of the partition spec's fields.
	Partition() map[string]any
	// Count returns the number of records in this file.
	Count() int64
	// FileSizeBytes is the total file size in bytes.
	FileSizeBytes() int64
	// ColumnSizes is a mapping from column id to the total size on disk
	// of all regions that store the column. Does not include bytes
	// necessary to read other columns, like footers. Map will be nil for
	// row-oriented formats (avro).
	ColumnSizes() map[int]int64
	// ValueCounts is a mapping from column id to the number of values
	// in the column, including null and NaN values.
	ValueCounts() map[int]int64
	// NullValueCounts is a mapping from column id to the number of
	// null values in the column.
	NullValueCounts() map[int]int64
	// NaNValueCounts is a mapping from column id to the number of NaN
	// values in the column.
	NaNValueCounts() map[int]int64
	// DistictValueCounts is a mapping from column id to the number of
	// distinct values in the column. Distinct counts must be derived
	// using values in the file by counting or using sketches, but not
	// using methods like merging existing distinct counts.
	DistinctValueCounts() map[int]int64
	// LowerBoundValues is a mapping from column id to the lower bounded
	// value of the column, serialized as binary. Each value in the column
	// must be less than or requal to all non-null, non-NaN values in the
	// column for the file.
	LowerBoundValues() map[int][]byte
	// UpperBoundValues is a mapping from column id to the upper bounded
	// value of the column, serialized as binary. Each value in the column
	// must be greater than or equal to all non-null, non-NaN values in
	// the column for the file.
	UpperBoundValues() map[int][]byte
	// KeyMetadata is implementation-specific key metadata for encryption.
	KeyMetadata() []byte
	// SplitOffsets are the split offsets for the data file. For example,
	// all row group offsets in a Parquet file. Must be sorted ascending.
	SplitOffsets() []int64
	// EqualityFieldIDs are used to determine row equality in equality
	// delete files. It is required when the content type is
	// EntryContentEqDeletes.
	EqualityFieldIDs() []int
	// SortOrderID returns the id representing the sort order for this
	// file, or nil if there is no sort order.
	SortOrderID() *int
}

// ManifestEntry is an interface for both v1 and v2 manifest entries.
type ManifestEntry interface {
	// Status returns the type of the file tracked by this entry.
	// Deletes are informational only and not used in scans.
	Status() ManifestEntryStatus
	// SnapshotID is the id where the file was added, or deleted,
	// if null it is inherited from the manifest list.
	SnapshotID() int64
	// SequenceNum returns the data sequence number of the file.
	// If it was null and the status is EntryStatusADDED then it
	// is inherited from the manifest list.
	SequenceNum() int64
	// FileSequenceNum returns the file sequence number indicating
	// when the file was added. If it was null and the status is
	// EntryStatusADDED then it is inherited from the manifest list.
	FileSequenceNum() *int64
	// DataFile provides the information about the data file indicated
	// by this manifest entry.
	DataFile() DataFile

	inheritSeqNum(manifest ManifestFile)
}

var PositionalDeleteSchema = NewSchema(0,
	NestedField{ID: 2147483546, Type: PrimitiveTypes.String, Name: "file_path", Required: true},
	NestedField{ID: 2147483545, Type: PrimitiveTypes.Int32, Name: "pos", Required: true},
)