package pgtype

import (
	
	
	
	
	
	
	

	
)

type Vec2 struct {
	X float64
	Y float64
}

type PointScanner interface {
	ScanPoint(v Point) error
}

type PointValuer interface {
	PointValue() (Point, error)
}

type Point struct {
	P     Vec2
	Valid bool
}

func ( *Point) ( Point) error {
	* = 
	return nil
}

func ( Point) () (Point, error) {
	return , nil
}

func parsePoint( []byte) (*Point, error) {
	if  == nil || bytes.Equal(, []byte("null")) {
		return &Point{}, nil
	}

	if len() < 5 {
		return nil, fmt.Errorf("invalid length for point: %v", len())
	}
	if [0] == '"' && [len()-1] == '"' {
		 = [1 : len()-1]
	}
	 := strings.SplitN(string([1:len()-1]), ",", 2)
	if len() < 2 {
		return nil, fmt.Errorf("invalid format for point")
	}

	,  := strconv.ParseFloat([0], 64)
	if  != nil {
		return nil, 
	}

	,  := strconv.ParseFloat([1], 64)
	if  != nil {
		return nil, 
	}

	return &Point{P: Vec2{, }, Valid: true}, nil
}

// Scan implements the database/sql Scanner interface.
func ( *Point) ( any) error {
	if  == nil {
		* = Point{}
		return nil
	}

	switch src := .(type) {
	case string:
		return scanPlanTextAnyToPointScanner{}.Scan([]byte(), )
	}

	return fmt.Errorf("cannot scan %T", )
}

// Value implements the database/sql/driver Valuer interface.
func ( Point) () (driver.Value, error) {
	if !.Valid {
		return nil, nil
	}

	,  := PointCodec{}.PlanEncode(nil, 0, TextFormatCode, ).Encode(, nil)
	if  != nil {
		return nil, 
	}
	return string(), 
}

func ( Point) () ([]byte, error) {
	if !.Valid {
		return []byte("null"), nil
	}

	var  bytes.Buffer
	.WriteByte('"')
	.WriteString(fmt.Sprintf("(%g,%g)", .P.X, .P.Y))
	.WriteByte('"')
	return .Bytes(), nil
}

func ( *Point) ( []byte) error {
	,  := parsePoint()
	if  != nil {
		return 
	}
	* = *
	return nil
}

type PointCodec struct{}

func (PointCodec) ( int16) bool {
	return  == TextFormatCode ||  == BinaryFormatCode
}

func (PointCodec) () int16 {
	return BinaryFormatCode
}

func (PointCodec) ( *Map,  uint32,  int16,  any) EncodePlan {
	if ,  := .(PointValuer); ! {
		return nil
	}

	switch  {
	case BinaryFormatCode:
		return encodePlanPointCodecBinary{}
	case TextFormatCode:
		return encodePlanPointCodecText{}
	}

	return nil
}

type encodePlanPointCodecBinary struct{}

func (encodePlanPointCodecBinary) ( any,  []byte) ( []byte,  error) {
	,  := .(PointValuer).PointValue()
	if  != nil {
		return nil, 
	}

	if !.Valid {
		return nil, nil
	}

	 = pgio.AppendUint64(, math.Float64bits(.P.X))
	 = pgio.AppendUint64(, math.Float64bits(.P.Y))
	return , nil
}

type encodePlanPointCodecText struct{}

func (encodePlanPointCodecText) ( any,  []byte) ( []byte,  error) {
	,  := .(PointValuer).PointValue()
	if  != nil {
		return nil, 
	}

	if !.Valid {
		return nil, nil
	}

	return append(, fmt.Sprintf(`(%s,%s)`,
		strconv.FormatFloat(.P.X, 'f', -1, 64),
		strconv.FormatFloat(.P.Y, 'f', -1, 64),
	)...), nil
}

func (PointCodec) ( *Map,  uint32,  int16,  any) ScanPlan {

	switch  {
	case BinaryFormatCode:
		switch .(type) {
		case PointScanner:
			return scanPlanBinaryPointToPointScanner{}
		}
	case TextFormatCode:
		switch .(type) {
		case PointScanner:
			return scanPlanTextAnyToPointScanner{}
		}
	}

	return nil
}

func ( PointCodec) ( *Map,  uint32,  int16,  []byte) (driver.Value, error) {
	return codecDecodeToTextFormat(, , , , )
}

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

	var  Point
	 := codecScan(, , , , , &)
	if  != nil {
		return nil, 
	}
	return , nil
}

type scanPlanBinaryPointToPointScanner struct{}

func (scanPlanBinaryPointToPointScanner) ( []byte,  any) error {
	 := ().(PointScanner)

	if  == nil {
		return .ScanPoint(Point{})
	}

	if len() != 16 {
		return fmt.Errorf("invalid length for point: %v", len())
	}

	 := binary.BigEndian.Uint64()
	 := binary.BigEndian.Uint64([8:])

	return .ScanPoint(Point{
		P:     Vec2{math.Float64frombits(), math.Float64frombits()},
		Valid: true,
	})
}

type scanPlanTextAnyToPointScanner struct{}

func (scanPlanTextAnyToPointScanner) ( []byte,  any) error {
	 := ().(PointScanner)

	if  == nil {
		return .ScanPoint(Point{})
	}

	if len() < 5 {
		return fmt.Errorf("invalid length for point: %v", len())
	}

	 := strings.SplitN(string([1:len()-1]), ",", 2)
	if len() < 2 {
		return fmt.Errorf("invalid format for point")
	}

	,  := strconv.ParseFloat([0], 64)
	if  != nil {
		return 
	}

	,  := strconv.ParseFloat([1], 64)
	if  != nil {
		return 
	}

	return .ScanPoint(Point{P: Vec2{, }, Valid: true})
}