package lnwire

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"

	"github.com/lightningnetwork/lnd/tlv"
)

var (
	// ErrFeaturePairExists signals an error in feature vector construction
	// where the opposing bit in a feature pair has already been set.
	ErrFeaturePairExists = errors.New("feature pair exists")

	// ErrFeatureStandard is returned when attempts to modify LND's known
	// set of features are made.
	ErrFeatureStandard = errors.New("feature is used in standard " +
		"protocol set")

	// ErrFeatureBitMaximum is returned when a feature bit exceeds the
	// maximum allowable value.
	ErrFeatureBitMaximum = errors.New("feature bit exceeds allowed maximum")
)

// FeatureBit represents a feature that can be enabled in either a local or
// global feature vector at a specific bit position. Feature bits follow the
// "it's OK to be odd" rule, where features at even bit positions must be known
// to a node receiving them from a peer while odd bits do not. In accordance,
// feature bits are usually assigned in pairs, first being assigned an odd bit
// position which may later be changed to the preceding even position once
// knowledge of the feature becomes required on the network.
type FeatureBit uint16

const (
	// DataLossProtectRequired is a feature bit that indicates that a peer
	// *requires* the other party know about the data-loss-protect optional
	// feature. If the remote peer does not know of such a feature, then
	// the sending peer SHOULD disconnect them. The data-loss-protect
	// feature allows a peer that's lost partial data to recover their
	// settled funds of the latest commitment state.
	DataLossProtectRequired FeatureBit = 0

	// DataLossProtectOptional is an optional feature bit that indicates
	// that the sending peer knows of this new feature and can activate it
	// it. The data-loss-protect feature allows a peer that's lost partial
	// data to recover their settled funds of the latest commitment state.
	DataLossProtectOptional FeatureBit = 1

	// InitialRoutingSync is a local feature bit meaning that the receiving
	// node should send a complete dump of routing information when a new
	// connection is established.
	InitialRoutingSync FeatureBit = 3

	// UpfrontShutdownScriptRequired is a feature bit which indicates that a
	// peer *requires* that the remote peer accept an upfront shutdown script to
	// which payout is enforced on cooperative closes.
	UpfrontShutdownScriptRequired FeatureBit = 4

	// UpfrontShutdownScriptOptional is an optional feature bit which indicates
	// that the peer will accept an upfront shutdown script to which payout is
	// enforced on cooperative closes.
	UpfrontShutdownScriptOptional FeatureBit = 5

	// GossipQueriesRequired is a feature bit that indicates that the
	// receiving peer MUST know of the set of features that allows nodes to
	// more efficiently query the network view of peers on the network for
	// reconciliation purposes.
	GossipQueriesRequired FeatureBit = 6

	// GossipQueriesOptional is an optional feature bit that signals that
	// the setting peer knows of the set of features that allows more
	// efficient network view reconciliation.
	GossipQueriesOptional FeatureBit = 7

	// TLVOnionPayloadRequired is a feature bit that indicates a node is
	// able to decode the new TLV information included in the onion packet.
	TLVOnionPayloadRequired FeatureBit = 8

	// TLVOnionPayloadOptional is an optional feature bit that indicates a
	// node is able to decode the new TLV information included in the onion
	// packet.
	TLVOnionPayloadOptional FeatureBit = 9

	// StaticRemoteKeyRequired is a required feature bit that signals that
	// within one's commitment transaction, the key used for the remote
	// party's non-delay output should not be tweaked.
	StaticRemoteKeyRequired FeatureBit = 12

	// StaticRemoteKeyOptional is an optional feature bit that signals that
	// within one's commitment transaction, the key used for the remote
	// party's non-delay output should not be tweaked.
	StaticRemoteKeyOptional FeatureBit = 13

	// PaymentAddrRequired is a required feature bit that signals that a
	// node requires payment addresses, which are used to mitigate probing
	// attacks on the receiver of a payment.
	PaymentAddrRequired FeatureBit = 14

	// PaymentAddrOptional is an optional feature bit that signals that a
	// node supports payment addresses, which are used to mitigate probing
	// attacks on the receiver of a payment.
	PaymentAddrOptional FeatureBit = 15

	// MPPRequired is a required feature bit that signals that the receiver
	// of a payment requires settlement of an invoice with more than one
	// HTLC.
	MPPRequired FeatureBit = 16

	// MPPOptional is an optional feature bit that signals that the receiver
	// of a payment supports settlement of an invoice with more than one
	// HTLC.
	MPPOptional FeatureBit = 17

	// WumboChannelsRequired is a required feature bit that signals that a
	// node is willing to accept channels larger than 2^24 satoshis.
	WumboChannelsRequired FeatureBit = 18

	// WumboChannelsOptional is an optional feature bit that signals that a
	// node is willing to accept channels larger than 2^24 satoshis.
	WumboChannelsOptional FeatureBit = 19

	// AnchorsRequired is a required feature bit that signals that the node
	// requires channels to be made using commitments having anchor
	// outputs.
	AnchorsRequired FeatureBit = 20

	// AnchorsOptional is an optional feature bit that signals that the
	// node supports channels to be made using commitments having anchor
	// outputs.
	AnchorsOptional FeatureBit = 21

	// AnchorsZeroFeeHtlcTxRequired is a required feature bit that signals
	// that the node requires channels having zero-fee second-level HTLC
	// transactions, which also imply anchor commitments.
	AnchorsZeroFeeHtlcTxRequired FeatureBit = 22

	// AnchorsZeroFeeHtlcTxOptional is an optional feature bit that signals
	// that the node supports channels having zero-fee second-level HTLC
	// transactions, which also imply anchor commitments.
	AnchorsZeroFeeHtlcTxOptional FeatureBit = 23

	// RouteBlindingRequired is a required feature bit that signals that
	// the node supports blinded payments.
	RouteBlindingRequired FeatureBit = 24

	// RouteBlindingOptional is an optional feature bit that signals that
	// the node supports blinded payments.
	RouteBlindingOptional FeatureBit = 25

	// ShutdownAnySegwitRequired is an required feature bit that signals
	// that the sender is able to properly handle/parse segwit witness
	// programs up to version 16. This enables utilization of Taproot
	// addresses for cooperative closure addresses.
	ShutdownAnySegwitRequired FeatureBit = 26

	// ShutdownAnySegwitOptional is an optional feature bit that signals
	// that the sender is able to properly handle/parse segwit witness
	// programs up to version 16. This enables utilization of Taproot
	// addresses for cooperative closure addresses.
	ShutdownAnySegwitOptional FeatureBit = 27

	// AMPRequired is a required feature bit that signals that the receiver
	// of a payment supports accepts spontaneous payments, i.e.
	// sender-generated preimages according to BOLT XX.
	AMPRequired FeatureBit = 30

	// AMPOptional is an optional feature bit that signals that the receiver
	// of a payment supports accepts spontaneous payments, i.e.
	// sender-generated preimages according to BOLT XX.
	AMPOptional FeatureBit = 31

	// ExplicitChannelTypeRequired is a required bit that denotes that a
	// connection established with this node is to use explicit channel
	// commitment types for negotiation instead of the existing implicit
	// negotiation methods. With this bit, there is no longer a "default"
	// implicit channel commitment type, allowing a connection to
	// open/maintain types of several channels over its lifetime.
	ExplicitChannelTypeRequired = 44

	// ExplicitChannelTypeOptional is an optional bit that denotes that a
	// connection established with this node is to use explicit channel
	// commitment types for negotiation instead of the existing implicit
	// negotiation methods. With this bit, there is no longer a "default"
	// implicit channel commitment type, allowing a connection to
	// TODO: Decide on actual feature bit value.
	ExplicitChannelTypeOptional = 45

	// ScidAliasRequired is a required feature bit that signals that the
	// node requires understanding of ShortChannelID aliases in the TLV
	// segment of the channel_ready message.
	ScidAliasRequired FeatureBit = 46

	// ScidAliasOptional is an optional feature bit that signals that the
	// node understands ShortChannelID aliases in the TLV segment of the
	// channel_ready message.
	ScidAliasOptional FeatureBit = 47

	// PaymentMetadataRequired is a required bit that denotes that if an
	// invoice contains metadata, it must be passed along with the payment
	// htlc(s).
	PaymentMetadataRequired = 48

	// PaymentMetadataOptional is an optional bit that denotes that if an
	// invoice contains metadata, it may be passed along with the payment
	// htlc(s).
	PaymentMetadataOptional = 49

	// ZeroConfRequired is a required feature bit that signals that the
	// node requires understanding of the zero-conf channel_type.
	ZeroConfRequired FeatureBit = 50

	// ZeroConfOptional is an optional feature bit that signals that the
	// node understands the zero-conf channel type.
	ZeroConfOptional FeatureBit = 51

	// KeysendRequired is a required bit that indicates that the node is
	// able and willing to accept keysend payments.
	KeysendRequired = 54

	// KeysendOptional is an optional bit that indicates that the node is
	// able and willing to accept keysend payments.
	KeysendOptional = 55

	// ScriptEnforcedLeaseRequired is a required feature bit that signals
	// that the node requires channels having zero-fee second-level HTLC
	// transactions, which also imply anchor commitments, along with an
	// additional CLTV constraint of a channel lease's expiration height
	// applied to all outputs that pay directly to the channel initiator.
	//
	// TODO: Decide on actual feature bit value.
	ScriptEnforcedLeaseRequired FeatureBit = 2022

	// ScriptEnforcedLeaseOptional is an optional feature bit that signals
	// that the node requires channels having zero-fee second-level HTLC
	// transactions, which also imply anchor commitments, along with an
	// additional CLTV constraint of a channel lease's expiration height
	// applied to all outputs that pay directly to the channel initiator.
	//
	// TODO: Decide on actual feature bit value.
	ScriptEnforcedLeaseOptional FeatureBit = 2023

	// SimpleTaprootChannelsRequiredFinal is a required bit that indicates
	// the node is able to create taproot-native channels. This is the
	// final feature bit to be used once the channel type is finalized.
	SimpleTaprootChannelsRequiredFinal = 80

	// SimpleTaprootChannelsOptionalFinal is an optional bit that indicates
	// the node is able to create taproot-native channels. This is the
	// final feature bit to be used once the channel type is finalized.
	SimpleTaprootChannelsOptionalFinal = 81

	// SimpleTaprootChannelsRequiredStaging is a required bit that indicates
	// the node is able to create taproot-native channels. This is a
	// feature bit used in the wild while the channel type is still being
	// finalized.
	SimpleTaprootChannelsRequiredStaging = 180

	// SimpleTaprootChannelsOptionalStaging is an optional bit that
	// indicates the node is able to create taproot-native channels. This
	// is a feature bit used in the wild while the channel type is still
	// being finalized.
	SimpleTaprootChannelsOptionalStaging = 181

	// Bolt11BlindedPathsRequired is a required feature bit that indicates
	// that the node is able to understand the blinded path tagged field in
	// a BOLT 11 invoice.
	Bolt11BlindedPathsRequired = 262

	// Bolt11BlindedPathsOptional is an optional feature bit that indicates
	// that the node is able to understand the blinded path tagged field in
	// a BOLT 11 invoice.
	Bolt11BlindedPathsOptional = 263

	// SimpleTaprootOverlayChansRequired is a required bit that indicates
	// support for the special custom taproot overlay channel.
	SimpleTaprootOverlayChansOptional = 2025

	// SimpleTaprootOverlayChansRequired is a required bit that indicates
	// support for the special custom taproot overlay channel.
	SimpleTaprootOverlayChansRequired = 2026

	// MaxBolt11Feature is the maximum feature bit value allowed in bolt 11
	// invoices.
	//
	// The base 32 encoded tagged fields in invoices are limited to 10 bits
	// to express the length of the field's data.
	//nolint:lll
	// See: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md#tagged-fields
	//
	// With a maximum length field of 1023 (2^10 -1) and 5 bit encoding,
	// the highest feature bit that can be expressed is:
	// 1023 * 5 - 1 = 5114.
	MaxBolt11Feature = 5114
)

// IsRequired returns true if the feature bit is even, and false otherwise.
func (b FeatureBit) IsRequired() bool {
	return b&0x01 == 0x00
}

// Features is a mapping of known feature bits to a descriptive name. All known
// feature bits must be assigned a name in this mapping, and feature bit pairs
// must be assigned together for correct behavior.
var Features = map[FeatureBit]string{
	DataLossProtectRequired:              "data-loss-protect",
	DataLossProtectOptional:              "data-loss-protect",
	InitialRoutingSync:                   "initial-routing-sync",
	UpfrontShutdownScriptRequired:        "upfront-shutdown-script",
	UpfrontShutdownScriptOptional:        "upfront-shutdown-script",
	GossipQueriesRequired:                "gossip-queries",
	GossipQueriesOptional:                "gossip-queries",
	TLVOnionPayloadRequired:              "tlv-onion",
	TLVOnionPayloadOptional:              "tlv-onion",
	StaticRemoteKeyOptional:              "static-remote-key",
	StaticRemoteKeyRequired:              "static-remote-key",
	PaymentAddrOptional:                  "payment-addr",
	PaymentAddrRequired:                  "payment-addr",
	MPPOptional:                          "multi-path-payments",
	MPPRequired:                          "multi-path-payments",
	AnchorsRequired:                      "anchor-commitments",
	AnchorsOptional:                      "anchor-commitments",
	AnchorsZeroFeeHtlcTxRequired:         "anchors-zero-fee-htlc-tx",
	AnchorsZeroFeeHtlcTxOptional:         "anchors-zero-fee-htlc-tx",
	WumboChannelsRequired:                "wumbo-channels",
	WumboChannelsOptional:                "wumbo-channels",
	AMPRequired:                          "amp",
	AMPOptional:                          "amp",
	PaymentMetadataOptional:              "payment-metadata",
	PaymentMetadataRequired:              "payment-metadata",
	ExplicitChannelTypeOptional:          "explicit-commitment-type",
	ExplicitChannelTypeRequired:          "explicit-commitment-type",
	KeysendOptional:                      "keysend",
	KeysendRequired:                      "keysend",
	ScriptEnforcedLeaseRequired:          "script-enforced-lease",
	ScriptEnforcedLeaseOptional:          "script-enforced-lease",
	ScidAliasRequired:                    "scid-alias",
	ScidAliasOptional:                    "scid-alias",
	ZeroConfRequired:                     "zero-conf",
	ZeroConfOptional:                     "zero-conf",
	RouteBlindingRequired:                "route-blinding",
	RouteBlindingOptional:                "route-blinding",
	ShutdownAnySegwitRequired:            "shutdown-any-segwit",
	ShutdownAnySegwitOptional:            "shutdown-any-segwit",
	SimpleTaprootChannelsRequiredFinal:   "simple-taproot-chans",
	SimpleTaprootChannelsOptionalFinal:   "simple-taproot-chans",
	SimpleTaprootChannelsRequiredStaging: "simple-taproot-chans-x",
	SimpleTaprootChannelsOptionalStaging: "simple-taproot-chans-x",
	SimpleTaprootOverlayChansOptional:    "taproot-overlay-chans",
	SimpleTaprootOverlayChansRequired:    "taproot-overlay-chans",
	Bolt11BlindedPathsOptional:           "bolt-11-blinded-paths",
	Bolt11BlindedPathsRequired:           "bolt-11-blinded-paths",
}

// RawFeatureVector represents a set of feature bits as defined in BOLT-09.  A
// RawFeatureVector itself just stores a set of bit flags but can be used to
// construct a FeatureVector which binds meaning to each bit. Feature vectors
// can be serialized and deserialized to/from a byte representation that is
// transmitted in Lightning network messages.
type RawFeatureVector struct {
	features map[FeatureBit]struct{}
}

// NewRawFeatureVector creates a feature vector with all of the feature bits
// given as arguments enabled.
func NewRawFeatureVector(bits ...FeatureBit) *RawFeatureVector {
	fv := &RawFeatureVector{features: make(map[FeatureBit]struct{})}
	for _, bit := range bits {
		fv.Set(bit)
	}
	return fv
}

// IsEmpty returns whether the feature vector contains any feature bits.
func (fv RawFeatureVector) IsEmpty() bool {
	return len(fv.features) == 0
}

// OnlyContains determines whether only the specified feature bits are found.
func (fv RawFeatureVector) OnlyContains(bits ...FeatureBit) bool {
	if len(bits) != len(fv.features) {
		return false
	}
	for _, bit := range bits {
		if !fv.IsSet(bit) {
			return false
		}
	}
	return true
}

// Equals determines whether two features vectors contain exactly the same
// features.
func (fv RawFeatureVector) Equals(other *RawFeatureVector) bool {
	if len(fv.features) != len(other.features) {
		return false
	}
	for bit := range fv.features {
		if _, ok := other.features[bit]; !ok {
			return false
		}
	}
	return true
}

// Merges sets all feature bits in other on the receiver's feature vector.
func (fv *RawFeatureVector) Merge(other *RawFeatureVector) error {
	for bit := range other.features {
		err := fv.SafeSet(bit)
		if err != nil {
			return err
		}
	}
	return nil
}

// ValidateUpdate checks whether a feature vector can safely be updated to the
// new feature vector provided, checking that it does not alter any of the
// "standard" features that are defined by LND. The new feature vector should
// be inclusive of all features in the original vector that it still wants to
// advertise, setting and unsetting updates as desired. Features in the vector
// are also checked against a maximum inclusive value, as feature vectors in
// different contexts have different maximum values.
func (fv *RawFeatureVector) ValidateUpdate(other *RawFeatureVector,
	maximumValue FeatureBit) error {

	// Run through the new set of features and check that we're not adding
	// any feature bits that are defined but not set in LND.
	for feature := range other.features {
		if fv.IsSet(feature) {
			continue
		}

		if feature > maximumValue {
			return fmt.Errorf("can't set feature bit %d: %w %v",
				feature, ErrFeatureBitMaximum,
				maximumValue)
		}

		if name, known := Features[feature]; known {
			return fmt.Errorf("can't set feature "+
				"bit %d (%v): %w", feature, name,
				ErrFeatureStandard)
		}
	}

	// Check that the new feature vector for this set does not unset any
	// features that are standard in LND by comparing the features in our
	// current set to the omitted values in the new set.
	for feature := range fv.features {
		if other.IsSet(feature) {
			continue
		}

		if name, known := Features[feature]; known {
			return fmt.Errorf("can't unset feature "+
				"bit %d (%v): %w", feature, name,
				ErrFeatureStandard)
		}
	}

	return nil
}

// ValidatePairs checks each feature bit in a raw vector to ensure that the
// opposing bit is not set, validating that the vector has either the optional
// or required bit set, not both.
func (fv *RawFeatureVector) ValidatePairs() error {
	for feature := range fv.features {
		if _, ok := fv.features[feature^1]; ok {
			return ErrFeaturePairExists
		}
	}

	return nil
}

// Clone makes a copy of a feature vector.
func (fv *RawFeatureVector) Clone() *RawFeatureVector {
	newFeatures := NewRawFeatureVector()
	for bit := range fv.features {
		newFeatures.Set(bit)
	}
	return newFeatures
}

// IsSet returns whether a particular feature bit is enabled in the vector.
func (fv *RawFeatureVector) IsSet(feature FeatureBit) bool {
	_, ok := fv.features[feature]
	return ok
}

// Set marks a feature as enabled in the vector.
func (fv *RawFeatureVector) Set(feature FeatureBit) {
	fv.features[feature] = struct{}{}
}

// SafeSet sets the chosen feature bit in the feature vector, but returns an
// error if the opposing feature bit is already set. This ensures both that we
// are creating properly structured feature vectors, and in some cases, that
// peers are sending properly encoded ones, i.e. it can't be both optional and
// required.
func (fv *RawFeatureVector) SafeSet(feature FeatureBit) error {
	if _, ok := fv.features[feature^1]; ok {
		return ErrFeaturePairExists
	}

	fv.Set(feature)
	return nil
}

// Unset marks a feature as disabled in the vector.
func (fv *RawFeatureVector) Unset(feature FeatureBit) {
	delete(fv.features, feature)
}

// SerializeSize returns the number of bytes needed to represent feature vector
// in byte format.
func (fv *RawFeatureVector) SerializeSize() int {
	// We calculate byte-length via the largest bit index.
	return fv.serializeSize(8)
}

// SerializeSize32 returns the number of bytes needed to represent feature
// vector in base32 format.
func (fv *RawFeatureVector) SerializeSize32() int {
	// We calculate base32-length via the largest bit index.
	return fv.serializeSize(5)
}

// serializeSize returns the number of bytes required to encode the feature
// vector using at most width bits per encoded byte.
func (fv *RawFeatureVector) serializeSize(width int) int {
	// Find the largest feature bit index
	max := -1
	for feature := range fv.features {
		index := int(feature)
		if index > max {
			max = index
		}
	}
	if max == -1 {
		return 0
	}

	return max/width + 1
}

// Encode writes the feature vector in byte representation. Every feature
// encoded as a bit, and the bit vector is serialized using the least number of
// bytes. Since the bit vector length is variable, the first two bytes of the
// serialization represent the length.
func (fv *RawFeatureVector) Encode(w io.Writer) error {
	// Write length of feature vector.
	var l [2]byte
	length := fv.SerializeSize()
	binary.BigEndian.PutUint16(l[:], uint16(length))
	if _, err := w.Write(l[:]); err != nil {
		return err
	}

	return fv.encode(w, length, 8)
}

// EncodeBase256 writes the feature vector in base256 representation. Every
// feature is encoded as a bit, and the bit vector is serialized using the least
// number of bytes.
func (fv *RawFeatureVector) EncodeBase256(w io.Writer) error {
	length := fv.SerializeSize()
	return fv.encode(w, length, 8)
}

// EncodeBase32 writes the feature vector in base32 representation. Every feature
// is encoded as a bit, and the bit vector is serialized using the least number of
// bytes.
func (fv *RawFeatureVector) EncodeBase32(w io.Writer) error {
	length := fv.SerializeSize32()
	return fv.encode(w, length, 5)
}

// encode writes the feature vector
func (fv *RawFeatureVector) encode(w io.Writer, length, width int) error {
	// Generate the data and write it.
	data := make([]byte, length)
	for feature := range fv.features {
		byteIndex := int(feature) / width
		bitIndex := int(feature) % width
		data[length-byteIndex-1] |= 1 << uint(bitIndex)
	}

	_, err := w.Write(data)
	return err
}

// Decode reads the feature vector from its byte representation. Every feature
// is encoded as a bit, and the bit vector is serialized using the least number
// of bytes. Since the bit vector length is variable, the first two bytes of the
// serialization represent the length.
func (fv *RawFeatureVector) Decode(r io.Reader) error {
	// Read the length of the feature vector.
	var l [2]byte
	if _, err := io.ReadFull(r, l[:]); err != nil {
		return err
	}
	length := binary.BigEndian.Uint16(l[:])

	return fv.decode(r, int(length), 8)
}

// DecodeBase256 reads the feature vector from its base256 representation. Every
// feature encoded as a bit, and the bit vector is serialized using the least
// number of bytes.
func (fv *RawFeatureVector) DecodeBase256(r io.Reader, length int) error {
	return fv.decode(r, length, 8)
}

// DecodeBase32 reads the feature vector from its base32 representation. Every
// feature encoded as a bit, and the bit vector is serialized using the least
// number of bytes.
func (fv *RawFeatureVector) DecodeBase32(r io.Reader, length int) error {
	return fv.decode(r, length, 5)
}

// decode reads a feature vector from the next length bytes of the io.Reader,
// assuming each byte has width feature bits encoded per byte.
func (fv *RawFeatureVector) decode(r io.Reader, length, width int) error {
	// Read the feature vector data.
	data := make([]byte, length)
	if _, err := io.ReadFull(r, data); err != nil {
		return err
	}

	// Set feature bits from parsed data.
	bitsNumber := len(data) * width
	for i := 0; i < bitsNumber; i++ {
		byteIndex := int(i / width)
		bitIndex := uint(i % width)
		if (data[length-byteIndex-1]>>bitIndex)&1 == 1 {
			fv.Set(FeatureBit(i))
		}
	}

	return nil
}

// sizeFunc returns the length required to encode the feature vector.
func (fv *RawFeatureVector) sizeFunc() uint64 {
	return uint64(fv.SerializeSize())
}

// Record returns a TLV record that can be used to encode/decode raw feature
// vectors. Note that the length of the feature vector is not included, because
// it is covered by the TLV record's length field.
func (fv *RawFeatureVector) Record() tlv.Record {
	return tlv.MakeDynamicRecord(
		0, fv, fv.sizeFunc, rawFeatureEncoder, rawFeatureDecoder,
	)
}

// rawFeatureEncoder is a custom TLV encoder for raw feature vectors.
func rawFeatureEncoder(w io.Writer, val interface{}, _ *[8]byte) error {
	if v, ok := val.(*RawFeatureVector); ok {
		// Encode the feature bits as a byte slice without its length
		// prepended, as that's already taken care of by the TLV record.
		fv := *v
		return fv.encode(w, fv.SerializeSize(), 8)
	}

	return tlv.NewTypeForEncodingErr(val, "lnwire.RawFeatureVector")
}

// rawFeatureDecoder is a custom TLV decoder for raw feature vectors.
func rawFeatureDecoder(r io.Reader, val interface{}, _ *[8]byte,
	l uint64) error {

	if v, ok := val.(*RawFeatureVector); ok {
		fv := NewRawFeatureVector()
		if err := fv.decode(r, int(l), 8); err != nil {
			return err
		}
		*v = *fv

		return nil
	}

	return tlv.NewTypeForEncodingErr(val, "lnwire.RawFeatureVector")
}

// FeatureVector represents a set of enabled features. The set stores
// information on enabled flags and metadata about the feature names. A feature
// vector is serializable to a compact byte representation that is included in
// Lightning network messages.
type FeatureVector struct {
	*RawFeatureVector
	featureNames map[FeatureBit]string
}

// NewFeatureVector constructs a new FeatureVector from a raw feature vector
// and mapping of feature definitions. If the feature vector argument is nil, a
// new one will be constructed with no enabled features.
func NewFeatureVector(featureVector *RawFeatureVector,
	featureNames map[FeatureBit]string) *FeatureVector {

	if featureVector == nil {
		featureVector = NewRawFeatureVector()
	}
	return &FeatureVector{
		RawFeatureVector: featureVector,
		featureNames:     featureNames,
	}
}

// EmptyFeatureVector returns a feature vector with no bits set.
func EmptyFeatureVector() *FeatureVector {
	return NewFeatureVector(nil, Features)
}

// Record implements the RecordProducer interface for FeatureVector. Note that
// it uses a zero-value type is used to produce the record, as we expect this
// type value to be overwritten when used in generic TLV record production.
// This allows a single Record function to serve in the many different contexts
// in which feature vectors are encoded. This record wraps the encoding/
// decoding for our raw feature vectors so that we can directly parse fully
// formed feature vector types.
func (fv *FeatureVector) Record() tlv.Record {
	return tlv.MakeDynamicRecord(0, fv, fv.sizeFunc,
		func(w io.Writer, val interface{}, buf *[8]byte) error {
			if f, ok := val.(*FeatureVector); ok {
				return rawFeatureEncoder(
					w, f.RawFeatureVector, buf,
				)
			}

			return tlv.NewTypeForEncodingErr(
				val, "*lnwire.FeatureVector",
			)
		},
		func(r io.Reader, val interface{}, buf *[8]byte,
			l uint64) error {

			if f, ok := val.(*FeatureVector); ok {
				features := NewFeatureVector(nil, Features)
				err := rawFeatureDecoder(
					r, features.RawFeatureVector, buf, l,
				)
				if err != nil {
					return err
				}

				*f = *features

				return nil
			}

			return tlv.NewTypeForDecodingErr(
				val, "*lnwire.FeatureVector", l, l,
			)
		},
	)
}

// HasFeature returns whether a particular feature is included in the set. The
// feature can be seen as set either if the bit is set directly OR the queried
// bit has the same meaning as its corresponding even/odd bit, which is set
// instead. The second case is because feature bits are generally assigned in
// pairs where both the even and odd position represent the same feature.
func (fv *FeatureVector) HasFeature(feature FeatureBit) bool {
	return fv.IsSet(feature) ||
		(fv.isFeatureBitPair(feature) && fv.IsSet(feature^1))
}

// RequiresFeature returns true if the referenced feature vector *requires*
// that the given required bit be set. This method can be used with both
// optional and required feature bits as a parameter.
func (fv *FeatureVector) RequiresFeature(feature FeatureBit) bool {
	// If we weren't passed a required feature bit, then we'll flip the
	// lowest bit to query for the required version of the feature. This
	// lets callers pass in both the optional and required bits.
	if !feature.IsRequired() {
		feature ^= 1
	}

	return fv.IsSet(feature)
}

// UnknownRequiredFeatures returns a list of feature bits set in the vector
// that are unknown and in an even bit position. Feature bits with an even
// index must be known to a node receiving the feature vector in a message.
func (fv *FeatureVector) UnknownRequiredFeatures() []FeatureBit {
	var unknown []FeatureBit
	for feature := range fv.features {
		if feature%2 == 0 && !fv.IsKnown(feature) {
			unknown = append(unknown, feature)
		}
	}
	return unknown
}

// UnknownFeatures returns a boolean if a feature vector contains *any*
// unknown features (even if they are odd).
func (fv *FeatureVector) UnknownFeatures() bool {
	for feature := range fv.features {
		if !fv.IsKnown(feature) {
			return true
		}
	}

	return false
}

// Name returns a string identifier for the feature represented by this bit. If
// the bit does not represent a known feature, this returns a string indicating
// as such.
func (fv *FeatureVector) Name(bit FeatureBit) string {
	name, known := fv.featureNames[bit]
	if !known {
		return "unknown"
	}
	return name
}

// IsKnown returns whether this feature bit represents a known feature.
func (fv *FeatureVector) IsKnown(bit FeatureBit) bool {
	_, known := fv.featureNames[bit]
	return known
}

// isFeatureBitPair returns whether this feature bit and its corresponding
// even/odd bit both represent the same feature. This may often be the case as
// bits are generally assigned in pairs, first being assigned an odd bit
// position then being promoted to an even bit position once the network is
// ready.
func (fv *FeatureVector) isFeatureBitPair(bit FeatureBit) bool {
	name1, known1 := fv.featureNames[bit]
	name2, known2 := fv.featureNames[bit^1]
	return known1 && known2 && name1 == name2
}

// Features returns the set of raw features contained in the feature vector.
func (fv *FeatureVector) Features() map[FeatureBit]struct{} {
	fs := make(map[FeatureBit]struct{}, len(fv.RawFeatureVector.features))
	for b := range fv.RawFeatureVector.features {
		fs[b] = struct{}{}
	}
	return fs
}

// Clone copies a feature vector, carrying over its feature bits. The feature
// names are not copied.
func (fv *FeatureVector) Clone() *FeatureVector {
	features := fv.RawFeatureVector.Clone()
	return NewFeatureVector(features, fv.featureNames)
}
