package routing

import (
	
	

	
)

// QueryEventType indicates the query event's type.
type QueryEventType int

// Number of events to buffer.
var QueryEventBufferSize = 16

const (
	// Sending a query to a peer.
	SendingQuery QueryEventType = iota
	// Got a response from a peer.
	PeerResponse
	// Found a "closest" peer (not currently used).
	FinalPeer
	// Got an error when querying.
	QueryError
	// Found a provider.
	Provider
	// Found a value.
	Value
	// Adding a peer to the query.
	AddingPeer
	// Dialing a peer.
	DialingPeer
)

// QueryEvent is emitted for every notable event that happens during a DHT query.
type QueryEvent struct {
	ID        peer.ID
	Type      QueryEventType
	Responses []*peer.AddrInfo
	Extra     string
}

type routingQueryKey struct{}
type eventChannel struct {
	mu  sync.Mutex
	ctx context.Context
	ch  chan<- *QueryEvent
}

// waitThenClose is spawned in a goroutine when the channel is registered. This
// safely cleans up the channel when the context has been canceled.
func ( *eventChannel) () {
	<-.ctx.Done()
	.mu.Lock()
	close(.ch)
	// 1. Signals that we're done.
	// 2. Frees memory (in case we end up hanging on to this for a while).
	.ch = nil
	.mu.Unlock()
}

// send sends an event on the event channel, aborting if either the passed or
// the internal context expire.
func ( *eventChannel) ( context.Context,  *QueryEvent) {
	.mu.Lock()
	// Closed.
	if .ch == nil {
		.mu.Unlock()
		return
	}
	// in case the passed context is unrelated, wait on both.
	select {
	case .ch <- :
	case <-.ctx.Done():
	case <-.Done():
	}
	.mu.Unlock()
}

// RegisterForQueryEvents registers a query event channel with the given
// context. The returned context can be passed to DHT queries to receive query
// events on the returned channels.
//
// The passed context MUST be canceled when the caller is no longer interested
// in query events.
func ( context.Context) (context.Context, <-chan *QueryEvent) {
	 := make(chan *QueryEvent, QueryEventBufferSize)
	 := &eventChannel{ch: , ctx: }
	go .waitThenClose()
	return context.WithValue(, routingQueryKey{}, ), 
}

// PublishQueryEvent publishes a query event to the query event channel
// associated with the given context, if any.
func ( context.Context,  *QueryEvent) {
	 := .Value(routingQueryKey{})
	if  == nil {
		return
	}

	// We *want* to panic here.
	 := .(*eventChannel)
	.send(, )
}

// SubscribesToQueryEvents returns true if the context subscribes to query
// events. If this function returns false, calling `PublishQueryEvent` on the
// context will be a no-op.
func ( context.Context) bool {
	return .Value(routingQueryKey{}) != nil
}