package pgtype

import (
	
	
	
	
	

	
)

// MultirangeGetter is a type that can be converted into a PostgreSQL multirange.
type MultirangeGetter interface {
	// IsNull returns true if the value is SQL NULL.
	IsNull() bool

	// Len returns the number of elements in the multirange.
	Len() int

	// Index returns the element at i.
	Index(i int) any

	// IndexType returns a non-nil scan target of the type Index will return. This is used by MultirangeCodec.PlanEncode.
	IndexType() any
}

// MultirangeSetter is a type can be set from a PostgreSQL multirange.
type MultirangeSetter interface {
	// ScanNull sets the value to SQL NULL.
	ScanNull() error

	// SetLen prepares the value such that ScanIndex can be called for each element. This will remove any existing
	// elements.
	SetLen(n int) error

	// ScanIndex returns a value usable as a scan target for i. SetLen must be called before ScanIndex.
	ScanIndex(i int) any

	// ScanIndexType returns a non-nil scan target of the type ScanIndex will return. This is used by
	// MultirangeCodec.PlanScan.
	ScanIndexType() any
}

// MultirangeCodec is a codec for any multirange type.
type MultirangeCodec struct {
	ElementType *Type
}

func ( *MultirangeCodec) ( int16) bool {
	return .ElementType.Codec.FormatSupported()
}

func ( *MultirangeCodec) () int16 {
	return .ElementType.Codec.PreferredFormat()
}

func ( *MultirangeCodec) ( *Map,  uint32,  int16,  any) EncodePlan {
	,  := .(MultirangeGetter)
	if ! {
		return nil
	}

	 := .IndexType()

	 := .PlanEncode(.ElementType.OID, , )
	if  == nil {
		return nil
	}

	switch  {
	case BinaryFormatCode:
		return &encodePlanMultirangeCodecBinary{ac: , m: , oid: }
	case TextFormatCode:
		return &encodePlanMultirangeCodecText{ac: , m: , oid: }
	}

	return nil
}

type encodePlanMultirangeCodecText struct {
	ac  *MultirangeCodec
	m   *Map
	oid uint32
}

func ( *encodePlanMultirangeCodecText) ( any,  []byte) ( []byte,  error) {
	 := .(MultirangeGetter)

	if .IsNull() {
		return nil, nil
	}

	 := .Len()

	 = append(, '{')

	var  EncodePlan
	var  reflect.Type
	 := make([]byte, 0, 32)
	for  := 0;  < ; ++ {
		if  > 0 {
			 = append(, ',')
		}

		 := .Index()
		var  []byte
		if  != nil {
			 := reflect.TypeOf()
			if  !=  {
				 = 
				 = .m.PlanEncode(.ac.ElementType.OID, TextFormatCode, )
				if  == nil {
					return nil, fmt.Errorf("unable to encode %v", .Index())
				}
			}
			,  = .Encode(, )
			if  != nil {
				return nil, 
			}
		}

		if  == nil {
			return nil, fmt.Errorf("multirange cannot contain NULL element")
		} else {
			 = append(, ...)
		}
	}

	 = append(, '}')

	return , nil
}

type encodePlanMultirangeCodecBinary struct {
	ac  *MultirangeCodec
	m   *Map
	oid uint32
}

func ( *encodePlanMultirangeCodecBinary) ( any,  []byte) ( []byte,  error) {
	 := .(MultirangeGetter)

	if .IsNull() {
		return nil, nil
	}

	 := .Len()

	 = pgio.AppendInt32(, int32())

	var  EncodePlan
	var  reflect.Type
	for  := 0;  < ; ++ {
		 := len()
		 = pgio.AppendInt32(, -1)

		 := .Index()
		var  []byte
		if  != nil {
			 := reflect.TypeOf()
			if  !=  {
				 = 
				 = .m.PlanEncode(.ac.ElementType.OID, BinaryFormatCode, )
				if  == nil {
					return nil, fmt.Errorf("unable to encode %v", .Index())
				}
			}
			,  = .Encode(, )
			if  != nil {
				return nil, 
			}
		}

		if  == nil {
			return nil, fmt.Errorf("multirange cannot contain NULL element")
		} else {
			 = 
			pgio.SetInt32([:], int32(len([:])-4))
		}
	}

	return , nil
}

func ( *MultirangeCodec) ( *Map,  uint32,  int16,  any) ScanPlan {
	,  := .(MultirangeSetter)
	if ! {
		return nil
	}

	 := .ScanIndexType()

	 := .PlanScan(.ElementType.OID, , )
	if ,  := .(*scanPlanFail);  {
		return nil
	}

	return &scanPlanMultirangeCodec{
		multirangeCodec: ,
		m:               ,
		oid:             ,
		formatCode:      ,
	}
}

func ( *MultirangeCodec) ( *Map,  uint32,  []byte,  MultirangeSetter) error {
	 := 0

	 := int(binary.BigEndian.Uint32([:]))
	 += 4

	 := .SetLen()
	if  != nil {
		return 
	}

	if  == 0 {
		return nil
	}

	 := .ElementType.Codec.PlanScan(, .ElementType.OID, BinaryFormatCode, .ScanIndex(0))
	if  == nil {
		 = .PlanScan(.ElementType.OID, BinaryFormatCode, .ScanIndex(0))
	}

	for  := 0;  < ; ++ {
		 := .ScanIndex()
		 := int(int32(binary.BigEndian.Uint32([:])))
		 += 4
		var  []byte
		if  >= 0 {
			 = [ : +]
			 += 
		}
		 = .Scan(, )
		if  != nil {
			return fmt.Errorf("failed to scan multirange element %d: %w", , )
		}
	}

	return nil
}

func ( *MultirangeCodec) ( *Map,  uint32,  []byte,  MultirangeSetter) error {
	,  := parseUntypedTextMultirange()
	if  != nil {
		return 
	}

	 = .SetLen(len())
	if  != nil {
		return 
	}

	if len() == 0 {
		return nil
	}

	 := .ElementType.Codec.PlanScan(, .ElementType.OID, TextFormatCode, .ScanIndex(0))
	if  == nil {
		 = .PlanScan(.ElementType.OID, TextFormatCode, .ScanIndex(0))
	}

	for ,  := range  {
		 := .ScanIndex()
		 = .Scan([]byte(), )
		if  != nil {
			return 
		}
	}

	return nil
}

type scanPlanMultirangeCodec struct {
	multirangeCodec *MultirangeCodec
	m               *Map
	oid             uint32
	formatCode      int16
	elementScanPlan ScanPlan
}

func ( *scanPlanMultirangeCodec) ( []byte,  any) error {
	 := .multirangeCodec
	 := .m
	 := .oid
	 := .formatCode

	 := .(MultirangeSetter)

	if  == nil {
		return .ScanNull()
	}

	switch  {
	case BinaryFormatCode:
		return .decodeBinary(, , , )
	case TextFormatCode:
		return .decodeText(, , , )
	default:
		return fmt.Errorf("unknown format code %d", )
	}
}

func ( *MultirangeCodec) ( *Map,  uint32,  int16,  []byte) (driver.Value, error) {
	if  == nil {
		return nil, nil
	}

	switch  {
	case TextFormatCode:
		return string(), nil
	case BinaryFormatCode:
		 := make([]byte, len())
		copy(, )
		return , nil
	default:
		return nil, fmt.Errorf("unknown format code %d", )
	}
}

func ( *MultirangeCodec) ( *Map,  uint32,  int16,  []byte) (any, error) {
	if  == nil {
		return nil, nil
	}

	var  Multirange[Range[any]]
	 := .PlanScan(, , &).Scan(, &)
	return , 
}

func parseUntypedTextMultirange( []byte) ([]string, error) {
	 := make([]string, 0)

	 := bytes.NewBuffer()

	skipWhitespace()

	, ,  := .ReadRune()
	if  != nil {
		return nil, fmt.Errorf("invalid array: %v", )
	}

	if  != '{' {
		return nil, fmt.Errorf("invalid multirange, expected '{': %v", )
	}

:
	for {
		, _,  = .ReadRune()
		if  != nil {
			return nil, fmt.Errorf("invalid multirange: %v", )
		}

		switch  {
		case ',': // skip range separator
		case '}':
			break 
		default:
			.UnreadRune()
			,  := parseRange()
			if  != nil {
				return nil, fmt.Errorf("invalid multirange value: %v", )
			}
			 = append(, )
		}
	}

	skipWhitespace()

	if .Len() > 0 {
		return nil, fmt.Errorf("unexpected trailing data: %v", .String())
	}

	return , nil

}

func parseRange( *bytes.Buffer) (string, error) {
	 := &bytes.Buffer{}

	 := false
	for {
		, ,  := .ReadRune()
		if  != nil {
			return "", 
		}

		switch  {
		case ',', '}':
			if  == ',' && ! {
				 = true
				break
			}
			.UnreadRune()
			return .String(), nil
		}

		.WriteRune()
	}
}

// Multirange is a generic multirange type.
//
// T should implement RangeValuer and *T should implement RangeScanner. However, there does not appear to be a way to
// enforce the RangeScanner constraint.
type Multirange[ RangeValuer] []

func ( Multirange[]) () bool {
	return  == nil
}

func ( Multirange[]) () int {
	return len()
}

func ( Multirange[]) ( int) any {
	return []
}

func ( Multirange[]) () any {
	var  
	return 
}

func ( *Multirange[]) () error {
	* = nil
	return nil
}

func ( *Multirange[]) ( int) error {
	* = make([], )
	return nil
}

func ( Multirange[]) ( int) any {
	return &[]
}

func ( Multirange[]) () any {
	return new()
}