package org.skywalking.apm.toolkit.opentracing;

import io.opentracing.SpanContext;
import io.opentracing.Tracer;
import io.opentracing.propagation.Format;
import io.opentracing.propagation.TextMap;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Map;

/**
 * All source code in SkyWalkingTracer acts like an NoopTracer.
 * Actually, it is NOT.
 * The whole logic will be added after toolkit-activation.
 * <p>
 * Created by wusheng on 2016/12/20.
 */
public class SkyWalkingTracer implements Tracer {
    private static String TRACE_HEAD_NAME = "sw3";

    public static Tracer INSTANCE = new SkyWalkingTracer();


    @Override
    public SpanBuilder buildSpan(String operationName) {
        return new SkyWalkingSpanBuilder(operationName);
    }

    @Override
    public <C> void inject(SpanContext spanContext, Format<C> format, C carrier) {
        if (Format.Builtin.TEXT_MAP.equals(format) || Format.Builtin.HTTP_HEADERS.equals(format)) {
            ((TextMap) carrier).put(TRACE_HEAD_NAME, formatInjectCrossProcessPropagationContextData());
        } else if (Format.Builtin.BINARY.equals(format)) {
            byte[] key = TRACE_HEAD_NAME.getBytes(ByteBufferContext.CHARSET);
            byte[] value = formatInjectCrossProcessPropagationContextData().getBytes(ByteBufferContext.CHARSET);
            ((ByteBuffer) carrier).put(key);
            ((ByteBuffer) carrier).putInt(value.length);
            ((ByteBuffer) carrier).put(value);
        } else {
            throw new IllegalArgumentException("Unsupported format: " + format);
        }
    }

    @Override
    public <C> SpanContext extract(Format<C> format, C carrier) {
        if (Format.Builtin.TEXT_MAP.equals(format) || Format.Builtin.HTTP_HEADERS.equals(format)) {
            TextMap textMapCarrier = (TextMap) carrier;
            formatExtractCrossProcessPropagationContextData(fetchContextData(textMapCarrier));
            return new TextMapContext(textMapCarrier);
        } else if (Format.Builtin.BINARY.equals(format)) {
            ByteBuffer byteBufferCarrier = (ByteBuffer) carrier;
            formatExtractCrossProcessPropagationContextData(fetchContextData(byteBufferCarrier));
            return new ByteBufferContext((ByteBuffer) carrier);
        } else {
            throw new IllegalArgumentException("Unsupported format: " + format);
        }
    }

    /**
     * set context data in toolkit-opentracing-activation
     */
    private String formatInjectCrossProcessPropagationContextData() {
        return "";
    }

    /**
     * read context data in toolkit-opentracing-activation
     */
    private void formatExtractCrossProcessPropagationContextData(String contextData) {
    }

    private String fetchContextData(TextMap textMap) {
        Iterator<Map.Entry<String, String>> iterator = textMap.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            if (TRACE_HEAD_NAME.equals(entry.getKey())) {
                return entry.getValue();
            }
        }

        return null;
    }

    private String fetchContextData(ByteBuffer byteBuffer) {
        String contextDataStr = new String(byteBuffer.array(), Charset.forName("UTF-8"));
        int index = contextDataStr.indexOf(TRACE_HEAD_NAME);
        if (index == -1) {
            return null;
        }

        try {
            byteBuffer.position(index + TRACE_HEAD_NAME.getBytes().length);
            byte[] contextDataBytes = new byte[byteBuffer.getInt()];
            byteBuffer.get(contextDataBytes);
            return new String(contextDataBytes, Charset.forName("UTF-8"));
        } catch (Exception e) {
            return null;
        }
    }
}
