/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed 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.
 */

#include "runtime/timing.h"

#include <iomanip>

#include "utils/logger.h"

namespace panda {

constexpr uint64_t NS_PER_SECOND = 1000000000;
constexpr uint64_t NS_PER_MILLISECOND = 1000000;
constexpr uint64_t NS_PER_MICROSECOND = 1000;

PandaString Timing::PrettyTimeNs(uint64_t duration)
{
    uint64_t time_uint;
    PandaString time_uint_name;
    uint64_t main_part;
    uint64_t fractional_part;
    if (duration > NS_PER_SECOND) {
        time_uint = NS_PER_SECOND;
        main_part = duration / time_uint;
        fractional_part = duration % time_uint / NS_PER_MILLISECOND;
        time_uint_name = "s";
    } else if (duration > NS_PER_MILLISECOND) {
        time_uint = NS_PER_MILLISECOND;
        main_part = duration / time_uint;
        fractional_part = duration % time_uint / NS_PER_MICROSECOND;
        time_uint_name = "ms";
    } else {
        time_uint = NS_PER_MICROSECOND;
        main_part = duration / time_uint;
        fractional_part = duration % time_uint;
        time_uint_name = "us";
    }
    PandaStringStream ss;
    constexpr size_t FRACTION_WIDTH = 3U;
    ss << main_part << "." << std::setfill('0') << std::setw(FRACTION_WIDTH) << fractional_part << time_uint_name;
    return ss.str();
}

void Timing::Process()
{
    PandaStack<PandaVector<TimeLabel>::iterator> label_stack;
    // NOLINTNEXTLINE(modernize-loop-convert)
    for (auto it = labels_.begin(); it != labels_.end(); it++) {
        if (it->GetType() == TimeLabelType::BEGIN) {
            label_stack.push(it);
            continue;
        }
        auto begin_it = label_stack.top();
        label_stack.pop();
        uint64_t duration = it->GetTime() - begin_it->GetTime();
        uint64_t cpu_duration = it->GetCPUTime() - begin_it->GetCPUTime();
        begin_it->SetTime(duration);
        begin_it->SetCPUTime(cpu_duration);
    }
}

PandaString Timing::Dump()
{
    Process();
    PandaStringStream ss;
    std::string indent = "    ";
    size_t indent_count = 0;
    for (auto &label : labels_) {
        if (label.GetType() == TimeLabelType::BEGIN) {
            for (size_t i = 0; i < indent_count; i++) {
                ss << indent;
            }
            ss << label.GetName() << " " << PrettyTimeNs(label.GetCPUTime()) << "/" << PrettyTimeNs(label.GetTime())
               << std::endl;
            indent_count++;
            continue;
        }
        if (indent_count > 0U) {
            indent_count--;
        }
    }
    return ss.str();
}

}  // namespace panda