package encoder

import (
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"gitlab.com/jaxnet/core/shard.core.git/chaincfg/chainhash"
	"gitlab.com/jaxnet/core/shard.core.git/wire/types"
	"io"
	"math"
	"net"
	"reflect"
	"time"
)

const (

	MaxVarIntPayload = 9

	binaryFreeListMaxItems = 1024

	CommandSize       = 12
	MaxMessagePayload = 1024 * 1024 * 32 
)

type MessageEncoding uint32

var (

	littleEndian = binary.LittleEndian

	bigEndian = binary.BigEndian
)

var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
	"encode a value greater than %x"

type Uint32Time time.Time

type Int64Time time.Time

func ReadElement(r io.Reader, element interface{}) error {

	switch e := element.(type) {
	case *int32:
		rv, err := BinarySerializer.Uint32(r, littleEndian)
		if err != nil {
			return err
		}
		*e = int32(rv)
		return nil

	case *uint32:
		rv, err := BinarySerializer.Uint32(r, littleEndian)
		if err != nil {
			return err
		}
		*e = rv
		return nil

	case *int64:
		rv, err := BinarySerializer.Uint64(r, littleEndian)
		if err != nil {
			return err
		}
		*e = int64(rv)
		return nil

	case *uint64:
		rv, err := BinarySerializer.Uint64(r, littleEndian)
		if err != nil {
			return err
		}
		*e = rv
		return nil

	case *bool:
		rv, err := BinarySerializer.Uint8(r)
		if err != nil {
			return err
		}
		if rv == 0x00 {
			*e = false
		} else {
			*e = true
		}
		return nil

	case *Uint32Time:
		rv, err := BinarySerializer.Uint32(r, binary.LittleEndian)
		if err != nil {
			return err
		}
		*e = Uint32Time(time.Unix(int64(rv), 0))
		return nil

	case *Int64Time:
		rv, err := BinarySerializer.Uint64(r, binary.LittleEndian)
		if err != nil {
			return err
		}
		*e = Int64Time(time.Unix(int64(rv), 0))
		return nil

	case *chainhash.Hash:
		_, err := io.ReadFull(r, e[:])
		if err != nil {
			return err
		}
		return nil

	case *types.ServiceFlag:
		rv, err := BinarySerializer.Uint64(r, littleEndian)
		if err != nil {
			return err
		}
		*e = types.ServiceFlag(rv)
		return nil

	case *types.InvType:
		rv, err := BinarySerializer.Uint32(r, littleEndian)
		if err != nil {
			return err
		}
		*e = types.InvType(rv)
		return nil

	case *types.BitcoinNet:
		rv, err := BinarySerializer.Uint32(r, littleEndian)
		if err != nil {
			return err
		}
		*e = types.BitcoinNet(rv)
		return nil

	case *types.BloomUpdateType:
		rv, err := BinarySerializer.Uint8(r)
		if err != nil {
			return err
		}
		*e = types.BloomUpdateType(rv)
		return nil

	case *types.RejectCode:
		rv, err := BinarySerializer.Uint8(r)
		if err != nil {
			return err
		}
		*e = types.RejectCode(rv)
		return nil
	}

	t := reflect.TypeOf(element)
	v := reflect.ValueOf(element)
	if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Array {
		v := v.Elem()
		bytes := make([]byte, v.Len())
		_, err := io.ReadFull(r, bytes[:])
		if err != nil {
			return err
		}
		reflect.Copy(v, reflect.ValueOf(bytes))
		return nil
	}

	return binary.Read(r, littleEndian, element)
}

func ReadElements(r io.Reader, elements ...interface{}) error {
	for _, element := range elements {
		err := ReadElement(r, element)
		if err != nil {
			return err
		}
	}
	return nil
}

func WriteElement(w io.Writer, element interface{}) error {

	switch e := element.(type) {
	case int32:
		err := BinarySerializer.PutUint32(w, littleEndian, uint32(e))
		if err != nil {
			return err
		}
		return nil

	case uint32:
		err := BinarySerializer.PutUint32(w, littleEndian, e)
		if err != nil {
			return err
		}
		return nil

	case int64:
		err := BinarySerializer.PutUint64(w, littleEndian, uint64(e))
		if err != nil {
			return err
		}
		return nil

	case uint64:
		err := BinarySerializer.PutUint64(w, littleEndian, e)
		if err != nil {
			return err
		}
		return nil

	case bool:
		var err error
		if e {
			err = BinarySerializer.PutUint8(w, 0x01)
		} else {
			err = BinarySerializer.PutUint8(w, 0x00)
		}
		if err != nil {
			return err
		}
		return nil

	case *chainhash.Hash:
		_, err := w.Write(e[:])
		if err != nil {
			return err
		}
		return nil

	case types.ServiceFlag:
		err := BinarySerializer.PutUint64(w, littleEndian, uint64(e))
		if err != nil {
			return err
		}
		return nil

	case types.InvType:
		err := BinarySerializer.PutUint32(w, littleEndian, uint32(e))
		if err != nil {
			return err
		}
		return nil

	case types.BitcoinNet:
		err := BinarySerializer.PutUint32(w, littleEndian, uint32(e))
		if err != nil {
			return err
		}
		return nil

	case types.BloomUpdateType:
		err := BinarySerializer.PutUint8(w, uint8(e))
		if err != nil {
			return err
		}
		return nil

	case types.RejectCode:
		err := BinarySerializer.PutUint8(w, uint8(e))
		if err != nil {
			return err
		}
		return nil
	}

	t := reflect.TypeOf(element)
	v := reflect.ValueOf(element)
	if t.Kind() == reflect.Array {
		bytes := make([]byte, v.Len())
		reflect.Copy(reflect.ValueOf(bytes), v)
		_, err := w.Write(bytes)
		return err
	}

	return binary.Write(w, littleEndian, element)
}

func WriteElements(w io.Writer, elements ...interface{}) error {
	for _, element := range elements {
		err := WriteElement(w, element)
		if err != nil {
			return err
		}
	}
	return nil
}

func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
	discriminant, err := BinarySerializer.Uint8(r)
	if err != nil {
		return 0, err
	}

	var rv uint64
	switch discriminant {
	case 0xff:
		sv, err := BinarySerializer.Uint64(r, littleEndian)
		if err != nil {
			return 0, err
		}
		rv = sv

		min := uint64(0x100000000)
		if rv < min {
			return 0, types.Error("ReadVarInt", fmt.Sprintf(
				errNonCanonicalVarInt, rv, discriminant, min))
		}

	case 0xfe:
		sv, err := BinarySerializer.Uint32(r, littleEndian)
		if err != nil {
			return 0, err
		}
		rv = uint64(sv)

		min := uint64(0x10000)
		if rv < min {
			return 0, types.Error("ReadVarInt", fmt.Sprintf(
				errNonCanonicalVarInt, rv, discriminant, min))
		}

	case 0xfd:
		sv, err := BinarySerializer.Uint16(r, littleEndian)
		if err != nil {
			return 0, err
		}
		rv = uint64(sv)

		min := uint64(0xfd)
		if rv < min {
			return 0, types.Error("ReadVarInt", fmt.Sprintf(
				errNonCanonicalVarInt, rv, discriminant, min))
		}

	default:
		rv = uint64(discriminant)
	}

	return rv, nil
}

func Uint32(r io.Reader) (uint32, error) {
	return BinarySerializer.Uint32(r, littleEndian)
}

func PutUint32(w io.Writer, val uint32) error {
	return BinarySerializer.PutUint32(w, littleEndian, val)
}

func Uint64(r io.Reader) (uint64, error) {
	return BinarySerializer.Uint64(r, littleEndian)
}
func PutUint64(w io.Writer, val uint64) error {
	return BinarySerializer.PutUint64(w, littleEndian, val)
}

func WriteVarInt(w io.Writer, val uint64) error {
	if val < 0xfd {
		return BinarySerializer.PutUint8(w, uint8(val))
	}

	if val <= math.MaxUint16 {
		err := BinarySerializer.PutUint8(w, 0xfd)
		if err != nil {
			return err
		}
		return BinarySerializer.PutUint16(w, littleEndian, uint16(val))
	}

	if val <= math.MaxUint32 {
		err := BinarySerializer.PutUint8(w, 0xfe)
		if err != nil {
			return err
		}
		return BinarySerializer.PutUint32(w, littleEndian, uint32(val))
	}

	err := BinarySerializer.PutUint8(w, 0xff)
	if err != nil {
		return err
	}
	return BinarySerializer.PutUint64(w, littleEndian, val)
}

func VarIntSerializeSize(val uint64) int {

	if val < 0xfd {
		return 1
	}

	if val <= math.MaxUint16 {
		return 3
	}

	if val <= math.MaxUint32 {
		return 5
	}

	return 9
}

func ReadVarString(r io.Reader, pver uint32) (string, error) {
	count, err := ReadVarInt(r, pver)
	if err != nil {
		return "", err
	}

	if count > MaxMessagePayload {
		str := fmt.Sprintf("variable length string is too long "+
			"[count %d, max %d]", count, MaxMessagePayload)
		return "", types.Error("ReadVarString", str)
	}

	buf := make([]byte, count)
	_, err = io.ReadFull(r, buf)
	if err != nil {
		return "", err
	}
	return string(buf), nil
}

func WriteVarString(w io.Writer, pver uint32, str string) error {
	err := WriteVarInt(w, uint64(len(str)))
	if err != nil {
		return err
	}
	_, err = w.Write([]byte(str))
	return err
}

func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32,
	fieldName string) ([]byte, error) {

	count, err := ReadVarInt(r, pver)
	if err != nil {
		return nil, err
	}

	if count > uint64(maxAllowed) {
		str := fmt.Sprintf("%s is larger than the max allowed size "+
			"[count %d, max %d]", fieldName, count, maxAllowed)
		return nil, types.Error("ReadVarBytes", str)
	}

	b := make([]byte, count)
	_, err = io.ReadFull(r, b)
	if err != nil {
		return nil, err
	}
	return b, nil
}

func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error {
	slen := uint64(len(bytes))
	err := WriteVarInt(w, slen)
	if err != nil {
		return err
	}

	_, err = w.Write(bytes)
	return err
}

func randomUint64(r io.Reader) (uint64, error) {
	rv, err := BinarySerializer.Uint64(r, bigEndian)
	if err != nil {
		return 0, err
	}
	return rv, nil
}

func RandomUint64() (uint64, error) {
	return randomUint64(rand.Reader)
}

func ReadInvVect(r io.Reader, iv *types.InvVect) error {

	return ReadElements(r, &iv.Type, &iv.Hash)
}

func WriteInvVect(w io.Writer, iv *types.InvVect) error {

	return WriteElements(w, iv.Type, &iv.Hash)
}

func ReadNetAddress(r io.Reader, pver uint32, na *types.NetAddress, ts bool) error {
	var ip [16]byte

	if ts && pver >= types.NetAddressTimeVersion {
		err := ReadElements(r, (*Uint32Time)(&na.Timestamp))
		if err != nil {
			return err
		}
	}

	err := ReadElements(r, &na.Services, &ip)
	if err != nil {
		return err
	}

	port, err := BinarySerializer.Uint16(r, binary.BigEndian)
	if err != nil {
		return err
	}

	*na = types.NetAddress{
		Timestamp: na.Timestamp,
		Services:  na.Services,
		IP:        net.IP(ip[:]),
		Port:      port,
	}
	return nil
}

func WriteNetAddress(w io.Writer, pver uint32, na *types.NetAddress, ts bool) error {

	if ts && pver >= types.NetAddressTimeVersion {
		err := WriteElements(w, uint32(na.Timestamp.Unix()))
		if err != nil {
			return err
		}
	}

	var ip [16]byte
	if na.IP != nil {
		copy(ip[:], na.IP.To16())
	}
	err := WriteElements(w, na.Services, ip)
	if err != nil {
		return err
	}

	return binary.Write(w, binary.BigEndian, na.Port)
}
package encoder

import (
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"gitlab.com/jaxnet/core/shard.core.git/chaincfg/chainhash"
	"gitlab.com/jaxnet/core/shard.core.git/wire/types"
	"io"
	"math"
	"net"
	"reflect"
	"time"
)

const (
	// MaxVarIntPayload is the maximum payload size for a variable length integer.
	MaxVarIntPayload = 9

	// binaryFreeListMaxItems is the number of buffers to keep in the free
	// list to use for binary serialization and deserialization.
	binaryFreeListMaxItems = 1024

	CommandSize       = 12
	MaxMessagePayload = 1024 * 1024 * 32 // 32MB
)

type MessageEncoding uint32

var (
	// littleEndian is a convenience variable since binary.LittleEndian is
	// quite long.
	littleEndian = binary.LittleEndian

	// bigEndian is a convenience variable since binary.BigEndian is quite
	// long.
	bigEndian = binary.BigEndian
)

//
//type Encoder interface {
//	ReadElements(r io.Reader, elements ...interface{}) error
//	WriteElements(w io.Writer, elements ...interface{}) error
//
//	ReadElement(r io.Reader, element interface{}) error
//	WriteElement(w io.Writer, element interface{}) error
//
//	ReadVarInt(r io.Reader, pver uint32) (uint64, error)
//	WriteVarInt(w io.Writer, val uint64) error
//
//	ReadInvVect(r io.Reader, iv *types.InvVect) error
//	WriteInvVect(w io.Writer, iv *types.InvVect) error
//
//	ReadVarString(r io.Reader, pver uint32) (string, error)
//	WriteVarString(w io.Writer, pver uint32, str string) error
//
//	ReadNetAddress(r io.Reader, pver uint32, na *types.NetAddress, ts bool) error
//	WriteNetAddress(w io.Writer, pver uint32, na *types.NetAddress, ts bool) error
//
//	ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32, fieldName string) ([]byte, error)
//	WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error
//
//	VarIntSerializeSize(val uint64) int
//
//	Uint32(r io.Reader, ) (uint32, error)
//	PutUint32(w io.Writer, val uint32) error
//
//	Uint64(r io.Reader, ) (uint64, error)
//	PutUint64(w io.Writer, val uint64) error
//}

// errNonCanonicalVarInt is the common format string used for non-canonically
// encoded variable length integer errors.
var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
	"encode a value greater than %x"

// Uint32Time represents a unix timestamp encoded with a uint32.  It is used as
// a way to signal the readElement function how to decode a timestamp into a Go
// time.Time since it is otherwise ambiguous.
type Uint32Time time.Time

// Int64Time represents a unix timestamp encoded with an int64.  It is used as
// a way to signal the readElement function how to decode a timestamp into a Go
// time.Time since it is otherwise ambiguous.
type Int64Time time.Time

// readElement reads the next sequence of bytes from r using little endian
// depending on the concrete type of element pointed to.
func ReadElement(r io.Reader, element interface{}) error {

	// Attempt to read the element based on the concrete type via fast
	// type assertions first.
	switch e := element.(type) {
	case *int32:
		rv, err := BinarySerializer.Uint32(r, littleEndian)
		if err != nil {
			return err
		}
		*e = int32(rv)
		return nil

	case *uint32:
		rv, err := BinarySerializer.Uint32(r, littleEndian)
		if err != nil {
			return err
		}
		*e = rv
		return nil

	case *int64:
		rv, err := BinarySerializer.Uint64(r, littleEndian)
		if err != nil {
			return err
		}
		*e = int64(rv)
		return nil

	case *uint64:
		rv, err := BinarySerializer.Uint64(r, littleEndian)
		if err != nil {
			return err
		}
		*e = rv
		return nil

	case *bool:
		rv, err := BinarySerializer.Uint8(r)
		if err != nil {
			return err
		}
		if rv == 0x00 {
			*e = false
		} else {
			*e = true
		}
		return nil

	// Unix timestamp encoded as a uint32.
	case *Uint32Time:
		rv, err := BinarySerializer.Uint32(r, binary.LittleEndian)
		if err != nil {
			return err
		}
		*e = Uint32Time(time.Unix(int64(rv), 0))
		return nil

	// Unix timestamp encoded as an int64.
	case *Int64Time:
		rv, err := BinarySerializer.Uint64(r, binary.LittleEndian)
		if err != nil {
			return err
		}
		*e = Int64Time(time.Unix(int64(rv), 0))
		return nil

	case *chainhash.Hash:
		_, err := io.ReadFull(r, e[:])
		if err != nil {
			return err
		}
		return nil

	case *types.ServiceFlag:
		rv, err := BinarySerializer.Uint64(r, littleEndian)
		if err != nil {
			return err
		}
		*e = types.ServiceFlag(rv)
		return nil

	case *types.InvType:
		rv, err := BinarySerializer.Uint32(r, littleEndian)
		if err != nil {
			return err
		}
		*e = types.InvType(rv)
		return nil

	case *types.BitcoinNet:
		rv, err := BinarySerializer.Uint32(r, littleEndian)
		if err != nil {
			return err
		}
		*e = types.BitcoinNet(rv)
		return nil

	case *types.BloomUpdateType:
		rv, err := BinarySerializer.Uint8(r)
		if err != nil {
			return err
		}
		*e = types.BloomUpdateType(rv)
		return nil

	case *types.RejectCode:
		rv, err := BinarySerializer.Uint8(r)
		if err != nil {
			return err
		}
		*e = types.RejectCode(rv)
		return nil
	}

	t := reflect.TypeOf(element)
	v := reflect.ValueOf(element)
	if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Array {
		v := v.Elem()
		bytes := make([]byte, v.Len())
		_, err := io.ReadFull(r, bytes[:])
		if err != nil {
			return err
		}
		reflect.Copy(v, reflect.ValueOf(bytes))
		return nil
	}

	// Fall back to the slower binary.Read if a fast path was not available
	// above.
	return binary.Read(r, littleEndian, element)
}

// readElements reads multiple items from r.  It is equivalent to multiple
// calls to readElement.
func ReadElements(r io.Reader, elements ...interface{}) error {
	for _, element := range elements {
		err := ReadElement(r, element)
		if err != nil {
			return err
		}
	}
	return nil
}

// writeElement writes the little endian representation of element to w.
func WriteElement(w io.Writer, element interface{}) error {

	// Attempt to write the element based on the concrete type via fast
	// type assertions first.
	switch e := element.(type) {
	case int32:
		err := BinarySerializer.PutUint32(w, littleEndian, uint32(e))
		if err != nil {
			return err
		}
		return nil

	case uint32:
		err := BinarySerializer.PutUint32(w, littleEndian, e)
		if err != nil {
			return err
		}
		return nil

	case int64:
		err := BinarySerializer.PutUint64(w, littleEndian, uint64(e))
		if err != nil {
			return err
		}
		return nil

	case uint64:
		err := BinarySerializer.PutUint64(w, littleEndian, e)
		if err != nil {
			return err
		}
		return nil

	case bool:
		var err error
		if e {
			err = BinarySerializer.PutUint8(w, 0x01)
		} else {
			err = BinarySerializer.PutUint8(w, 0x00)
		}
		if err != nil {
			return err
		}
		return nil

	// Message header checksum.

	case *chainhash.Hash:
		_, err := w.Write(e[:])
		if err != nil {
			return err
		}
		return nil

	case types.ServiceFlag:
		err := BinarySerializer.PutUint64(w, littleEndian, uint64(e))
		if err != nil {
			return err
		}
		return nil

	case types.InvType:
		err := BinarySerializer.PutUint32(w, littleEndian, uint32(e))
		if err != nil {
			return err
		}
		return nil

	case types.BitcoinNet:
		err := BinarySerializer.PutUint32(w, littleEndian, uint32(e))
		if err != nil {
			return err
		}
		return nil

	case types.BloomUpdateType:
		err := BinarySerializer.PutUint8(w, uint8(e))
		if err != nil {
			return err
		}
		return nil

	case types.RejectCode:
		err := BinarySerializer.PutUint8(w, uint8(e))
		if err != nil {
			return err
		}
		return nil
	}

	t := reflect.TypeOf(element)
	v := reflect.ValueOf(element)
	if t.Kind() == reflect.Array {
		bytes := make([]byte, v.Len())
		reflect.Copy(reflect.ValueOf(bytes), v)
		_, err := w.Write(bytes)
		return err
	}

	// Fall back to the slower binary.Write if a fast path was not available
	// above.
	return binary.Write(w, littleEndian, element)
}

// writeElements writes multiple items to w.  It is equivalent to multiple
// calls to writeElement.
func WriteElements(w io.Writer, elements ...interface{}) error {
	for _, element := range elements {
		err := WriteElement(w, element)
		if err != nil {
			return err
		}
	}
	return nil
}

// ReadVarInt reads a variable length integer from r and returns it as a uint64.
func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
	discriminant, err := BinarySerializer.Uint8(r)
	if err != nil {
		return 0, err
	}

	var rv uint64
	switch discriminant {
	case 0xff:
		sv, err := BinarySerializer.Uint64(r, littleEndian)
		if err != nil {
			return 0, err
		}
		rv = sv

		// The encoding is not canonical if the value could have been
		// encoded using fewer bytes.
		min := uint64(0x100000000)
		if rv < min {
			return 0, types.Error("ReadVarInt", fmt.Sprintf(
				errNonCanonicalVarInt, rv, discriminant, min))
		}

	case 0xfe:
		sv, err := BinarySerializer.Uint32(r, littleEndian)
		if err != nil {
			return 0, err
		}
		rv = uint64(sv)

		// The encoding is not canonical if the value could have been
		// encoded using fewer bytes.
		min := uint64(0x10000)
		if rv < min {
			return 0, types.Error("ReadVarInt", fmt.Sprintf(
				errNonCanonicalVarInt, rv, discriminant, min))
		}

	case 0xfd:
		sv, err := BinarySerializer.Uint16(r, littleEndian)
		if err != nil {
			return 0, err
		}
		rv = uint64(sv)

		// The encoding is not canonical if the value could have been
		// encoded using fewer bytes.
		min := uint64(0xfd)
		if rv < min {
			return 0, types.Error("ReadVarInt", fmt.Sprintf(
				errNonCanonicalVarInt, rv, discriminant, min))
		}

	default:
		rv = uint64(discriminant)
	}

	return rv, nil
}

func Uint32(r io.Reader) (uint32, error) {
	return BinarySerializer.Uint32(r, littleEndian)
}

func PutUint32(w io.Writer, val uint32) error {
	return BinarySerializer.PutUint32(w, littleEndian, val)
}

func Uint64(r io.Reader) (uint64, error) {
	return BinarySerializer.Uint64(r, littleEndian)
}
func PutUint64(w io.Writer, val uint64) error {
	return BinarySerializer.PutUint64(w, littleEndian, val)
}

// WriteVarInt serializes val to w using a variable number of bytes depending
// on its value.
func WriteVarInt(w io.Writer, val uint64) error {
	if val < 0xfd {
		return BinarySerializer.PutUint8(w, uint8(val))
	}

	if val <= math.MaxUint16 {
		err := BinarySerializer.PutUint8(w, 0xfd)
		if err != nil {
			return err
		}
		return BinarySerializer.PutUint16(w, littleEndian, uint16(val))
	}

	if val <= math.MaxUint32 {
		err := BinarySerializer.PutUint8(w, 0xfe)
		if err != nil {
			return err
		}
		return BinarySerializer.PutUint32(w, littleEndian, uint32(val))
	}

	err := BinarySerializer.PutUint8(w, 0xff)
	if err != nil {
		return err
	}
	return BinarySerializer.PutUint64(w, littleEndian, val)
}

// VarIntSerializeSize returns the number of bytes it would take to serialize
// val as a variable length integer.
func VarIntSerializeSize(val uint64) int {
	// The value is small enough to be represented by itself, so it's
	// just 1 byte.
	if val < 0xfd {
		return 1
	}

	// Discriminant 1 byte plus 2 bytes for the uint16.
	if val <= math.MaxUint16 {
		return 3
	}

	// Discriminant 1 byte plus 4 bytes for the uint32.
	if val <= math.MaxUint32 {
		return 5
	}

	// Discriminant 1 byte plus 8 bytes for the uint64.
	return 9
}

// ReadVarString reads a variable length string from r and returns it as a Go
// string.  A variable length string is encoded as a variable length integer
// containing the length of the string followed by the bytes that represent the
// string itself.  An error is returned if the length is greater than the
// maximum block payload size since it helps protect against memory exhaustion
// attacks and forced panics through malformed messages.
func ReadVarString(r io.Reader, pver uint32) (string, error) {
	count, err := ReadVarInt(r, pver)
	if err != nil {
		return "", err
	}

	// Prevent variable length strings that are larger than the maximum
	// message size.  It would be possible to cause memory exhaustion and
	// panics without a sane upper bound on this count.
	if count > MaxMessagePayload {
		str := fmt.Sprintf("variable length string is too long "+
			"[count %d, max %d]", count, MaxMessagePayload)
		return "", types.Error("ReadVarString", str)
	}

	buf := make([]byte, count)
	_, err = io.ReadFull(r, buf)
	if err != nil {
		return "", err
	}
	return string(buf), nil
}

// WriteVarString serializes str to w as a variable length integer containing
// the length of the string followed by the bytes that represent the string
// itself.
func WriteVarString(w io.Writer, pver uint32, str string) error {
	err := WriteVarInt(w, uint64(len(str)))
	if err != nil {
		return err
	}
	_, err = w.Write([]byte(str))
	return err
}

// ReadVarBytes reads a variable length byte array.  A byte array is encoded
// as a varInt containing the length of the array followed by the bytes
// themselves.  An error is returned if the length is greater than the
// passed maxAllowed parameter which helps protect against memory exhaustion
// attacks and forced panics through malformed messages.  The fieldName
// parameter is only used for the error message so it provides more context in
// the error.
func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32,
	fieldName string) ([]byte, error) {

	count, err := ReadVarInt(r, pver)
	if err != nil {
		return nil, err
	}

	// Prevent byte array larger than the max message size.  It would
	// be possible to cause memory exhaustion and panics without a sane
	// upper bound on this count.
	if count > uint64(maxAllowed) {
		str := fmt.Sprintf("%s is larger than the max allowed size "+
			"[count %d, max %d]", fieldName, count, maxAllowed)
		return nil, types.Error("ReadVarBytes", str)
	}

	b := make([]byte, count)
	_, err = io.ReadFull(r, b)
	if err != nil {
		return nil, err
	}
	return b, nil
}

// WriteVarBytes serializes a variable length byte array to w as a varInt
// containing the number of bytes, followed by the bytes themselves.
func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error {
	slen := uint64(len(bytes))
	err := WriteVarInt(w, slen)
	if err != nil {
		return err
	}

	_, err = w.Write(bytes)
	return err
}

// randomUint64 returns a cryptographically random uint64 value.  This
// unexported version takes a reader primarily to ensure the error paths
// can be properly tested by passing a fake reader in the tests.
func randomUint64(r io.Reader) (uint64, error) {
	rv, err := BinarySerializer.Uint64(r, bigEndian)
	if err != nil {
		return 0, err
	}
	return rv, nil
}

// RandomUint64 returns a cryptographically random uint64 value.
func RandomUint64() (uint64, error) {
	return randomUint64(rand.Reader)
}

// ReadInvVect reads an encoded InvVect from r depending on the protocol
// version.
func ReadInvVect(r io.Reader, iv *types.InvVect) error {
	//enc := NewEncoder(CommandSize)
	return ReadElements(r, &iv.Type, &iv.Hash)
}

// WriteInvVect serializes an InvVect to w depending on the protocol version.
func WriteInvVect(w io.Writer, iv *types.InvVect) error {
	//enc := chain.NewEncoder(chain.CommandSize)
	return WriteElements(w, iv.Type, &iv.Hash)
}

// readNetAddress reads an encoded NetAddress from r depending on the protocol
// version and whether or not the timestamp is included per ts.  Some messages
// like version do not include the timestamp.
func ReadNetAddress(r io.Reader, pver uint32, na *types.NetAddress, ts bool) error {
	var ip [16]byte

	//enc := chain.NewEncoder(chain.CommandSize)
	// NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will
	// stop working somewhere around 2106.  Also timestamp wasn't added until
	// protocol version >= NetAddressTimeVersion
	if ts && pver >= types.NetAddressTimeVersion {
		err := ReadElements(r, (*Uint32Time)(&na.Timestamp))
		if err != nil {
			return err
		}
	}

	err := ReadElements(r, &na.Services, &ip)
	if err != nil {
		return err
	}
	// Sigh.  Bitcoin protocol mixes little and big endian.
	port, err := BinarySerializer.Uint16(r, binary.BigEndian)
	if err != nil {
		return err
	}

	*na = types.NetAddress{
		Timestamp: na.Timestamp,
		Services:  na.Services,
		IP:        net.IP(ip[:]),
		Port:      port,
	}
	return nil
}

// writeNetAddress serializes a NetAddress to w depending on the protocol
// version and whether or not the timestamp is included per ts.  Some messages
// like version do not include the timestamp.
func WriteNetAddress(w io.Writer, pver uint32, na *types.NetAddress, ts bool) error {
	// NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will
	// stop working somewhere around 2106.  Also timestamp wasn't added until
	// until protocol version >= NetAddressTimeVersion.
	//enc := chain.NewEncoder(chain.CommandSize)
	if ts && pver >= types.NetAddressTimeVersion {
		err := WriteElements(w, uint32(na.Timestamp.Unix()))
		if err != nil {
			return err
		}
	}

	// Ensure to always write 16 bytes even if the ip is nil.
	var ip [16]byte
	if na.IP != nil {
		copy(ip[:], na.IP.To16())
	}
	err := WriteElements(w, na.Services, ip)
	if err != nil {
		return err
	}

	// Sigh.  Bitcoin protocol mixes little and big endian.
	return binary.Write(w, binary.BigEndian, na.Port)
}
