package pgtype

import (
	
	
	
	
	
	
	

	
)

// Information on the internals of PostgreSQL arrays can be found in
// src/include/utils/array.h and src/backend/utils/adt/arrayfuncs.c. Of
// particular interest is the array_send function.

type arrayHeader struct {
	ContainsNull bool
	ElementOID   uint32
	Dimensions   []ArrayDimension
}

type ArrayDimension struct {
	Length     int32
	LowerBound int32
}

// cardinality returns the number of elements in an array of dimensions size.
func cardinality( []ArrayDimension) int {
	if len() == 0 {
		return 0
	}

	 := int([0].Length)
	for ,  := range [1:] {
		 *= int(.Length)
	}

	return 
}

func ( *arrayHeader) ( *Map,  []byte) (int, error) {
	if len() < 12 {
		return 0, fmt.Errorf("array header too short: %d", len())
	}

	 := 0

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

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

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

	.Dimensions = make([]ArrayDimension, )
	if len() < 12+*8 {
		return 0, fmt.Errorf("array header too short for %d dimensions: %d", , len())
	}
	for  := range .Dimensions {
		.Dimensions[].Length = int32(binary.BigEndian.Uint32([:]))
		 += 4

		.Dimensions[].LowerBound = int32(binary.BigEndian.Uint32([:]))
		 += 4
	}

	return , nil
}

func ( arrayHeader) ( []byte) []byte {
	 = pgio.AppendInt32(, int32(len(.Dimensions)))

	var  int32
	if .ContainsNull {
		 = 1
	}
	 = pgio.AppendInt32(, )

	 = pgio.AppendUint32(, .ElementOID)

	for  := range .Dimensions {
		 = pgio.AppendInt32(, .Dimensions[].Length)
		 = pgio.AppendInt32(, .Dimensions[].LowerBound)
	}

	return 
}

type untypedTextArray struct {
	Elements   []string
	Quoted     []bool
	Dimensions []ArrayDimension
}

func parseUntypedTextArray( string) (*untypedTextArray, error) {
	 := &untypedTextArray{
		Elements:   []string{},
		Quoted:     []bool{},
		Dimensions: []ArrayDimension{},
	}

	 := bytes.NewBufferString()

	skipWhitespace()

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

	var  []ArrayDimension

	// Array has explicit dimensions
	if  == '[' {
		.UnreadRune()

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

			if  == '=' {
				break
			} else if  != '[' {
				return nil, fmt.Errorf("invalid array, expected '[' or '=' got %v", )
			}

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

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

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

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

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

			if  != ']' {
				return nil, fmt.Errorf("invalid array, expected ']' got %v", )
			}

			 = append(, ArrayDimension{LowerBound: , Length:  -  + 1})
		}

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

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

	 := []ArrayDimension{{LowerBound: 1, Length: 0}}

	// Consume all initial opening brackets. This provides number of dimensions.
	for {
		, _,  = .ReadRune()
		if  != nil {
			return nil, fmt.Errorf("invalid array: %v", )
		}

		if  == '{' {
			[len()-1].Length = 1
			 = append(, ArrayDimension{LowerBound: 1})
		} else {
			.UnreadRune()
			break
		}
	}
	 := len() - 1
	 := 

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

		switch  {
		case '{':
			if  ==  {
				[].Length++
			}
			++
		case ',':
		case '}':
			--
			if  <  {
				 = 
			}
		default:
			.UnreadRune()
			, ,  := arrayParseValue()
			if  != nil {
				return nil, fmt.Errorf("invalid array value: %v", )
			}
			if  ==  {
				[].Length++
			}
			.Quoted = append(.Quoted, )
			.Elements = append(.Elements, )
		}

		if  < 0 {
			break
		}
	}

	skipWhitespace()

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

	if len(.Elements) == 0 {
	} else if len() > 0 {
		.Dimensions = 
	} else {
		.Dimensions = 
	}

	return , nil
}

func skipWhitespace( *bytes.Buffer) {
	var  rune
	var  error
	for , _, _ = .ReadRune(); unicode.IsSpace(); , _, _ = .ReadRune() {
	}

	if  != io.EOF {
		.UnreadRune()
	}
}

func arrayParseValue( *bytes.Buffer) (string, bool, error) {
	, ,  := .ReadRune()
	if  != nil {
		return "", false, 
	}
	if  == '"' {
		return arrayParseQuotedValue()
	}
	.UnreadRune()

	 := &bytes.Buffer{}

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

		switch  {
		case ',', '}':
			.UnreadRune()
			return .String(), false, nil
		}

		.WriteRune()
	}
}

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

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

		switch  {
		case '\\':
			, _,  = .ReadRune()
			if  != nil {
				return "", false, 
			}
		case '"':
			, _,  = .ReadRune()
			if  != nil {
				return "", false, 
			}
			.UnreadRune()
			return .String(), true, nil
		}
		.WriteRune()
	}
}

func arrayParseInteger( *bytes.Buffer) (int32, error) {
	 := &bytes.Buffer{}

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

		if ('0' <=  &&  <= '9') ||  == '-' {
			.WriteRune()
		} else {
			.UnreadRune()
			,  := strconv.ParseInt(.String(), 10, 32)
			if  != nil {
				return 0, 
			}
			return int32(), nil
		}
	}
}

func encodeTextArrayDimensions( []byte,  []ArrayDimension) []byte {
	var  bool
	for ,  := range  {
		if .LowerBound != 1 {
			 = true
		}
	}

	if ! {
		return 
	}

	for ,  := range  {
		 = append(, '[')
		 = append(, strconv.FormatInt(int64(.LowerBound), 10)...)
		 = append(, ':')
		 = append(, strconv.FormatInt(int64(.LowerBound+.Length-1), 10)...)
		 = append(, ']')
	}

	return append(, '=')
}

var quoteArrayReplacer = strings.NewReplacer(`\`, `\\`, `"`, `\"`)

func quoteArrayElement( string) string {
	return `"` + quoteArrayReplacer.Replace() + `"`
}

func isSpace( byte) bool {
	// see array_isspace:
	// https://github.com/postgres/postgres/blob/master/src/backend/utils/adt/arrayfuncs.c
	return  == ' ' ||  == '\t' ||  == '\n' ||  == '\r' ||  == '\v' ||  == '\f'
}

func quoteArrayElementIfNeeded( string) string {
	if  == "" || (len() == 4 && strings.EqualFold(, "null")) || isSpace([0]) || isSpace([len()-1]) || strings.ContainsAny(, `{},"\`) {
		return quoteArrayElement()
	}
	return 
}

// Array represents a PostgreSQL array for T. It implements the ArrayGetter and ArraySetter interfaces. It preserves
// PostgreSQL dimensions and custom lower bounds. Use FlatArray if these are not needed.
type Array[ any] struct {
	Elements []
	Dims     []ArrayDimension
	Valid    bool
}

func ( Array[]) () []ArrayDimension {
	return .Dims
}

func ( Array[]) ( int) any {
	return .Elements[]
}

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

func ( *Array[]) ( []ArrayDimension) error {
	if  == nil {
		* = Array[]{}
		return nil
	}

	 := cardinality()
	* = Array[]{
		Elements: make([], ),
		Dims:     ,
		Valid:    true,
	}

	return nil
}

func ( Array[]) ( int) any {
	return &.Elements[]
}

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

// FlatArray implements the ArrayGetter and ArraySetter interfaces for any slice of T. It ignores PostgreSQL dimensions
// and custom lower bounds. Use Array to preserve these.
type FlatArray[ any] []

func ( FlatArray[]) () []ArrayDimension {
	if  == nil {
		return nil
	}

	return []ArrayDimension{{Length: int32(len()), LowerBound: 1}}
}

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

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

func ( *FlatArray[]) ( []ArrayDimension) error {
	if  == nil {
		* = nil
		return nil
	}

	 := cardinality()
	* = make(FlatArray[], )
	return nil
}

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

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