package pubsub

import (
	

	
	

	pb 
)

// EventTracer is a generic event tracer interface.
// This is a high level tracing interface which delivers tracing events, as defined by the protobuf
// schema in pb/trace.proto.
type EventTracer interface {
	Trace(evt *pb.TraceEvent)
}

// RawTracer is a low level tracing interface that allows an application to trace the internal
// operation of the pubsub subsystem.
//
// Note that the tracers are invoked synchronously, which means that application tracers must
// take care to not block or modify arguments.
//
// Warning: this interface is not fixed, we may be adding new methods as necessitated by the system
// in the future.
type RawTracer interface {
	// AddPeer is invoked when a new peer is added.
	AddPeer(p peer.ID, proto protocol.ID)
	// RemovePeer is invoked when a peer is removed.
	RemovePeer(p peer.ID)
	// Join is invoked when a new topic is joined
	Join(topic string)
	// Leave is invoked when a topic is abandoned
	Leave(topic string)
	// Graft is invoked when a new peer is grafted on the mesh (gossipsub)
	Graft(p peer.ID, topic string)
	// Prune is invoked when a peer is pruned from the message (gossipsub)
	Prune(p peer.ID, topic string)
	// ValidateMessage is invoked when a message first enters the validation pipeline.
	ValidateMessage(msg *Message)
	// DeliverMessage is invoked when a message is delivered
	DeliverMessage(msg *Message)
	// RejectMessage is invoked when a message is Rejected or Ignored.
	// The reason argument can be one of the named strings Reject*.
	RejectMessage(msg *Message, reason string)
	// DuplicateMessage is invoked when a duplicate message is dropped.
	DuplicateMessage(msg *Message)
	// ThrottlePeer is invoked when a peer is throttled by the peer gater.
	ThrottlePeer(p peer.ID)
	// RecvRPC is invoked when an incoming RPC is received.
	RecvRPC(rpc *RPC)
	// SendRPC is invoked when a RPC is sent.
	SendRPC(rpc *RPC, p peer.ID)
	// DropRPC is invoked when an outbound RPC is dropped, typically because of a queue full.
	DropRPC(rpc *RPC, p peer.ID)
	// UndeliverableMessage is invoked when the consumer of Subscribe is not reading messages fast enough and
	// the pressure release mechanism trigger, dropping messages.
	UndeliverableMessage(msg *Message)
}

// pubsub tracer details
type pubsubTracer struct {
	tracer EventTracer
	raw    []RawTracer
	pid    peer.ID
	idGen  *msgIDGenerator
}

func ( *pubsubTracer) ( *Message) {
	if  == nil {
		return
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_PUBLISH_MESSAGE.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		PublishMessage: &pb.TraceEvent_PublishMessage{
			MessageID: []byte(.idGen.ID()),
			Topic:     .Message.Topic,
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( *Message) {
	if  == nil {
		return
	}

	if .ReceivedFrom != .pid {
		for ,  := range .raw {
			.ValidateMessage()
		}
	}
}

func ( *pubsubTracer) ( *Message,  string) {
	if  == nil {
		return
	}

	if .ReceivedFrom != .pid {
		for ,  := range .raw {
			.RejectMessage(, )
		}
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_REJECT_MESSAGE.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		RejectMessage: &pb.TraceEvent_RejectMessage{
			MessageID:    []byte(.idGen.ID()),
			ReceivedFrom: []byte(.ReceivedFrom),
			Reason:       &,
			Topic:        .Topic,
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( *Message) {
	if  == nil {
		return
	}

	if .ReceivedFrom != .pid {
		for ,  := range .raw {
			.DuplicateMessage()
		}
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_DUPLICATE_MESSAGE.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		DuplicateMessage: &pb.TraceEvent_DuplicateMessage{
			MessageID:    []byte(.idGen.ID()),
			ReceivedFrom: []byte(.ReceivedFrom),
			Topic:        .Topic,
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( *Message) {
	if  == nil {
		return
	}

	if .ReceivedFrom != .pid {
		for ,  := range .raw {
			.DeliverMessage()
		}
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_DELIVER_MESSAGE.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		DeliverMessage: &pb.TraceEvent_DeliverMessage{
			MessageID:    []byte(.idGen.ID()),
			Topic:        .Topic,
			ReceivedFrom: []byte(.ReceivedFrom),
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( peer.ID,  protocol.ID) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.AddPeer(, )
	}

	if .tracer == nil {
		return
	}

	 := string()
	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_ADD_PEER.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		AddPeer: &pb.TraceEvent_AddPeer{
			PeerID: []byte(),
			Proto:  &,
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( peer.ID) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.RemovePeer()
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_REMOVE_PEER.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		RemovePeer: &pb.TraceEvent_RemovePeer{
			PeerID: []byte(),
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( *RPC) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.RecvRPC()
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_RECV_RPC.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		RecvRPC: &pb.TraceEvent_RecvRPC{
			ReceivedFrom: []byte(.from),
			Meta:         .traceRPCMeta(),
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( *RPC,  peer.ID) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.SendRPC(, )
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_SEND_RPC.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		SendRPC: &pb.TraceEvent_SendRPC{
			SendTo: []byte(),
			Meta:   .traceRPCMeta(),
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( *RPC,  peer.ID) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.DropRPC(, )
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_DROP_RPC.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		DropRPC: &pb.TraceEvent_DropRPC{
			SendTo: []byte(),
			Meta:   .traceRPCMeta(),
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( *Message) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.UndeliverableMessage()
	}
}

func ( *pubsubTracer) ( *RPC) *pb.TraceEvent_RPCMeta {
	 := new(pb.TraceEvent_RPCMeta)

	var  []*pb.TraceEvent_MessageMeta
	for ,  := range .Publish {
		 = append(, &pb.TraceEvent_MessageMeta{
			MessageID: []byte(.idGen.RawID()),
			Topic:     .Topic,
		})
	}
	.Messages = 

	var  []*pb.TraceEvent_SubMeta
	for ,  := range .Subscriptions {
		 = append(, &pb.TraceEvent_SubMeta{
			Subscribe: .Subscribe,
			Topic:     .Topicid,
		})
	}
	.Subscription = 

	if .Control != nil {
		var  []*pb.TraceEvent_ControlIHaveMeta
		for ,  := range .Control.Ihave {
			var  [][]byte
			for ,  := range .MessageIDs {
				 = append(, []byte())
			}
			 = append(, &pb.TraceEvent_ControlIHaveMeta{
				Topic:      .TopicID,
				MessageIDs: ,
			})
		}

		var  []*pb.TraceEvent_ControlIWantMeta
		for ,  := range .Control.Iwant {
			var  [][]byte
			for ,  := range .MessageIDs {
				 = append(, []byte())
			}
			 = append(, &pb.TraceEvent_ControlIWantMeta{
				MessageIDs: ,
			})
		}

		var  []*pb.TraceEvent_ControlGraftMeta
		for ,  := range .Control.Graft {
			 = append(, &pb.TraceEvent_ControlGraftMeta{
				Topic: .TopicID,
			})
		}

		var  []*pb.TraceEvent_ControlPruneMeta
		for ,  := range .Control.Prune {
			 := make([][]byte, 0, len(.Peers))
			for ,  := range .Peers {
				 = append(, .PeerID)
			}
			 = append(, &pb.TraceEvent_ControlPruneMeta{
				Topic: .TopicID,
				Peers: ,
			})
		}

		var  []*pb.TraceEvent_ControlIDontWantMeta
		for ,  := range .Control.Idontwant {
			var  [][]byte
			for ,  := range .MessageIDs {
				 = append(, []byte())
			}
			 = append(, &pb.TraceEvent_ControlIDontWantMeta{
				MessageIDs: ,
			})
		}

		.Control = &pb.TraceEvent_ControlMeta{
			Ihave:     ,
			Iwant:     ,
			Graft:     ,
			Prune:     ,
			Idontwant: ,
		}
	}

	return 
}

func ( *pubsubTracer) ( string) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.Join()
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_JOIN.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		Join: &pb.TraceEvent_Join{
			Topic: &,
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( string) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.Leave()
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_LEAVE.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		Leave: &pb.TraceEvent_Leave{
			Topic: &,
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( peer.ID,  string) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.Graft(, )
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_GRAFT.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		Graft: &pb.TraceEvent_Graft{
			PeerID: []byte(),
			Topic:  &,
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( peer.ID,  string) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.Prune(, )
	}

	if .tracer == nil {
		return
	}

	 := time.Now().UnixNano()
	 := &pb.TraceEvent{
		Type:      pb.TraceEvent_PRUNE.Enum(),
		PeerID:    []byte(.pid),
		Timestamp: &,
		Prune: &pb.TraceEvent_Prune{
			PeerID: []byte(),
			Topic:  &,
		},
	}

	.tracer.Trace()
}

func ( *pubsubTracer) ( peer.ID) {
	if  == nil {
		return
	}

	for ,  := range .raw {
		.ThrottlePeer()
	}
}