package rcmgr

import (
	
	
	
	
	
	
	
	
	

	
)

type trace struct {
	path string

	ctx    context.Context
	cancel func()
	wg     sync.WaitGroup

	mx            sync.Mutex
	done          bool
	pendingWrites []interface{}
	reporters     []TraceReporter
}

type TraceReporter interface {
	// ConsumeEvent consumes a trace event. This is called synchronously,
	// implementations should process the event quickly.
	ConsumeEvent(TraceEvt)
}

func ( string) Option {
	return func( *resourceManager) error {
		if .trace == nil {
			.trace = &trace{path: }
		} else {
			.trace.path = 
		}
		return nil
	}
}

func ( TraceReporter) Option {
	return func( *resourceManager) error {
		if .trace == nil {
			.trace = &trace{}
		}
		.trace.reporters = append(.trace.reporters, )
		return nil
	}
}

type TraceEvtTyp string

const (
	TraceStartEvt              TraceEvtTyp = "start"
	TraceCreateScopeEvt        TraceEvtTyp = "create_scope"
	TraceDestroyScopeEvt       TraceEvtTyp = "destroy_scope"
	TraceReserveMemoryEvt      TraceEvtTyp = "reserve_memory"
	TraceBlockReserveMemoryEvt TraceEvtTyp = "block_reserve_memory"
	TraceReleaseMemoryEvt      TraceEvtTyp = "release_memory"
	TraceAddStreamEvt          TraceEvtTyp = "add_stream"
	TraceBlockAddStreamEvt     TraceEvtTyp = "block_add_stream"
	TraceRemoveStreamEvt       TraceEvtTyp = "remove_stream"
	TraceAddConnEvt            TraceEvtTyp = "add_conn"
	TraceBlockAddConnEvt       TraceEvtTyp = "block_add_conn"
	TraceRemoveConnEvt         TraceEvtTyp = "remove_conn"
)

type scopeClass struct {
	name string
}

func ( scopeClass) () ([]byte, error) {
	 := .name
	var  string
	if  := strings.Index(, "span:");  > -1 {
		 = [:-1]
		 = [+5:]
	}
	// System and Transient scope
	if  == "system" ||  == "transient" ||  == "allowlistedSystem" ||  == "allowlistedTransient" {
		return json.Marshal(struct {
			 string
			  string `json:",omitempty"`
		}{
			: ,
			:  ,
		})
	}
	// Connection scope
	if strings.HasPrefix(, "conn-") {
		return json.Marshal(struct {
			 string
			  string
			  string `json:",omitempty"`
		}{
			: "conn",
			:  [5:],
			:  ,
		})
	}
	// Stream scope
	if strings.HasPrefix(, "stream-") {
		return json.Marshal(struct {
			  string
			 string
			   string `json:",omitempty"`
		}{
			:  "stream",
			: [7:],
			:   ,
		})
	}
	// Peer scope
	if strings.HasPrefix(, "peer:") {
		return json.Marshal(struct {
			 string
			  string
			  string `json:",omitempty"`
		}{
			: "peer",
			:  [5:],
			:  ,
		})
	}

	if strings.HasPrefix(, "service:") {
		if  := strings.Index(, "peer:");  > 0 { // Peer-Service scope
			return json.Marshal(struct {
				   string
				 string
				    string
				    string `json:",omitempty"`
			}{
				:   "service-peer",
				: [8 : -1],
				:    [+5:],
				:    ,
			})
		} else { // Service scope
			return json.Marshal(struct {
				   string
				 string
				    string `json:",omitempty"`
			}{
				:   "service",
				: [8:],
				:    ,
			})
		}
	}

	if strings.HasPrefix(, "protocol:") {
		if  := strings.Index(, "peer:");  > -1 { // Peer-Protocol scope
			return json.Marshal(struct {
				    string
				 string
				     string
				     string `json:",omitempty"`
			}{
				:    "protocol-peer",
				: [9 : -1],
				:     [+5:],
				:     ,
			})
		} else { // Protocol scope
			return json.Marshal(struct {
				    string
				 string
				     string `json:",omitempty"`
			}{
				:    "protocol",
				: [9:],
				:     ,
			})
		}
	}

	return nil, fmt.Errorf("unrecognized scope: %s", )
}

type TraceEvt struct {
	Time string
	Type TraceEvtTyp

	Scope *scopeClass `json:",omitempty"`
	Name  string      `json:",omitempty"`

	Limit interface{} `json:",omitempty"`

	Priority uint8 `json:",omitempty"`

	Delta    int64 `json:",omitempty"`
	DeltaIn  int   `json:",omitempty"`
	DeltaOut int   `json:",omitempty"`

	Memory int64 `json:",omitempty"`

	StreamsIn  int `json:",omitempty"`
	StreamsOut int `json:",omitempty"`

	ConnsIn  int `json:",omitempty"`
	ConnsOut int `json:",omitempty"`

	FD int `json:",omitempty"`
}

func ( *trace) ( TraceEvt) {
	.mx.Lock()
	defer .mx.Unlock()

	if .done {
		return
	}
	.Time = time.Now().Format(time.RFC3339Nano)
	if .Name != "" {
		.Scope = &scopeClass{name: .Name}
	}

	for ,  := range .reporters {
		.ConsumeEvent()
	}

	if .path != "" {
		.pendingWrites = append(.pendingWrites, )
	}
}

func ( *trace) ( io.WriteCloser) {
	defer .wg.Done()
	defer .Close()

	 := gzip.NewWriter()
	defer .Close()

	 := json.NewEncoder()

	 := time.NewTicker(time.Second)
	defer .Stop()

	var  []interface{}

	 := func() {
		.mx.Lock()
		 := .pendingWrites
		.pendingWrites = [:0]
		 = 
		.mx.Unlock()
	}

	for {
		select {
		case <-.C:
			()

			if len() == 0 {
				continue
			}

			if  := .writeEvents(, );  != nil {
				log.Warnf("error writing rcmgr trace: %s", )
				.mx.Lock()
				.done = true
				.mx.Unlock()
				return
			}

			if  := .Flush();  != nil {
				log.Warnf("error flushing rcmgr trace: %s", )
				.mx.Lock()
				.done = true
				.mx.Unlock()
				return
			}

		case <-.ctx.Done():
			()

			if len() == 0 {
				return
			}

			if  := .writeEvents(, );  != nil {
				log.Warnf("error writing rcmgr trace: %s", )
				return
			}

			if  := .Flush();  != nil {
				log.Warnf("error flushing rcmgr trace: %s", )
			}

			return
		}
	}
}

func ( *trace) ( []interface{},  *json.Encoder) error {
	for ,  := range  {
		if  := .Encode();  != nil {
			return 
		}
	}

	return nil
}

func ( *trace) ( Limiter) error {
	if  == nil {
		return nil
	}

	.ctx, .cancel = context.WithCancel(context.Background())

	if .path != "" {
		,  := os.OpenFile(.path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
		if  != nil {
			return nil
		}

		.wg.Add(1)
		go .backgroundWriter()
	}

	.push(TraceEvt{
		Type:  TraceStartEvt,
		Limit: ,
	})

	return nil
}

func ( *trace) () error {
	if  == nil {
		return nil
	}

	.mx.Lock()

	if .done {
		.mx.Unlock()
		return nil
	}

	.cancel()
	.done = true
	.mx.Unlock()

	.wg.Wait()
	return nil
}

func ( *trace) ( string,  Limit) {
	if  == nil {
		return
	}

	.push(TraceEvt{
		Type:  TraceCreateScopeEvt,
		Name:  ,
		Limit: ,
	})
}

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

	.push(TraceEvt{
		Type: TraceDestroyScopeEvt,
		Name: ,
	})
}

func ( *trace) ( string,  uint8, ,  int64) {
	if  == nil {
		return
	}

	if  == 0 {
		return
	}

	.push(TraceEvt{
		Type:     TraceReserveMemoryEvt,
		Name:     ,
		Priority: ,
		Delta:    ,
		Memory:   ,
	})
}

func ( *trace) ( string,  uint8, ,  int64) {
	if  == nil {
		return
	}

	if  == 0 {
		return
	}

	.push(TraceEvt{
		Type:     TraceBlockReserveMemoryEvt,
		Name:     ,
		Priority: ,
		Delta:    ,
		Memory:   ,
	})
}

func ( *trace) ( string, ,  int64) {
	if  == nil {
		return
	}

	if  == 0 {
		return
	}

	.push(TraceEvt{
		Type:   TraceReleaseMemoryEvt,
		Name:   ,
		Delta:  -,
		Memory: ,
	})
}

func ( *trace) ( string,  network.Direction, ,  int) {
	if  == nil {
		return
	}

	var ,  int
	if  == network.DirInbound {
		 = 1
	} else {
		 = 1
	}

	.push(TraceEvt{
		Type:       TraceAddStreamEvt,
		Name:       ,
		DeltaIn:    ,
		DeltaOut:   ,
		StreamsIn:  ,
		StreamsOut: ,
	})
}

func ( *trace) ( string,  network.Direction, ,  int) {
	if  == nil {
		return
	}

	var ,  int
	if  == network.DirInbound {
		 = 1
	} else {
		 = 1
	}

	.push(TraceEvt{
		Type:       TraceBlockAddStreamEvt,
		Name:       ,
		DeltaIn:    ,
		DeltaOut:   ,
		StreamsIn:  ,
		StreamsOut: ,
	})
}

func ( *trace) ( string,  network.Direction, ,  int) {
	if  == nil {
		return
	}

	var ,  int
	if  == network.DirInbound {
		 = -1
	} else {
		 = -1
	}

	.push(TraceEvt{
		Type:       TraceRemoveStreamEvt,
		Name:       ,
		DeltaIn:    ,
		DeltaOut:   ,
		StreamsIn:  ,
		StreamsOut: ,
	})
}

func ( *trace) ( string, , , ,  int) {
	if  == nil {
		return
	}

	if  == 0 &&  == 0 {
		return
	}

	.push(TraceEvt{
		Type:       TraceAddStreamEvt,
		Name:       ,
		DeltaIn:    ,
		DeltaOut:   ,
		StreamsIn:  ,
		StreamsOut: ,
	})
}

func ( *trace) ( string, , , ,  int) {
	if  == nil {
		return
	}

	if  == 0 &&  == 0 {
		return
	}

	.push(TraceEvt{
		Type:       TraceBlockAddStreamEvt,
		Name:       ,
		DeltaIn:    ,
		DeltaOut:   ,
		StreamsIn:  ,
		StreamsOut: ,
	})
}

func ( *trace) ( string, , , ,  int) {
	if  == nil {
		return
	}

	if  == 0 &&  == 0 {
		return
	}

	.push(TraceEvt{
		Type:       TraceRemoveStreamEvt,
		Name:       ,
		DeltaIn:    -,
		DeltaOut:   -,
		StreamsIn:  ,
		StreamsOut: ,
	})
}

func ( *trace) ( string,  network.Direction,  bool, , ,  int) {
	if  == nil {
		return
	}

	var , ,  int
	if  == network.DirInbound {
		 = 1
	} else {
		 = 1
	}
	if  {
		 = 1
	}

	.push(TraceEvt{
		Type:     TraceAddConnEvt,
		Name:     ,
		DeltaIn:  ,
		DeltaOut: ,
		Delta:    int64(),
		ConnsIn:  ,
		ConnsOut: ,
		FD:       ,
	})
}

func ( *trace) ( string,  network.Direction,  bool, , ,  int) {
	if  == nil {
		return
	}

	var , ,  int
	if  == network.DirInbound {
		 = 1
	} else {
		 = 1
	}
	if  {
		 = 1
	}

	.push(TraceEvt{
		Type:     TraceBlockAddConnEvt,
		Name:     ,
		DeltaIn:  ,
		DeltaOut: ,
		Delta:    int64(),
		ConnsIn:  ,
		ConnsOut: ,
		FD:       ,
	})
}

func ( *trace) ( string,  network.Direction,  bool, , ,  int) {
	if  == nil {
		return
	}

	var , ,  int
	if  == network.DirInbound {
		 = -1
	} else {
		 = -1
	}
	if  {
		 = -1
	}

	.push(TraceEvt{
		Type:     TraceRemoveConnEvt,
		Name:     ,
		DeltaIn:  ,
		DeltaOut: ,
		Delta:    int64(),
		ConnsIn:  ,
		ConnsOut: ,
		FD:       ,
	})
}

func ( *trace) ( string, , , , , ,  int) {
	if  == nil {
		return
	}

	if  == 0 &&  == 0 &&  == 0 {
		return
	}

	.push(TraceEvt{
		Type:     TraceAddConnEvt,
		Name:     ,
		DeltaIn:  ,
		DeltaOut: ,
		Delta:    int64(),
		ConnsIn:  ,
		ConnsOut: ,
		FD:       ,
	})
}

func ( *trace) ( string, , , , , ,  int) {
	if  == nil {
		return
	}

	if  == 0 &&  == 0 &&  == 0 {
		return
	}

	.push(TraceEvt{
		Type:     TraceBlockAddConnEvt,
		Name:     ,
		DeltaIn:  ,
		DeltaOut: ,
		Delta:    int64(),
		ConnsIn:  ,
		ConnsOut: ,
		FD:       ,
	})
}

func ( *trace) ( string, , , , , ,  int) {
	if  == nil {
		return
	}

	if  == 0 &&  == 0 &&  == 0 {
		return
	}

	.push(TraceEvt{
		Type:     TraceRemoveConnEvt,
		Name:     ,
		DeltaIn:  -,
		DeltaOut: -,
		Delta:    -int64(),
		ConnsIn:  ,
		ConnsOut: ,
		FD:       ,
	})
}