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

package trace // import "go.opentelemetry.io/otel/sdk/trace"

import (
	
	

	
	
	
	
	
)

type tracer struct {
	embedded.Tracer

	provider             *TracerProvider
	instrumentationScope instrumentation.Scope

	selfObservabilityEnabled bool
	spanLiveMetric           otelconv.SDKSpanLive
	spanStartedMetric        otelconv.SDKSpanStarted
}

var _ trace.Tracer = &tracer{}

// Start starts a Span and returns it along with a context containing it.
//
// The Span is created with the provided name and as a child of any existing
// span context found in the passed context. The created Span will be
// configured appropriately by any SpanOption passed.
func ( *tracer) (
	 context.Context,
	 string,
	 ...trace.SpanStartOption,
) (context.Context, trace.Span) {
	 := trace.NewSpanStartConfig(...)

	if  == nil {
		// Prevent trace.ContextWithSpan from panicking.
		 = context.Background()
	}

	// For local spans created by this SDK, track child span count.
	if  := trace.SpanFromContext();  != nil {
		if ,  := .(*recordingSpan);  {
			.addChild()
		}
	}

	 := .newSpan(, , &)
	 := trace.ContextWithSpan(, )
	if .selfObservabilityEnabled {
		 := trace.SpanContextFromContext()
		 := spanStartedSet(, )
		.spanStartedMetric.AddSet(, 1, )
	}

	if ,  := .(ReadWriteSpan);  && .IsRecording() {
		 := .provider.getSpanProcessors()
		for ,  := range  {
			// Use original context.
			.sp.OnStart(, )
		}
	}
	if ,  := .(runtimeTracer);  {
		 = .runtimeTrace()
	}

	return , 
}

type runtimeTracer interface {
	// runtimeTrace starts a "runtime/trace".Task for the span and
	// returns a context containing the task.
	runtimeTrace(ctx context.Context) context.Context
}

// newSpan returns a new configured span.
func ( *tracer) ( context.Context,  string,  *trace.SpanConfig) trace.Span {
	// If told explicitly to make this a new root use a zero value SpanContext
	// as a parent which contains an invalid trace ID and is not remote.
	var  trace.SpanContext
	if .NewRoot() {
		 = trace.ContextWithSpanContext(, )
	} else {
		 = trace.SpanContextFromContext()
	}

	// If there is a valid parent trace ID, use it to ensure the continuity of
	// the trace. Always generate a new span ID so other components can rely
	// on a unique span ID, even if the Span is non-recording.
	var  trace.TraceID
	var  trace.SpanID
	if !.TraceID().IsValid() {
		,  = .provider.idGenerator.NewIDs()
	} else {
		 = .TraceID()
		 = .provider.idGenerator.NewSpanID(, )
	}

	 := .provider.sampler.ShouldSample(SamplingParameters{
		ParentContext: ,
		TraceID:       ,
		Name:          ,
		Kind:          .SpanKind(),
		Attributes:    .Attributes(),
		Links:         .Links(),
	})

	 := trace.SpanContextConfig{
		TraceID:    ,
		SpanID:     ,
		TraceState: .Tracestate,
	}
	if isSampled() {
		.TraceFlags = .TraceFlags() | trace.FlagsSampled
	} else {
		.TraceFlags = .TraceFlags() &^ trace.FlagsSampled
	}
	 := trace.NewSpanContext()

	if !isRecording() {
		return .newNonRecordingSpan()
	}
	return .newRecordingSpan(, , , , , )
}

// newRecordingSpan returns a new configured recordingSpan.
func ( *tracer) (
	 context.Context,
	,  trace.SpanContext,
	 string,
	 SamplingResult,
	 *trace.SpanConfig,
) *recordingSpan {
	 := .Timestamp()
	if .IsZero() {
		 = time.Now()
	}

	 := &recordingSpan{
		// Do not pre-allocate the attributes slice here! Doing so will
		// allocate memory that is likely never going to be used, or if used,
		// will be over-sized. The default Go compiler has been tested to
		// dynamically allocate needed space very well. Benchmarking has shown
		// it to be more performant than what we can predetermine here,
		// especially for the common use case of few to no added
		// attributes.

		parent:      ,
		spanContext: ,
		spanKind:    trace.ValidateSpanKind(.SpanKind()),
		name:        ,
		startTime:   ,
		events:      newEvictedQueueEvent(.provider.spanLimits.EventCountLimit),
		links:       newEvictedQueueLink(.provider.spanLimits.LinkCountLimit),
		tracer:      ,
	}

	for ,  := range .Links() {
		.AddLink()
	}

	.SetAttributes(.Attributes...)
	.SetAttributes(.Attributes()...)

	if .selfObservabilityEnabled {
		// Propagate any existing values from the context with the new span to
		// the measurement context.
		 = trace.ContextWithSpan(, )
		 := spanLiveSet(.spanContext.IsSampled())
		.spanLiveMetric.AddSet(, 1, )
	}

	return 
}

// newNonRecordingSpan returns a new configured nonRecordingSpan.
func ( *tracer) ( trace.SpanContext) nonRecordingSpan {
	return nonRecordingSpan{tracer: , sc: }
}

type parentState int

const (
	parentStateNoParent parentState = iota
	parentStateLocalParent
	parentStateRemoteParent
)

type samplingState int

const (
	samplingStateDrop samplingState = iota
	samplingStateRecordOnly
	samplingStateRecordAndSample
)

type spanStartedSetKey struct {
	parent   parentState
	sampling samplingState
}

var spanStartedSetCache = map[spanStartedSetKey]attribute.Set{
	{parentStateNoParent, samplingStateDrop}: attribute.NewSet(
		otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
		otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
	),
	{parentStateLocalParent, samplingStateDrop}: attribute.NewSet(
		otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
		otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
	),
	{parentStateRemoteParent, samplingStateDrop}: attribute.NewSet(
		otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
		otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultDrop),
	),

	{parentStateNoParent, samplingStateRecordOnly}: attribute.NewSet(
		otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
		otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
	),
	{parentStateLocalParent, samplingStateRecordOnly}: attribute.NewSet(
		otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
		otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
	),
	{parentStateRemoteParent, samplingStateRecordOnly}: attribute.NewSet(
		otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
		otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordOnly),
	),

	{parentStateNoParent, samplingStateRecordAndSample}: attribute.NewSet(
		otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginNone),
		otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
	),
	{parentStateLocalParent, samplingStateRecordAndSample}: attribute.NewSet(
		otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginLocal),
		otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
	),
	{parentStateRemoteParent, samplingStateRecordAndSample}: attribute.NewSet(
		otelconv.SDKSpanStarted{}.AttrSpanParentOrigin(otelconv.SpanParentOriginRemote),
		otelconv.SDKSpanStarted{}.AttrSpanSamplingResult(otelconv.SpanSamplingResultRecordAndSample),
	),
}

func spanStartedSet( trace.SpanContext,  trace.Span) attribute.Set {
	 := spanStartedSetKey{
		parent:   parentStateNoParent,
		sampling: samplingStateDrop,
	}

	if .IsValid() {
		if .IsRemote() {
			.parent = parentStateRemoteParent
		} else {
			.parent = parentStateLocalParent
		}
	}

	if .IsRecording() {
		if .SpanContext().IsSampled() {
			.sampling = samplingStateRecordAndSample
		} else {
			.sampling = samplingStateRecordOnly
		}
	}

	return spanStartedSetCache[]
}

type spanLiveSetKey struct {
	sampled bool
}

var spanLiveSetCache = map[spanLiveSetKey]attribute.Set{
	{true}: attribute.NewSet(
		otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
			otelconv.SpanSamplingResultRecordAndSample,
		),
	),
	{false}: attribute.NewSet(
		otelconv.SDKSpanLive{}.AttrSpanSamplingResult(
			otelconv.SpanSamplingResultRecordOnly,
		),
	),
}

func spanLiveSet( bool) attribute.Set {
	 := spanLiveSetKey{sampled: }
	return spanLiveSetCache[]
}