/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.rocketmq.dashboard.util;

import java.util.ArrayList;
import java.util.List;

import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.trace.TraceBean;
import org.apache.rocketmq.client.trace.TraceConstants;
import org.apache.rocketmq.client.trace.TraceContext;
import org.apache.rocketmq.client.trace.TraceType;
import org.apache.rocketmq.common.message.MessageType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.rocketmq.client.trace.TraceType.Pub;

public class MsgTraceDecodeUtil {
    private final static Logger log = LoggerFactory.getLogger(MsgTraceDecodeUtil.class);

    private static final int TRACE_MSG_PUB_V1_LEN = 12;
    private static final int TRACE_MSG_PUB_V2_LEN = 13;
    private static final int TRACE_MSG_PUB_V3_LEN = 14;
    private static final int TRACE_MSG_PUB_V4_LEN = 15;

    private static final int TRACE_MSG_SUBAFTER_V1_LEN = 6;
    private static final int TRACE_MSG_SUBAFTER_V2_LEN = 7;
    private static final int TRACE_MSG_SUBAFTER_V3_LEN = 9;

    public static List<TraceContext> decoderFromTraceDataString(String traceData) {
        List<TraceContext> resList = new ArrayList<TraceContext>();
        if (traceData == null || traceData.length() <= 0) {
            return resList;
        }
        String[] contextList = traceData.split(String.valueOf(TraceConstants.FIELD_SPLITOR));
        for (String context : contextList) {
            String[] line = context.split(String.valueOf(TraceConstants.CONTENT_SPLITOR));
            if (line[0].equals(Pub.name())) {
                TraceContext pubContext = initTraceContext();
                pubContext.setTraceType(Pub);
                pubContext.setTimeStamp(Long.parseLong(line[1]));
                pubContext.setRegionId(line[2]);
                pubContext.setGroupName(line[3]);
                TraceBean bean = new TraceBean();
                bean.setTopic(line[4]);
                bean.setMsgId(line[5]);
                bean.setTags(line[6]);
                bean.setKeys(line[7]);
                bean.setStoreHost(line[8]);
                bean.setBodyLength(Integer.parseInt(line[9]));
                pubContext.setCostTime(Integer.parseInt(line[10]));
                bean.setMsgType(MessageType.values()[Integer.parseInt(line[11])]);
                // compatible with different version
                switch (line.length) {
                    case TRACE_MSG_PUB_V1_LEN:
                        break;
                    case TRACE_MSG_PUB_V2_LEN:
                        pubContext.setSuccess(Boolean.parseBoolean(line[12]));
                        break;
                    case TRACE_MSG_PUB_V3_LEN:
                        bean.setOffsetMsgId(line[12]);
                        pubContext.setSuccess(Boolean.parseBoolean(line[13]));
                        break;
                    case TRACE_MSG_PUB_V4_LEN:
                        bean.setOffsetMsgId(line[12]);
                        pubContext.setSuccess(Boolean.parseBoolean(line[13]));
                        bean.setClientHost(line[14]);
                        break;
                    default:
                        bean.setOffsetMsgId(line[12]);
                        pubContext.setSuccess(Boolean.parseBoolean(line[13]));
                        bean.setClientHost(line[14]);
                        log.warn("Detect new version trace msg of {} type", Pub.name());
                        break;
                }

                pubContext.setTraceBeans(new ArrayList<TraceBean>(1));
                pubContext.getTraceBeans().add(bean);
                resList.add(pubContext);
            } else if (line[0].equals(TraceType.SubBefore.name())) {
                TraceContext subBeforeContext = initTraceContext();
                subBeforeContext.setTraceType(TraceType.SubBefore);
                subBeforeContext.setTimeStamp(Long.parseLong(line[1]));
                subBeforeContext.setRegionId(line[2]);
                subBeforeContext.setGroupName(line[3]);
                subBeforeContext.setRequestId(line[4]);
                TraceBean bean = new TraceBean();
                bean.setMsgId(line[5]);
                bean.setRetryTimes(Integer.parseInt(line[6]));
                bean.setKeys(line[7]);
                subBeforeContext.setTraceBeans(new ArrayList<TraceBean>(1));
                subBeforeContext.getTraceBeans().add(bean);
                resList.add(subBeforeContext);
            } else if (line[0].equals(TraceType.SubAfter.name())) {
                TraceContext subAfterContext = initTraceContext();
                subAfterContext.setTraceType(TraceType.SubAfter);
                subAfterContext.setRequestId(line[1]);
                TraceBean bean = new TraceBean();
                bean.setMsgId(line[2]);
                bean.setKeys(line[5]);
                subAfterContext.setTraceBeans(new ArrayList<TraceBean>(1));
                subAfterContext.getTraceBeans().add(bean);
                subAfterContext.setCostTime(Integer.parseInt(line[3]));
                subAfterContext.setSuccess(Boolean.parseBoolean(line[4]));
                // compatible with different version
                switch (line.length) {
                    case TRACE_MSG_SUBAFTER_V1_LEN:
                        break;
                    case TRACE_MSG_SUBAFTER_V2_LEN:
                        subAfterContext.setContextCode(Integer.parseInt(line[6]));
                        break;
                    case TRACE_MSG_SUBAFTER_V3_LEN:
                        subAfterContext.setContextCode(Integer.parseInt(line[6]));
                        subAfterContext.setTimeStamp(Long.parseLong(line[7]));
                        subAfterContext.setGroupName(line[8]);
                        break;
                    default:
                        subAfterContext.setContextCode(Integer.parseInt(line[6]));
                        subAfterContext.setTimeStamp(Long.parseLong(line[7]));
                        subAfterContext.setGroupName(line[8]);
                        log.warn("Detect new version trace msg of {} type", TraceType.SubAfter.name());
                        break;
                }
                resList.add(subAfterContext);
            } else if (line[0].equals(TraceType.EndTransaction.name())) {
                TraceContext endTransactionContext = initTraceContext();
                endTransactionContext.setTraceType(TraceType.EndTransaction);
                endTransactionContext.setTimeStamp(Long.parseLong(line[1]));
                endTransactionContext.setRegionId(line[2]);
                endTransactionContext.setGroupName(line[3]);
                TraceBean bean = new TraceBean();
                bean.setTopic(line[4]);
                bean.setMsgId(line[5]);
                bean.setTags(line[6]);
                bean.setKeys(line[7]);
                bean.setStoreHost(line[8]);
                bean.setMsgType(MessageType.values()[Integer.parseInt(line[9])]);
                bean.setTransactionId(line[10]);
                bean.setTransactionState(LocalTransactionState.valueOf(line[11]));
                bean.setFromTransactionCheck(Boolean.parseBoolean(line[12]));
                endTransactionContext.setTraceBeans(new ArrayList<TraceBean>(1));
                endTransactionContext.getTraceBeans().add(bean);
                resList.add(endTransactionContext);
            }
        }
        return resList;
    }

    private static TraceContext initTraceContext() {
        TraceContext traceContext = new TraceContext();
        traceContext.setTimeStamp(0L);
        traceContext.setCostTime(-1);
        traceContext.setRequestId(null);
        return traceContext;
    }
}
