package httpsfv

import (
	"encoding/base64"
	"errors"
	"strings"
)

// ErrInvalidBinaryFormat is returned when the binary format is invalid.
var ErrInvalidBinaryFormat = errors.New("invalid binary format")

// marshalBinary serializes as defined in
// https://httpwg.org/specs/rfc8941.html#ser-binary.
func marshalBinary(b *strings.Builder, bs []byte) error {
	if err := b.WriteByte(':'); err != nil {
		return err
	}

	buf := make([]byte, base64.StdEncoding.EncodedLen(len(bs)))
	base64.StdEncoding.Encode(buf, bs)

	if _, err := b.Write(buf); err != nil {
		return err
	}

	return b.WriteByte(':')
}

// parseBinary parses as defined in
// https://httpwg.org/specs/rfc8941.html#parse-binary.
func parseBinary(s *scanner) ([]byte, error) {
	if s.eof() || s.data[s.off] != ':' {
		return nil, &UnmarshalError{s.off, ErrInvalidBinaryFormat}
	}
	s.off++

	start := s.off

	for !s.eof() {
		c := s.data[s.off]
		if c == ':' {
			// base64decode
			decoded, err := base64.StdEncoding.DecodeString(s.data[start:s.off])
			if err != nil {
				return nil, &UnmarshalError{s.off, err}
			}
			s.off++

			return decoded, nil
		}

		if !isAlpha(c) && !isDigit(c) && c != '+' && c != '/' && c != '=' {
			return nil, &UnmarshalError{s.off, ErrInvalidBinaryFormat}
		}
		s.off++
	}

	return nil, &UnmarshalError{s.off, ErrInvalidBinaryFormat}
}
