// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"

import (
	

	tracepb 

	
	
	
	tracesdk 
	
)

// Spans transforms a slice of OpenTelemetry spans into a slice of OTLP
// ResourceSpans.
func ( []tracesdk.ReadOnlySpan) []*tracepb.ResourceSpans {
	if len() == 0 {
		return nil
	}

	 := make(map[attribute.Distinct]*tracepb.ResourceSpans)

	type  struct {
		  attribute.Distinct
		 instrumentation.Scope
	}
	 := make(map[]*tracepb.ScopeSpans)

	var  int
	for ,  := range  {
		if  == nil {
			continue
		}

		 := .Resource().Equivalent()
		 := {
			:  ,
			: .InstrumentationScope(),
		}
		,  := []
		if ! {
			// Either the resource or instrumentation scope were unknown.
			 = &tracepb.ScopeSpans{
				Scope:     InstrumentationScope(.InstrumentationScope()),
				Spans:     []*tracepb.Span{},
				SchemaUrl: .InstrumentationScope().SchemaURL,
			}
		}
		.Spans = append(.Spans, span())
		[] = 

		,  := []
		if ! {
			++
			// The resource was unknown.
			 = &tracepb.ResourceSpans{
				Resource:   Resource(.Resource()),
				ScopeSpans: []*tracepb.ScopeSpans{},
				SchemaUrl:  .Resource().SchemaURL(),
			}
			[] = 
			continue
		}

		// The resource has been seen before. Check if the instrumentation
		// library lookup was unknown because if so we need to add it to the
		// ResourceSpans. Otherwise, the instrumentation library has already
		// been seen and the append we did above will be included it in the
		// ScopeSpans reference.
		if ! {
			.ScopeSpans = append(.ScopeSpans, )
		}
	}

	// Transform the categorized map into a slice
	 := make([]*tracepb.ResourceSpans, 0, )
	for ,  := range  {
		 = append(, )
	}
	return 
}

// span transforms a Span into an OTLP span.
func span( tracesdk.ReadOnlySpan) *tracepb.Span {
	if  == nil {
		return nil
	}

	 := .SpanContext().TraceID()
	 := .SpanContext().SpanID()

	 := &tracepb.Span{
		TraceId:                [:],
		SpanId:                 [:],
		TraceState:             .SpanContext().TraceState().String(),
		Status:                 status(.Status().Code, .Status().Description),
		StartTimeUnixNano:      uint64(max(0, .StartTime().UnixNano())), // nolint:gosec // Overflow checked.
		EndTimeUnixNano:        uint64(max(0, .EndTime().UnixNano())),   // nolint:gosec // Overflow checked.
		Links:                  links(.Links()),
		Kind:                   spanKind(.SpanKind()),
		Name:                   .Name(),
		Attributes:             KeyValues(.Attributes()),
		Events:                 spanEvents(.Events()),
		DroppedAttributesCount: clampUint32(.DroppedAttributes()),
		DroppedEventsCount:     clampUint32(.DroppedEvents()),
		DroppedLinksCount:      clampUint32(.DroppedLinks()),
	}

	if  := .Parent().SpanID(); .IsValid() {
		.ParentSpanId = [:]
	}
	.Flags = buildSpanFlags(.Parent())

	return 
}

func clampUint32( int) uint32 {
	if  < 0 {
		return 0
	}
	if int64() > math.MaxUint32 {
		return math.MaxUint32
	}
	return uint32() // nolint: gosec  // Overflow/Underflow checked.
}

// status transform a span code and message into an OTLP span status.
func status( codes.Code,  string) *tracepb.Status {
	var  tracepb.Status_StatusCode
	switch  {
	case codes.Ok:
		 = tracepb.Status_STATUS_CODE_OK
	case codes.Error:
		 = tracepb.Status_STATUS_CODE_ERROR
	default:
		 = tracepb.Status_STATUS_CODE_UNSET
	}
	return &tracepb.Status{
		Code:    ,
		Message: ,
	}
}

// links transforms span Links to OTLP span links.
func links( []tracesdk.Link) []*tracepb.Span_Link {
	if len() == 0 {
		return nil
	}

	 := make([]*tracepb.Span_Link, 0, len())
	for ,  := range  {
		// This redefinition is necessary to prevent otLink.*ID[:] copies
		// being reused -- in short we need a new otLink per iteration.

		 := .SpanContext.TraceID()
		 := .SpanContext.SpanID()

		 := buildSpanFlags(.SpanContext)

		 = append(, &tracepb.Span_Link{
			TraceId:                [:],
			SpanId:                 [:],
			Attributes:             KeyValues(.Attributes),
			DroppedAttributesCount: clampUint32(.DroppedAttributeCount),
			Flags:                  ,
		})
	}
	return 
}

func buildSpanFlags( trace.SpanContext) uint32 {
	 := tracepb.SpanFlags_SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK
	if .IsRemote() {
		 |= tracepb.SpanFlags_SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK
	}

	return uint32() // nolint:gosec // Flags is a bitmask and can't be negative
}

// spanEvents transforms span Events to an OTLP span events.
func spanEvents( []tracesdk.Event) []*tracepb.Span_Event {
	if len() == 0 {
		return nil
	}

	 := make([]*tracepb.Span_Event, len())
	// Transform message events
	for  := range  {
		[] = &tracepb.Span_Event{
			Name:                   [].Name,
			TimeUnixNano:           uint64(max(0, [].Time.UnixNano())), // nolint:gosec // Overflow checked.
			Attributes:             KeyValues([].Attributes),
			DroppedAttributesCount: clampUint32([].DroppedAttributeCount),
		}
	}
	return 
}

// spanKind transforms a SpanKind to an OTLP span kind.
func spanKind( trace.SpanKind) tracepb.Span_SpanKind {
	switch  {
	case trace.SpanKindInternal:
		return tracepb.Span_SPAN_KIND_INTERNAL
	case trace.SpanKindClient:
		return tracepb.Span_SPAN_KIND_CLIENT
	case trace.SpanKindServer:
		return tracepb.Span_SPAN_KIND_SERVER
	case trace.SpanKindProducer:
		return tracepb.Span_SPAN_KIND_PRODUCER
	case trace.SpanKindConsumer:
		return tracepb.Span_SPAN_KIND_CONSUMER
	default:
		return tracepb.Span_SPAN_KIND_UNSPECIFIED
	}
}