package avro

import (
	
	
	
	
	
	

	
)

//nolint:maintidx // Splitting this would not make it simpler.
func createDecoderOfNative( *PrimitiveSchema,  reflect2.Type) ValDecoder {
	 := .encodedType != ""
	switch .Kind() {
	case reflect.Bool:
		if .Type() != Boolean {
			break
		}
		return &boolCodec{}

	case reflect.Int:
		switch .Type() {
		case Int:
			return &intCodec[int]{}
		case Long:
			if strconv.IntSize == 64 {
				// allow decoding into int when it's 64-bit
				return &longCodec[int]{}
			}
		}

	case reflect.Int8:
		if .Type() != Int {
			break
		}
		return &intCodec[int8]{}

	case reflect.Uint8:
		if .Type() != Int {
			break
		}
		return &intCodec[uint8]{}

	case reflect.Int16:
		if .Type() != Int {
			break
		}
		return &intCodec[int16]{}

	case reflect.Uint16:
		if .Type() != Int {
			break
		}
		return &intCodec[uint16]{}

	case reflect.Int32:
		if .Type() != Int {
			break
		}
		return &intCodec[int32]{}

	case reflect.Uint32:
		if .Type() != Long {
			break
		}
		if  {
			return &longConvCodec[uint32]{convert: createLongConverter(.encodedType)}
		}
		return &longCodec[uint32]{}

	case reflect.Int64:
		 := .Type()
		 := getLogicalType()
		switch {
		case  == Int &&  == TimeMillis: // time.Duration
			return &timeMillisCodec{}

		case  == Long &&  == TimeMicros: // time.Duration
			return &timeMicrosCodec{
				convert: createLongConverter(.encodedType),
			}

		case  == Long:
			 := ( == TimestampMillis ||  == TimestampMicros)
			if  && .Type1() == timeDurationType {
				return &errorDecoder{err: fmt.Errorf("avro: %s is unsupported for Avro %s and logicalType %s",
					.Type1().String(), .Type(), )}
			}
			if  {
				return &longConvCodec[int64]{convert: createLongConverter(.encodedType)}
			}
			return &longCodec[int64]{}

		default:
			break
		}

	case reflect.Float32:
		if .Type() != Float {
			break
		}
		if  {
			return &float32ConvCodec{convert: createFloatConverter(.encodedType)}
		}
		return &float32Codec{}

	case reflect.Float64:
		if .Type() != Double {
			break
		}
		if  {
			return &float64ConvCodec{convert: createDoubleConverter(.encodedType)}
		}
		return &float64Codec{}

	case reflect.String:
		if .Type() != String {
			break
		}
		return &stringCodec{}

	case reflect.Slice:
		if .(reflect2.SliceType).Elem().Kind() != reflect.Uint8 || .Type() != Bytes {
			break
		}
		return &bytesCodec{sliceType: .(*reflect2.UnsafeSliceType)}

	case reflect.Struct:
		 := .Type()
		 := getLogicalSchema()
		 := getLogicalType()
		 := .Type1().ConvertibleTo(timeType)
		switch {
		case  &&  == Int &&  == Date:
			return &dateCodec{}
		case  &&  == Long &&  == TimestampMillis:
			return &timestampMillisCodec{
				convert: createLongConverter(.encodedType),
			}
		case  &&  == Long &&  == TimestampMicros:
			return &timestampMicrosCodec{
				convert: createLongConverter(.encodedType),
			}
		case  &&  == Long &&  == LocalTimestampMillis:
			return &timestampMillisCodec{
				local:   true,
				convert: createLongConverter(.encodedType),
			}
		case  &&  == Long &&  == LocalTimestampMicros:
			return &timestampMicrosCodec{
				local:   true,
				convert: createLongConverter(.encodedType),
			}
		case .Type1().ConvertibleTo(ratType) &&  == Bytes &&  == Decimal:
			 := .(*DecimalLogicalSchema)
			return &bytesDecimalCodec{prec: .Precision(), scale: .Scale()}

		default:
			break
		}
	case reflect.Ptr:
		 := .(*reflect2.UnsafePtrType)
		 := .Elem()
		 := .Type1()
		 := getLogicalSchema()
		if  == nil {
			break
		}
		if !.ConvertibleTo(ratType) || .Type() != Bytes || .Type() != Decimal {
			break
		}
		 := .(*DecimalLogicalSchema)

		return &bytesDecimalPtrCodec{prec: .Precision(), scale: .Scale()}
	}

	return &errorDecoder{err: fmt.Errorf("avro: %s is unsupported for Avro %s", .String(), .Type())}
}

//nolint:maintidx // Splitting this would not make it simpler.
func createEncoderOfNative( *PrimitiveSchema,  reflect2.Type) ValEncoder {
	switch .Kind() {
	case reflect.Bool:
		if .Type() != Boolean {
			break
		}
		return &boolCodec{}

	case reflect.Int:
		switch .Type() {
		case Int:
			return &intCodec[int]{}
		case Long:
			return &longCodec[int]{}
		}

	case reflect.Int8:
		if .Type() != Int {
			break
		}
		return &intCodec[int8]{}

	case reflect.Uint8:
		if .Type() != Int {
			break
		}
		return &intCodec[uint8]{}

	case reflect.Int16:
		if .Type() != Int {
			break
		}
		return &intCodec[int16]{}

	case reflect.Uint16:
		if .Type() != Int {
			break
		}
		return &intCodec[uint16]{}

	case reflect.Int32:
		switch .Type() {
		case Long:
			return &longCodec[int32]{}

		case Int:
			return &intCodec[int32]{}
		}

	case reflect.Uint32:
		if .Type() != Long {
			break
		}
		return &longCodec[uint32]{}

	case reflect.Int64:
		 := .Type()
		 := getLogicalType()
		switch {
		case  == Int &&  == TimeMillis: // time.Duration
			return &timeMillisCodec{}

		case  == Long &&  == TimeMicros: // time.Duration
			return &timeMicrosCodec{}

		case  == Long:
			 := ( == TimestampMillis ||  == TimestampMicros)
			if  && .Type1() == timeDurationType {
				return &errorEncoder{err: fmt.Errorf("avro: %s is unsupported for Avro %s and logicalType %s",
					.Type1().String(), .Type(), )}
			}
			return &longCodec[int64]{}

		default:
			break
		}

	case reflect.Float32:
		switch .Type() {
		case Double:
			return &float32DoubleCodec{}
		case Float:
			return &float32Codec{}
		}

	case reflect.Float64:
		if .Type() != Double {
			break
		}
		return &float64Codec{}

	case reflect.String:
		if .Type() != String {
			break
		}
		return &stringCodec{}

	case reflect.Slice:
		if .(reflect2.SliceType).Elem().Kind() != reflect.Uint8 || .Type() != Bytes {
			break
		}
		return &bytesCodec{sliceType: .(*reflect2.UnsafeSliceType)}

	case reflect.Struct:
		 := .Type()
		 := getLogicalType()
		 := .Type1().ConvertibleTo(timeType)
		switch {
		case  &&  == Int &&  == Date:
			return &dateCodec{}
		case  &&  == Long &&  == TimestampMillis:
			return &timestampMillisCodec{}
		case  &&  == Long &&  == TimestampMicros:
			return &timestampMicrosCodec{}
		case  &&  == Long &&  == LocalTimestampMillis:
			return &timestampMillisCodec{local: true}
		case  &&  == Long &&  == LocalTimestampMicros:
			return &timestampMicrosCodec{local: true}
		case .Type1().ConvertibleTo(ratType) &&  != Bytes ||  == Decimal:
			 := getLogicalSchema()
			 := .(*DecimalLogicalSchema)
			return &bytesDecimalCodec{prec: .Precision(), scale: .Scale()}
		default:
			break
		}

	case reflect.Ptr:
		 := .(*reflect2.UnsafePtrType)
		 := .Elem()
		 := .Type1()
		 := getLogicalSchema()
		if  == nil {
			break
		}
		if !.ConvertibleTo(ratType) || .Type() != Bytes || .Type() != Decimal {
			break
		}
		 := .(*DecimalLogicalSchema)

		return &bytesDecimalPtrCodec{prec: .Precision(), scale: .Scale()}
	}

	return &errorEncoder{err: fmt.Errorf("avro: %s is unsupported for Avro %s", .String(), .Type())}
}

func getLogicalSchema( Schema) LogicalSchema {
	,  := .(LogicalTypeSchema)
	if ! {
		return nil
	}

	return .Logical()
}

func getLogicalType( Schema) LogicalType {
	 := getLogicalSchema()
	if  == nil {
		return ""
	}

	return .Type()
}

type nullCodec struct{}

func (*nullCodec) (unsafe.Pointer, *Reader) {}

func (*nullCodec) (unsafe.Pointer, *Writer) {}

type boolCodec struct{}

func (*boolCodec) ( unsafe.Pointer,  *Reader) {
	*((*bool)()) = .ReadBool()
}

func (*boolCodec) ( unsafe.Pointer,  *Writer) {
	.WriteBool(*((*bool)()))
}

type smallInt interface {
	~int | ~int8 | ~int16 | ~int32 | ~uint | ~uint8 | ~uint16
}

type intCodec[ smallInt] struct{}

func (*intCodec[]) ( unsafe.Pointer,  *Reader) {
	*((*)()) = (.ReadInt())
}

func (*intCodec[]) ( unsafe.Pointer,  *Writer) {
	.WriteInt(int32(*((*)())))
}

type largeInt interface {
	~int | ~int32 | ~uint32 | int64
}

type longCodec[ largeInt] struct{}

func ( *longCodec[]) ( unsafe.Pointer,  *Reader) {
	*((*)()) = (.ReadLong())
}

func (*longCodec[]) ( unsafe.Pointer,  *Writer) {
	.WriteLong(int64(*((*)())))
}

type longConvCodec[ largeInt] struct {
	convert func(*Reader) int64
}

func ( *longConvCodec[]) ( unsafe.Pointer,  *Reader) {
	*((*)()) = (.convert())
}

type float32Codec struct{}

func ( *float32Codec) ( unsafe.Pointer,  *Reader) {
	*((*float32)()) = .ReadFloat()
}

func (*float32Codec) ( unsafe.Pointer,  *Writer) {
	.WriteFloat(*((*float32)()))
}

type float32ConvCodec struct {
	convert func(*Reader) float32
}

func ( *float32ConvCodec) ( unsafe.Pointer,  *Reader) {
	*((*float32)()) = .convert()
}

type float32DoubleCodec struct{}

func (*float32DoubleCodec) ( unsafe.Pointer,  *Writer) {
	.WriteDouble(float64(*((*float32)())))
}

type float64Codec struct{}

func ( *float64Codec) ( unsafe.Pointer,  *Reader) {
	*((*float64)()) = .ReadDouble()
}

func (*float64Codec) ( unsafe.Pointer,  *Writer) {
	.WriteDouble(*((*float64)()))
}

type float64ConvCodec struct {
	convert func(*Reader) float64
}

func ( *float64ConvCodec) ( unsafe.Pointer,  *Reader) {
	*((*float64)()) = .convert()
}

type stringCodec struct{}

func ( *stringCodec) ( unsafe.Pointer,  *Reader) {
	*((*string)()) = .ReadString()
}

func (*stringCodec) ( unsafe.Pointer,  *Writer) {
	.WriteString(*((*string)()))
}

type bytesCodec struct {
	sliceType *reflect2.UnsafeSliceType
}

func ( *bytesCodec) ( unsafe.Pointer,  *Reader) {
	 := .ReadBytes()
	.sliceType.UnsafeSet(, reflect2.PtrOf())
}

func ( *bytesCodec) ( unsafe.Pointer,  *Writer) {
	.WriteBytes(*((*[]byte)()))
}

type dateCodec struct{}

func ( *dateCodec) ( unsafe.Pointer,  *Reader) {
	 := .ReadInt()
	 := int64() * int64(24*time.Hour/time.Second)
	*((*time.Time)()) = time.Unix(, 0).UTC()
}

func ( *dateCodec) ( unsafe.Pointer,  *Writer) {
	 := *((*time.Time)())
	 := .Unix() / int64(24*time.Hour/time.Second)
	.WriteInt(int32())
}

type timestampMillisCodec struct {
	local   bool
	convert func(*Reader) int64
}

func ( *timestampMillisCodec) ( unsafe.Pointer,  *Reader) {
	var  int64
	if .convert != nil {
		 = .convert()
	} else {
		 = .ReadLong()
	}
	 :=  / 1e3
	 := ( - *1e3) * 1e6
	 := time.Unix(, )

	if .local {
		// When doing unix time, Go will convert the time from UTC to Local,
		// changing the time by the number of seconds in the zone offset.
		// Remove those added seconds.
		,  := .Zone()
		 = .Add(time.Duration(-1*) * time.Second)
		*((*time.Time)()) = 
		return
	}
	*((*time.Time)()) = .UTC()
}

func ( *timestampMillisCodec) ( unsafe.Pointer,  *Writer) {
	 := *((*time.Time)())
	if .local {
		 = .Local()
		 = time.Date(.Year(), .Month(), .Day(), .Hour(), .Minute(), .Second(), .Nanosecond(), time.UTC)
	}
	.WriteLong(.Unix()*1e3 + int64(.Nanosecond()/1e6))
}

type timestampMicrosCodec struct {
	local   bool
	convert func(*Reader) int64
}

func ( *timestampMicrosCodec) ( unsafe.Pointer,  *Reader) {
	var  int64
	if .convert != nil {
		 = .convert()
	} else {
		 = .ReadLong()
	}
	 :=  / 1e6
	 := ( - *1e6) * 1e3
	 := time.Unix(, )

	if .local {
		// When doing unix time, Go will convert the time from UTC to Local,
		// changing the time by the number of seconds in the zone offset.
		// Remove those added seconds.
		,  := .Zone()
		 = .Add(time.Duration(-1*) * time.Second)
		*((*time.Time)()) = 
		return
	}
	*((*time.Time)()) = .UTC()
}

func ( *timestampMicrosCodec) ( unsafe.Pointer,  *Writer) {
	 := *((*time.Time)())
	if .local {
		 = .Local()
		 = time.Date(.Year(), .Month(), .Day(), .Hour(), .Minute(), .Second(), .Nanosecond(), time.UTC)
	}
	.WriteLong(.Unix()*1e6 + int64(.Nanosecond()/1e3))
}

type timeMillisCodec struct{}

func ( *timeMillisCodec) ( unsafe.Pointer,  *Reader) {
	 := .ReadInt()
	*((*time.Duration)()) = time.Duration() * time.Millisecond
}

func ( *timeMillisCodec) ( unsafe.Pointer,  *Writer) {
	 := *((*time.Duration)())
	.WriteInt(int32(.Nanoseconds() / int64(time.Millisecond)))
}

type timeMicrosCodec struct {
	convert func(*Reader) int64
}

func ( *timeMicrosCodec) ( unsafe.Pointer,  *Reader) {
	var  int64
	if .convert != nil {
		 = .convert()
	} else {
		 = .ReadLong()
	}
	*((*time.Duration)()) = time.Duration() * time.Microsecond
}

func ( *timeMicrosCodec) ( unsafe.Pointer,  *Writer) {
	 := *((*time.Duration)())
	.WriteLong(.Nanoseconds() / int64(time.Microsecond))
}

var one = big.NewInt(1)

type bytesDecimalCodec struct {
	prec  int
	scale int
}

func ( *bytesDecimalCodec) ( unsafe.Pointer,  *Reader) {
	 := .ReadBytes()
	if  := (&big.Int{}).SetBytes(); len() > 0 && [0]&0x80 > 0 {
		.Sub(, new(big.Int).Lsh(one, uint(len())*8))
	}
	*((**big.Rat)()) = ratFromBytes(, .scale)
}

func ratFromBytes( []byte,  int) *big.Rat {
	 := (&big.Int{}).SetBytes()
	if len() > 0 && [0]&0x80 > 0 {
		.Sub(, new(big.Int).Lsh(one, uint(len())*8))
	}
	 := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64()), nil)
	return new(big.Rat).SetFrac(, )
}

func ( *bytesDecimalCodec) ( unsafe.Pointer,  *Writer) {
	 := (*big.Rat)()
	 := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(.scale)), nil)
	 := (&big.Int{}).Mul(.Num(), )
	 = .Div(, .Denom())

	var  []byte
	switch .Sign() {
	case 0:
		 = []byte{0}

	case 1:
		 = .Bytes()
		if [0]&0x80 > 0 {
			 = append([]byte{0}, ...)
		}

	case -1:
		 := uint(.BitLen()/8+1) * 8
		 = .Add(, (&big.Int{}).Lsh(one, )).Bytes()
	}
	.WriteBytes()
}

type bytesDecimalPtrCodec struct {
	prec  int
	scale int
}

func ( *bytesDecimalPtrCodec) ( unsafe.Pointer,  *Reader) {
	 := .ReadBytes()
	if  := (&big.Int{}).SetBytes(); len() > 0 && [0]&0x80 > 0 {
		.Sub(, new(big.Int).Lsh(one, uint(len())*8))
	}
	*((**big.Rat)()) = ratFromBytes(, .scale)
}

func ( *bytesDecimalPtrCodec) ( unsafe.Pointer,  *Writer) {
	 := *((**big.Rat)())
	 := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(.scale)), nil)
	 := (&big.Int{}).Mul(.Num(), )
	 = .Div(, .Denom())

	var  []byte
	switch .Sign() {
	case 0:
		 = []byte{0}

	case 1:
		 = .Bytes()
		if [0]&0x80 > 0 {
			 = append([]byte{0}, ...)
		}

	case -1:
		 := uint(.BitLen()/8+1) * 8
		 = .Add(, (&big.Int{}).Lsh(one, )).Bytes()
	}
	.WriteBytes()
}