package writer

import (
	
	
	

	
	

	
)

type ValueWriter interface {
	// Write writes a slice of values to the underlying builder (slow path).
	Write([]parquet.Value)
}

type PageWriter interface {
	ValueWriter
	// WritePage is the optimized path for writing a page of values to the
	// underlying builder. There are cases in which the given page cannot be
	// written directly, in which case ErrCannotWritePageDirectly is returned.
	// The caller should fall back to writing values.
	WritePage(parquet.Page) error
}

var ErrCannotWritePageDirectly = errors.New("cannot write page directly")

type writerBase struct{}

func ( writerBase) ( parquet.Page) bool {
	// Currently, for most writers, only pages with zero nulls and no dictionary
	// can be written.
	return .NumNulls() == 0 && .Dictionary() == nil
}

type binaryValueWriter struct {
	writerBase
	b *builder.OptBinaryBuilder
}

type NewWriterFunc func(b builder.ColumnBuilder, numValues int) ValueWriter

func ( builder.ColumnBuilder,  int) ValueWriter {
	return &binaryValueWriter{
		b: .(*builder.OptBinaryBuilder),
	}
}

func ( *binaryValueWriter) ( []parquet.Value) {
	if  := .b.AppendParquetValues();  != nil {
		panic("unable to write value") // TODO: handle this error gracefully
	}
}

func ( *binaryValueWriter) ( parquet.Page) error {
	if !.canWritePageDirectly() {
		return ErrCannotWritePageDirectly
	}
	 := .Data()
	return .b.AppendData(.ByteArray())
}

type int64ValueWriter struct {
	writerBase
	b *builder.OptInt64Builder
}

func ( builder.ColumnBuilder,  int) ValueWriter {
	 := &int64ValueWriter{
		b: .(*builder.OptInt64Builder),
	}
	return 
}

func ( *int64ValueWriter) ( []parquet.Value) {
	.b.AppendParquetValues()
}

func ( *int64ValueWriter) ( parquet.Page) error {
	if !.canWritePageDirectly() {
		return ErrCannotWritePageDirectly
	}
	// No nulls in page.
	 := .Data()
	.b.AppendData(.Int64())
	return nil
}

type uint64ValueWriter struct {
	b *array.Uint64Builder
}

func ( builder.ColumnBuilder,  int) ValueWriter {
	 := &uint64ValueWriter{
		b: .(*array.Uint64Builder),
	}
	.b.Reserve()
	return 
}

func ( *uint64ValueWriter) ( []parquet.Value) {
	// Depending on the nullability of the column this could be optimized
	// further by reading uint64s directly and adding all of them at once
	// to the array builder.
	for ,  := range  {
		if .IsNull() {
			.b.AppendNull()
		} else {
			.b.Append(uint64(.Int64()))
		}
	}
}

type repeatedValueWriter struct {
	b      *builder.ListBuilder
	values ValueWriter
}

func ( func( builder.ColumnBuilder,  int) ValueWriter) func( builder.ColumnBuilder,  int) ValueWriter {
	return func( builder.ColumnBuilder,  int) ValueWriter {
		 := .(*builder.ListBuilder)

		return &repeatedValueWriter{
			b:      ,
			values: (.ValueBuilder(), ),
		}
	}
}

func ( *repeatedValueWriter) ( []parquet.Value) {
	 := false
	 := 0
	for ,  := range  {
		if .RepetitionLevel() == 0 {
			if  {
				.b.Append(true)
				.values.Write([:])
			}

			if .DefinitionLevel() == 0 {
				.b.AppendNull()
				 = false
				 =  + 1
			} else {
				 = true
				 = 
			}
		}
	}

	// write final list
	if len([:]) > 0 {
		.b.Append(true)
		.values.Write([:])
	}
}

type float64ValueWriter struct {
	b   *array.Float64Builder
	buf []float64
}

func ( builder.ColumnBuilder,  int) ValueWriter {
	 := &float64ValueWriter{
		b: .(*array.Float64Builder),
	}
	.b.Reserve()
	return 
}

func ( *float64ValueWriter) ( []parquet.Value) {
	for ,  := range  {
		if .IsNull() {
			.b.AppendNull()
		} else {
			.b.Append(.Double())
		}
	}
}

func ( *float64ValueWriter) ( parquet.Page) error {
	,  := .Values().(parquet.DoubleReader)
	if ! {
		return ErrCannotWritePageDirectly
	}

	if .buf == nil {
		.buf = make([]float64, .NumValues())
	}
	 := .buf
	for {
		,  := .ReadDoubles()
		if  != nil &&  != io.EOF {
			return fmt.Errorf("read values: %w", )
		}

		.b.AppendValues([:], nil)
		if  == io.EOF {
			break
		}
	}
	return nil
}

type booleanValueWriter struct {
	writerBase
	b *builder.OptBooleanBuilder
}

func ( builder.ColumnBuilder,  int) ValueWriter {
	 := &booleanValueWriter{
		b: .(*builder.OptBooleanBuilder),
	}
	.b.Reserve()
	return 
}

func ( *booleanValueWriter) ( []parquet.Value) {
	.b.AppendParquetValues()
}

func ( *booleanValueWriter) ( parquet.Page) error {
	if !.canWritePageDirectly() {
		return ErrCannotWritePageDirectly
	}
	 := .Data()
	.b.Append(.Boolean(), int(.NumValues()))
	return nil
}

type structWriter struct {
	// offset is the column index offset that this node has in the overall schema
	offset int
	b      *array.StructBuilder
}

func ( int) NewWriterFunc {
	return func( builder.ColumnBuilder,  int) ValueWriter {
		return &structWriter{
			offset: ,
			b:      .(*array.StructBuilder),
		}
	}
}

func ( *structWriter) ( []parquet.Value) {
	 := 0
	for ,  := range  {
		if .RepetitionLevel() == 0 {
			++
			if  > .b.Len() {
				.b.Append(true)
			}
		}
	}
	// recursively search the struct builder for the leaf that matches the values column index
	,  := .findLeafBuilder([0].Column(), .offset, .b, )
	if ! {
		panic("unable to write values to builder")
	}
}

// findLeafBuilder is a recursive function to find the leaf builder whose column index matches the search index.
// It returns the number of leaves found for the given builder and if one of the leaves in the builder was appended to.
func ( *structWriter) (,  int,  array.Builder,  []parquet.Value) (int, bool) {
	switch b := .(type) {
	case *array.StructBuilder:
		 := 0
		for  := 0;  < .NumField(); ++ {
			// recurse
			,  := .(, +, .FieldBuilder(), )
			if  {
				if .FieldBuilder().Len() != .Len() { // NOTE: I'm unsure if this is correct for multi-nested structs
					.Append(true)
				}
				return .NumField(), true
			}

			 += 
		}

		return , false

	case *array.Int64Builder:
		if  ==  {
			for ,  := range  {
				switch .IsNull() {
				case true:
					.AppendNull()
				default:
					.Append(.Int64())
				}
			}
			return 1, true
		}
	case *array.Uint64Builder:
		if  ==  {
			for ,  := range  {
				switch .IsNull() {
				case true:
					.AppendNull()
				default:
					.Append(.Uint64())
				}
			}
			return 1, true
		}
	case *array.Float64Builder:
		if  ==  {
			for ,  := range  {
				switch .IsNull() {
				case true:
					.AppendNull()
				default:
					.Append(.Double())
				}
			}
			return 1, true
		}
	case *array.StringBuilder:
		if  ==  {
			for ,  := range  {
				switch .IsNull() {
				case true:
					.AppendNull()
				default:
					.Append(string(.ByteArray()))
				}
			}
			return 1, true
		}
	case *array.BinaryBuilder:
		if  ==  {
			for ,  := range  {
				switch .IsNull() {
				case true:
					.AppendNull()
				default:
					.Append(.ByteArray())
				}
			}
			return 1, true
		}
	case *array.BinaryDictionaryBuilder:
		if  ==  {
			for ,  := range  {
				switch .IsNull() {
				case true:
					.AppendNull()
				default:
					if  := .Append(.ByteArray());  != nil {
						panic("failed to append to dictionary")
					}
				}
			}
			return 1, true
		}
	default:
		panic(fmt.Sprintf("unsuported value type: %v", ))
	}

	return 1, false
}

type mapWriter struct {
	b *array.MapBuilder
}

func ( builder.ColumnBuilder,  int) ValueWriter {
	return &mapWriter{
		b: .(*array.MapBuilder),
	}
}

func ( *mapWriter) ( []parquet.Value) {
	panic("not implemented")
}

type dictionaryValueWriter struct {
	b builder.ColumnBuilder
}

func ( builder.ColumnBuilder,  int) ValueWriter {
	 := &dictionaryValueWriter{
		b: ,
	}
	return 
}

func ( *dictionaryValueWriter) ( []parquet.Value) {
	switch db := .b.(type) {
	case *array.BinaryDictionaryBuilder:
		for ,  := range  {
			if .IsNull() {
				.AppendNull()
			} else {
				if  := .Append(.Bytes());  != nil {
					panic("failed to append to dictionary")
				}
			}
		}
	}
}