package pubsub

import (
	
	
	

	
	
)

// gossipTracer is an internal tracer that tracks IWANT requests in order to penalize
// peers who don't follow up on IWANT requests after an IHAVE advertisement.
// The tracking of promises is probabilistic to avoid using too much memory.
type gossipTracer struct {
	sync.Mutex

	idGen *msgIDGenerator

	followUpTime time.Duration

	// promises for messages by message ID; for each message tracked, we track the promise
	// expiration time for each peer.
	promises map[string]map[peer.ID]time.Time
	// promises for each peer; for each peer, we track the promised message IDs.
	// this index allows us to quickly void promises when a peer is throttled.
	peerPromises map[peer.ID]map[string]struct{}
}

func newGossipTracer() *gossipTracer {
	return &gossipTracer{
		idGen:        newMsgIdGenerator(),
		promises:     make(map[string]map[peer.ID]time.Time),
		peerPromises: make(map[peer.ID]map[string]struct{}),
	}
}

func ( *gossipTracer) ( *GossipSubRouter) {
	if  == nil {
		return
	}

	.idGen = .p.idGen
	.followUpTime = .params.IWantFollowupTime
}

// track a promise to deliver a message from a list of msgIDs we are requesting
func ( *gossipTracer) ( peer.ID,  []string) {
	if  == nil {
		return
	}

	 := rand.Intn(len())
	 := []

	.Lock()
	defer .Unlock()

	,  := .promises[]
	if ! {
		 = make(map[peer.ID]time.Time)
		.promises[] = 
	}

	_,  = []
	if ! {
		[] = time.Now().Add(.followUpTime)
		,  := .peerPromises[]
		if ! {
			 = make(map[string]struct{})
			.peerPromises[] = 
		}
		[] = struct{}{}
	}
}

// returns the number of broken promises for each peer who didn't follow up
// on an IWANT request.
func ( *gossipTracer) () map[peer.ID]int {
	if  == nil {
		return nil
	}

	.Lock()
	defer .Unlock()

	var  map[peer.ID]int
	 := time.Now()

	// find broken promises from peers
	for ,  := range .promises {
		for ,  := range  {
			if .Before() {
				if  == nil {
					 = make(map[peer.ID]int)
				}
				[]++

				delete(, )

				 := .peerPromises[]
				delete(, )
				if len() == 0 {
					delete(.peerPromises, )
				}
			}
		}

		if len() == 0 {
			delete(.promises, )
		}
	}

	return 
}

var _ RawTracer = (*gossipTracer)(nil)

func ( *gossipTracer) ( *Message) {
	 := .idGen.ID()

	.Lock()
	defer .Unlock()

	,  := .promises[]
	if ! {
		return
	}
	delete(.promises, )

	// delete the promise for all peers that promised it, as they have no way to fulfill it.
	for  := range  {
		,  := .peerPromises[]
		if  {
			delete(, )
			if len() == 0 {
				delete(.peerPromises, )
			}
		}
	}
}

func ( *gossipTracer) ( *Message) {
	// someone delivered a message, fulfill promises for it
	.fulfillPromise()
}

func ( *gossipTracer) ( *Message,  string) {
	// A message got rejected, so we can fulfill promises and let the score penalty apply
	// from invalid message delivery.
	// We do take exception and apply promise penalty regardless in the following cases, where
	// the peer delivered an obviously invalid message.
	switch  {
	case RejectMissingSignature:
		return
	case RejectInvalidSignature:
		return
	}

	.fulfillPromise()
}

func ( *gossipTracer) ( *Message) {
	// we consider the promise fulfilled as soon as the message begins validation
	// if it was a case of signature issue it would have been rejected immediately
	// without triggering the Validate trace
	.fulfillPromise()
}

func ( *gossipTracer) ( peer.ID,  protocol.ID) {}
func ( *gossipTracer) ( peer.ID)                 {}
func ( *gossipTracer) ( string)                    {}
func ( *gossipTracer) ( string)                   {}
func ( *gossipTracer) ( peer.ID,  string)        {}
func ( *gossipTracer) ( peer.ID,  string)        {}
func ( *gossipTracer) ( *Message)        {}
func ( *gossipTracer) ( *RPC)                     {}
func ( *gossipTracer) ( *RPC,  peer.ID)          {}
func ( *gossipTracer) ( *RPC,  peer.ID)          {}
func ( *gossipTracer) ( *Message)    {}

func ( *gossipTracer) ( peer.ID) {
	.Lock()
	defer .Unlock()

	,  := .peerPromises[]
	if ! {
		return
	}

	for  := range  {
		 := .promises[]
		delete(, )
		if len() == 0 {
			delete(.promises, )
		}
	}

	delete(.peerPromises, )
}