package handshake

import (
	
	
	
	
	

	
)

const (
	tokenPrefixIP byte = iota
	tokenPrefixString
)

// A Token is derived from the client address and can be used to verify the ownership of this address.
type Token struct {
	IsRetryToken      bool
	SentTime          time.Time
	encodedRemoteAddr []byte
	// only set for tokens sent in NEW_TOKEN frames
	RTT time.Duration
	// only set for retry tokens
	OriginalDestConnectionID protocol.ConnectionID
	RetrySrcConnectionID     protocol.ConnectionID
}

// ValidateRemoteAddr validates the address, but does not check expiration
func ( *Token) ( net.Addr) bool {
	return bytes.Equal(encodeRemoteAddr(), .encodedRemoteAddr)
}

// token is the struct that is used for ASN1 serialization and deserialization
type token struct {
	IsRetryToken             bool
	RemoteAddr               []byte
	Timestamp                int64
	RTT                      int64 // in mus
	OriginalDestConnectionID []byte
	RetrySrcConnectionID     []byte
}

// A TokenGenerator generates tokens
type TokenGenerator struct {
	tokenProtector tokenProtector
}

// NewTokenGenerator initializes a new TokenGenerator
func ( TokenProtectorKey) *TokenGenerator {
	return &TokenGenerator{tokenProtector: *newTokenProtector()}
}

// NewRetryToken generates a new token for a Retry for a given source address
func ( *TokenGenerator) (
	 net.Addr,
	 protocol.ConnectionID,
	 protocol.ConnectionID,
) ([]byte, error) {
	,  := asn1.Marshal(token{
		IsRetryToken:             true,
		RemoteAddr:               encodeRemoteAddr(),
		OriginalDestConnectionID: .Bytes(),
		RetrySrcConnectionID:     .Bytes(),
		Timestamp:                time.Now().UnixNano(),
	})
	if  != nil {
		return nil, 
	}
	return .tokenProtector.NewToken()
}

// NewToken generates a new token to be sent in a NEW_TOKEN frame
func ( *TokenGenerator) ( net.Addr,  time.Duration) ([]byte, error) {
	,  := asn1.Marshal(token{
		RemoteAddr: encodeRemoteAddr(),
		Timestamp:  time.Now().UnixNano(),
		RTT:        .Microseconds(),
	})
	if  != nil {
		return nil, 
	}
	return .tokenProtector.NewToken()
}

// DecodeToken decodes a token
func ( *TokenGenerator) ( []byte) (*Token, error) {
	// if the client didn't send any token, DecodeToken will be called with a nil-slice
	if len() == 0 {
		return nil, nil
	}

	,  := .tokenProtector.DecodeToken()
	if  != nil {
		return nil, 
	}
	 := &token{}
	,  := asn1.Unmarshal(, )
	if  != nil {
		return nil, 
	}
	if len() != 0 {
		return nil, fmt.Errorf("rest when unpacking token: %d", len())
	}
	 := &Token{
		IsRetryToken:      .IsRetryToken,
		SentTime:          time.Unix(0, .Timestamp),
		encodedRemoteAddr: .RemoteAddr,
	}
	if .IsRetryToken {
		.OriginalDestConnectionID = protocol.ParseConnectionID(.OriginalDestConnectionID)
		.RetrySrcConnectionID = protocol.ParseConnectionID(.RetrySrcConnectionID)
	} else {
		.RTT = time.Duration(.RTT) * time.Microsecond
	}
	return , nil
}

// encodeRemoteAddr encodes a remote address such that it can be saved in the token
func encodeRemoteAddr( net.Addr) []byte {
	if ,  := .(*net.UDPAddr);  {
		return append([]byte{tokenPrefixIP}, .IP...)
	}
	return append([]byte{tokenPrefixString}, []byte(.String())...)
}