1package pgtype
2
3import (
4	"database/sql/driver"
5	"encoding/binary"
6	"fmt"
7	"math"
8	"strconv"
9	"strings"
10
11	"github.com/jackc/pgx/pgio"
12	"github.com/pkg/errors"
13)
14
15type Box struct {
16	P      [2]Vec2
17	Status Status
18}
19
20func (dst *Box) Set(src interface{}) error {
21	return errors.Errorf("cannot convert %v to Box", src)
22}
23
24func (dst *Box) Get() interface{} {
25	switch dst.Status {
26	case Present:
27		return dst
28	case Null:
29		return nil
30	default:
31		return dst.Status
32	}
33}
34
35func (src *Box) AssignTo(dst interface{}) error {
36	return errors.Errorf("cannot assign %v to %T", src, dst)
37}
38
39func (dst *Box) DecodeText(ci *ConnInfo, src []byte) error {
40	if src == nil {
41		*dst = Box{Status: Null}
42		return nil
43	}
44
45	if len(src) < 11 {
46		return errors.Errorf("invalid length for Box: %v", len(src))
47	}
48
49	str := string(src[1:])
50
51	var end int
52	end = strings.IndexByte(str, ',')
53
54	x1, err := strconv.ParseFloat(str[:end], 64)
55	if err != nil {
56		return err
57	}
58
59	str = str[end+1:]
60	end = strings.IndexByte(str, ')')
61
62	y1, err := strconv.ParseFloat(str[:end], 64)
63	if err != nil {
64		return err
65	}
66
67	str = str[end+3:]
68	end = strings.IndexByte(str, ',')
69
70	x2, err := strconv.ParseFloat(str[:end], 64)
71	if err != nil {
72		return err
73	}
74
75	str = str[end+1 : len(str)-1]
76
77	y2, err := strconv.ParseFloat(str, 64)
78	if err != nil {
79		return err
80	}
81
82	*dst = Box{P: [2]Vec2{{x1, y1}, {x2, y2}}, Status: Present}
83	return nil
84}
85
86func (dst *Box) DecodeBinary(ci *ConnInfo, src []byte) error {
87	if src == nil {
88		*dst = Box{Status: Null}
89		return nil
90	}
91
92	if len(src) != 32 {
93		return errors.Errorf("invalid length for Box: %v", len(src))
94	}
95
96	x1 := binary.BigEndian.Uint64(src)
97	y1 := binary.BigEndian.Uint64(src[8:])
98	x2 := binary.BigEndian.Uint64(src[16:])
99	y2 := binary.BigEndian.Uint64(src[24:])
100
101	*dst = Box{
102		P: [2]Vec2{
103			{math.Float64frombits(x1), math.Float64frombits(y1)},
104			{math.Float64frombits(x2), math.Float64frombits(y2)},
105		},
106		Status: Present,
107	}
108	return nil
109}
110
111func (src *Box) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
112	switch src.Status {
113	case Null:
114		return nil, nil
115	case Undefined:
116		return nil, errUndefined
117	}
118
119	buf = append(buf, fmt.Sprintf(`(%f,%f),(%f,%f)`,
120		src.P[0].X, src.P[0].Y, src.P[1].X, src.P[1].Y)...)
121	return buf, nil
122}
123
124func (src *Box) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
125	switch src.Status {
126	case Null:
127		return nil, nil
128	case Undefined:
129		return nil, errUndefined
130	}
131
132	buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].X))
133	buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].Y))
134	buf = pgio.AppendUint64(buf, math.Float64bits(src.P[1].X))
135	buf = pgio.AppendUint64(buf, math.Float64bits(src.P[1].Y))
136
137	return buf, nil
138}
139
140// Scan implements the database/sql Scanner interface.
141func (dst *Box) Scan(src interface{}) error {
142	if src == nil {
143		*dst = Box{Status: Null}
144		return nil
145	}
146
147	switch src := src.(type) {
148	case string:
149		return dst.DecodeText(nil, []byte(src))
150	case []byte:
151		srcCopy := make([]byte, len(src))
152		copy(srcCopy, src)
153		return dst.DecodeText(nil, srcCopy)
154	}
155
156	return errors.Errorf("cannot scan %T", src)
157}
158
159// Value implements the database/sql/driver Valuer interface.
160func (src *Box) Value() (driver.Value, error) {
161	return EncodeValueText(src)
162}
163