/*
 * Copyright (c) 2022-2025 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.
 */

#ifndef _KOALA_PROFILER_
#define _KOALA_PROFILER_

#include <stdint.h>
#include <stdio.h>

#include <unordered_map>
#include <string>
#include <vector>
#include <chrono>
#include <algorithm>

constexpr double PERCENTAGE_FACTOR = 100.0;

struct InteropProfilerRecord {
    int64_t time;
    int64_t count;
    InteropProfilerRecord(int64_t time, int64_t count) : time(time), count(count) {}
};

class InteropProfiler {
  private:
    std::unordered_map<std::string, InteropProfilerRecord> records;
    static InteropProfiler* _instance;
    InteropProfiler() {}

  public:
    static InteropProfiler* instance() {
        if (!_instance) _instance = new InteropProfiler();
        return _instance;
    }

    void record(const char* name, int64_t ns) {
        auto it = records.find(name);
        if (it == records.end()) {
            records.insert({name, InteropProfilerRecord(ns, 1)});
        } else {
            it->second.time += ns;
            it->second.count++;
        }
    }

    std::string report() {
        std::vector<std::pair<std::string, InteropProfilerRecord>> elems(records.begin(), records.end());
        std::sort(elems.begin(), elems.end(),
            [](const std::pair<std::string, InteropProfilerRecord>&a, const std::pair<std::string, InteropProfilerRecord>&b) {
                return b.second.time < a.second.time;
            });
        int64_t total = 0;
        std::for_each(elems.begin(), elems.end(), [&total](const std::pair<std::string, InteropProfilerRecord>&a) {
            total += a.second.time;
        });
        std::string result;
        std::for_each(elems.begin(), elems.end(), [total, &result](const std::pair<std::string, InteropProfilerRecord>&a) {
            auto ns = a.second.time;
            auto count = a.second.count;
            char buffer[1024];
#ifdef __STDC_LIB_EXT1__ 
            errno_t res = snprintf_s(buffer, sizeof buffer, "for %s[%lld]: %.01f%% (%lld)\n", a.first.c_str(),
                (long long)count, (double)ns / total * PERCENTAGE_FACTOR, (long long)ns);
            if (res != EOK) {
                return "";
            }
#else
            snprintf(buffer, sizeof buffer, "for %s[%lld]: %.01f%% (%lld)\n", a.first.c_str(),
                (long long)count, (double)ns / total * PERCENTAGE_FACTOR, (long long)ns);
#endif
            result += buffer;
        });
        return result;
    }

    void reset() {
        records.clear();
    }
};


class InteropMethodCall {
  private:
    const char* name;
    std::chrono::steady_clock::time_point begin;
  public:
    InteropMethodCall(const char* name) : name(name) {
        begin = std::chrono::steady_clock::now();
    }
    ~InteropMethodCall() {
        auto end = std::chrono::steady_clock::now();
        int64_t ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
        InteropProfiler::instance()->record(name, ns);
    }
};

#endif // _KOALA_PROFILER_