package org.skywalking.apm.agent.core.context;

import org.skywalking.apm.trace.Span;
import org.skywalking.apm.trace.TraceSegment;

/**
 * LeafSpan is a special type of {@link Span}
 *
 * In rpc-client tracing scenario, one component could constitute by many other rpc-client.
 * e.g Feign constitutes by okhttp, apache httpclient, etc.
 *
 * By having leaf concept, no need so many spans for single rpc call.
 *
 * @author wusheng
 */
public class LeafSpan extends Span {
    private int stackDepth = 0;

    /**
     * Create a new span, by given span id and give startTime but no parent span id,
     * No parent span id means that, this Span is the first span of the {@link TraceSegment}
     *
     * @param spanId given by the creator, and must be unique id in the {@link TraceSegment}
     * @param operationName {@link #operationName}
     * @param startTime given start time of span
     */
    public LeafSpan(int spanId, String operationName, long startTime) {
        super(spanId, -1, operationName, startTime);
    }

    /**
     * Create a new span, by given span id, parent span, operationName and startTime.
     * This span must belong a {@link TraceSegment}, also is a part of Distributed Trace.
     *
     * @param spanId given by the creator, and must be unique id in the {@link TraceSegment}
     * @param parentSpan {@link Span}
     * @param operationName {@link #operationName}
     * @param startTime given start timestamp
     */
    public LeafSpan(int spanId, Span parentSpan, String operationName, long startTime) {
        super(spanId, parentSpan.getSpanId(), operationName, startTime);
    }

    void push() {
        stackDepth++;
    }

    void pop() {
        stackDepth--;
    }

    boolean isFinished() {
        return stackDepth == 0;
    }

    @Override
    public boolean isLeaf() {
        return true;
    }

    private boolean isInOwnerContext() {
        return stackDepth == 1;
    }

    /**
     * Sets the string name for the logical operation this span represents,
     * only when this is in context of the leaf span owner.
     *
     * @return this Span instance, for chaining
     */
    @Override
    public Span setOperationName(String operationName) {
        if (isInOwnerContext()) {
            super.setOperationName(operationName);
        }
        return this;
    }

    /**
     * Set a key:value tag on the Span,
     * only when this is in context of the leaf span owner.
     *
     * @return this Span instance, for chaining
     */
    @Override
    public final Span setTag(String key, String value) {
        if (isInOwnerContext()) {
            super.setTag(key, value);
        }
        return this;
    }

    @Override
    public final Span setTag(String key, boolean value) {
        if (isInOwnerContext()) {
            super.setTag(key, value);
        }
        return this;
    }

    @Override
    public final Span setTag(String key, Integer value) {
        if (isInOwnerContext()) {
            super.setTag(key, value);
        }
        return this;
    }
}
