/*
 * Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef ROCM_SMI_ROCM_SMI_DYN_GPU_METRICS_H_
#define ROCM_SMI_ROCM_SMI_DYN_GPU_METRICS_H_

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <shared_mutex>
#include <stdexcept>
#include <string>
#include <thread>
#include <unordered_map>
#include <variant>
#include <vector>

#include "rocm_smi/rocm_smi.h"
#include "rocm_smi/rocm_smi_common.h"

namespace amd::smi {

/*
 *  NOTES:
 *
 *  For the new dynamic metrics implementation, we need to define a `schema`.
 *  The `schema` defines the `types` of the `attributes` (or `properties`, much like a syntax) it
 *  defines the type of data that can be stored in an attribute. It acts as a blueprint.
 *
 *  If we think of the metrics system as a database, the schema is like the table structure.
 *  It defines the fields (attributes) that can be stored, their types, and any constraints on them.
 *  This allows for a flexible and extensible system where new metrics can be added without
 *  needing to change the underlying codebase significantly.
 *
 */

namespace details {
/*
 *  NOTE:
 *  Namespace for internal details of 'dynamic gpu metrics'.
 *  This namespace contains implementation details that are not intended for public use.
 *  It is used to encapsulate the internal workings of the dynamic GPU metrics system.
 *  This allows for better organization of code and separation of concerns.
 *  The public API will interact with this namespace, but the details will be hidden from the user.
 *  This is a common practice in C++ to keep the public interface clean and maintainable.
 *
 *  Guidelines for using namespace details:
 *  -----------------------------------------
 *    * Use namespace details in headers for:
 *      - Implementation-specific types (used in templates or PImpl).
 *      - SFINAE helpers, type traits, or metaprogramming utilities.
 *      - Internal functions needed for templates/inline functions.
 *      - Internal constants or enums that are not part of the public API.
 *      - Internal classes or structs that are not meant for public use.
 *
 *    * Use namespace details in implementation files for:
 *      - Helper functions/constants not meant for public use.
 *      - Internal state management (e.g., PImpl details).
 *    * Avoid exposing namespace details in documentation or public API.
 *
 * This improves encapsulation and prevents users from relying on internal details that may change.
 *
 */

/*
 * Data types for the attributes
 */
enum class AMDGpuMetricAttributeType_t {
  TYPE_UINT8,
  TYPE_INT8,
  TYPE_UINT16,
  TYPE_INT16,
  TYPE_UINT32,
  TYPE_INT32,
  TYPE_UINT64,
  TYPE_INT64
};

/*
 * Attribute IDs for the GPU metrics
 */
enum class AMDGpuMetricAttributeId_t {
  TEMPERATURE_HOTSPOT,
  TEMPERATURE_MEM,
  TEMPERATURE_VRSOC,
  CURR_SOCKET_POWER,
  AVERAGE_GFX_ACTIVITY,
  AVERAGE_UMC_ACTIVITY,
  MEM_MAX_BANDWIDTH,
  ENERGY_ACCUMULATOR,
  SYSTEM_CLOCK_COUNTER,
  ACCUMULATION_COUNTER,
  PROCHOT_RESIDENCY_ACC,
  PPT_RESIDENCY_ACC,
  SOCKET_THM_RESIDENCY_ACC,
  VR_THM_RESIDENCY_ACC,
  HBM_THM_RESIDENCY_ACC,
  GFXCLK_LOCK_STATUS,
  PCIE_LINK_WIDTH,
  PCIE_LINK_SPEED,
  XGMI_LINK_WIDTH,
  XGMI_LINK_SPEED,
  GFX_ACTIVITY_ACC,
  MEM_ACTIVITY_ACC,
  PCIE_BANDWIDTH_ACC,
  PCIE_BANDWIDTH_INST,
  PCIE_L0_TO_RECOV_COUNT_ACC,
  PCIE_REPLAY_COUNT_ACC,
  PCIE_REPLAY_ROVER_COUNT_ACC,
  PCIE_NAK_SENT_COUNT_ACC,
  PCIE_NAK_RCVD_COUNT_ACC,
  XGMI_READ_DATA_ACC,
  XGMI_WRITE_DATA_ACC,
  XGMI_LINK_STATUS,
  FIRMWARE_TIMESTAMP,
  CURRENT_GFXCLK,
  CURRENT_SOCCLK,
  CURRENT_VCLK0,
  CURRENT_DCLK0,
  CURRENT_UCLK,
  NUM_PARTITION,
  PCIE_LC_PERF_OTHER_END_RECOVERY,
  GFX_BUSY_INST,
  JPEG_BUSY,
  VCN_BUSY,
  GFX_BUSY_ACC,
  GFX_BELOW_HOST_LIMIT_PPT_ACC,
  GFX_BELOW_HOST_LIMIT_THM_ACC,
  GFX_LOW_UTILIZATION_ACC,
  GFX_BELOW_HOST_LIMIT_TOTAL_ACC,
  TEMPERATURE_HBM,
  TEMPERATURE_MID,
  TEMPERATURE_AID,
  TEMPERATURE_XCD,
  LABEL_VERSION,
  NODE_ID,
  NODE_TEMP_RETIMER,
  NODE_TEMP_IBC,
  NODE_TEMP_IBC_2,
  NODE_TEMP_VDD18_VR,
  NODE_TEMP_04_HBM_B_VR,
  NODE_TEMP_04_HBM_D_VR,
  VR_TEMP_VDDCR_SOCIO_A,
  VR_TEMP_VDDCR_SOCIO_C,
  VR_TEMP_VDDCR_X0,
  VR_TEMP_VDDCR_X1,
  VR_TEMP_VDDIO_HBM_B,
  VR_TEMP_VDDIO_HBM_D,
  VR_TEMP_VDDIO_04_HBM_B,
  VR_TEMP_VDDIO_04_HBM_D,
  VR_TEMP_VDDCR_HBM_B,
  VR_TEMP_VDDCR_HBM_D,
  VR_TEMP_VDDCR_075_HBM_B,
  VR_TEMP_VDDCR_075_HBM_D,
  VR_TEMP_VDDIO_11_GTA_A,
  VR_TEMP_VDDIO_11_GTA_C,
  VR_TEMP_VDDAN_075_GTA_A,
  VR_TEMP_VDDAN_075_GTA_C,
  VR_TEMP_VDDCR_075_UCIE,
  VR_TEMP_VDDIO_065_UCIEAA,
  VR_TEMP_VDDIO_065_UCIEAM_A,
  VR_TEMP_VDDIO_065_UCIEAM_C,
  VR_TEMP_VDDAN_075,
  SYSTEM_TEMP_UBB_FPGA,
  SYSTEM_TEMP_UBB_FRONT,
  SYSTEM_TEMP_UBB_BACK,
  SYSTEM_TEMP_UBB_OAM7,
  SYSTEM_TEMP_UBB_IBC,
  SYSTEM_TEMP_UBB_UFPGA,
  SYSTEM_TEMP_UBB_OAM1,
  SYSTEM_TEMP_OAM_0_1_HSC,
  SYSTEM_TEMP_OAM_2_3_HSC,
  SYSTEM_TEMP_OAM_4_5_HSC,
  SYSTEM_TEMP_OAM_6_7_HSC,
  SYSTEM_TEMP_UBB_FPGA_0V72_VR,
  SYSTEM_TEMP_UBB_FPGA_3V3_VR,
  SYSTEM_TEMP_RETIMER_0_1_2_3_1V2_VR,
  SYSTEM_TEMP_RETIMER_4_5_6_7_1V2_VR,
  SYSTEM_TEMP_RETIMER_0_1_0V9_VR,
  SYSTEM_TEMP_RETIMER_4_5_0V9_VR,
  SYSTEM_TEMP_RETIMER_2_3_0V9_VR,
  SYSTEM_TEMP_RETIMER_6_7_0V9_VR,
  SYSTEM_TEMP_OAM_0_1_2_3_3V3_VR,
  SYSTEM_TEMP_OAM_4_5_6_7_3V3_VR,
  SYSTEM_TEMP_IBC_HSC,
  SYSTEM_TEMP_IBC,
  MAX,
};

struct AMDGpuDynamicTranslationTextInfo_t {
 public:
  std::string m_short_info;
  std::string m_long_info;

 private:
};
using AMDGpuMetricAttributeIdTranslationTable_t =
    std::unordered_map<AMDGpuMetricAttributeId_t, AMDGpuDynamicTranslationTextInfo_t>;

static const auto AMDGpuMetricAttributeIdToString = AMDGpuMetricAttributeIdTranslationTable_t{
    {AMDGpuMetricAttributeId_t::TEMPERATURE_HOTSPOT,
     {"TEMPERATURE_HOTSPOT", "Temperature of the GPU hotspot"}},
    {AMDGpuMetricAttributeId_t::TEMPERATURE_MEM,
     {"TEMPERATURE_MEM", "Temperature of the GPU memory"}},
    {AMDGpuMetricAttributeId_t::TEMPERATURE_VRSOC,
     {"TEMPERATURE_VRSOC", "Temperature of the VR SOC"}},
    {AMDGpuMetricAttributeId_t::CURR_SOCKET_POWER,
     {"CURR_SOCKET_POWER", "Current power consumption of the socket"}},
    {AMDGpuMetricAttributeId_t::AVERAGE_GFX_ACTIVITY,
     {"AVERAGE_GFX_ACTIVITY", "Average GPU activity percentage"}},
    {AMDGpuMetricAttributeId_t::AVERAGE_UMC_ACTIVITY,
     {"AVERAGE_UMC_ACTIVITY", "Average UMC activity percentage"}},
    {AMDGpuMetricAttributeId_t::MEM_MAX_BANDWIDTH,
     {"MEM_MAX_BANDWIDTH", "Maximum memory bandwidth in GB/s"}},
    {AMDGpuMetricAttributeId_t::ENERGY_ACCUMULATOR,
     {"ENERGY_ACCUMULATOR", "Energy consumed in Joules"}},
    {AMDGpuMetricAttributeId_t::SYSTEM_CLOCK_COUNTER,
     {"SYSTEM_CLOCK_COUNTER", "System clock counter in nanoseconds"}},
    {AMDGpuMetricAttributeId_t::ACCUMULATION_COUNTER,
     {"ACCUMULATION_COUNTER", "Counter for accumulated metrics"}},
    {AMDGpuMetricAttributeId_t::PROCHOT_RESIDENCY_ACC,
     {"PROCHOT_RESIDENCY_ACC", "Accumulator for 'Processor Hot' residency time"}},
    {AMDGpuMetricAttributeId_t::PPT_RESIDENCY_ACC,
     {"PPT_RESIDENCY_ACC", "Accumulator for 'Package Power Tracking' residency time"}},
    {AMDGpuMetricAttributeId_t::SOCKET_THM_RESIDENCY_ACC,
     {"SOCKET_THM_RESIDENCY_ACC", "Accumulator for socket thermal residency time"}},
    {AMDGpuMetricAttributeId_t::VR_THM_RESIDENCY_ACC,
     {"VR_THM_RESIDENCY_ACC", "Accumulator for 'Voltage Regulator' thermal residency time"}},
    {AMDGpuMetricAttributeId_t::HBM_THM_RESIDENCY_ACC,
     {"HBM_THM_RESIDENCY_ACC", "Accumulator for 'High Bandwidth Memory' thermal residency time"}},
    {AMDGpuMetricAttributeId_t::GFXCLK_LOCK_STATUS,
     {"GFXCLK_LOCK_STATUS", "Status of GFX clock lock"}},
    {AMDGpuMetricAttributeId_t::PCIE_LINK_WIDTH, {"PCIE_LINK_WIDTH", "Width of the PCIe link"}},
    {AMDGpuMetricAttributeId_t::PCIE_LINK_SPEED, {"PCIE_LINK_SPEED", "Speed of the PCIe link"}},
    {AMDGpuMetricAttributeId_t::XGMI_LINK_WIDTH, {"XGMI_LINK_WIDTH", "Width of the XGMI link"}},
    {AMDGpuMetricAttributeId_t::XGMI_LINK_SPEED, {"XGMI_LINK_SPEED", "Speed of the XGMI link"}},
    {AMDGpuMetricAttributeId_t::GFX_ACTIVITY_ACC,
     {"GFX_ACTIVITY_ACC", "Accumulator for GFX activity"}},
    {AMDGpuMetricAttributeId_t::MEM_ACTIVITY_ACC,
     {"MEM_ACTIVITY_ACC", "Accumulator for memory activity"}},
    {AMDGpuMetricAttributeId_t::PCIE_BANDWIDTH_ACC,
     {"PCIE_BANDWIDTH_ACC", "Accumulator for PCIe bandwidth"}},
    {AMDGpuMetricAttributeId_t::PCIE_BANDWIDTH_INST,
     {"PCIE_BANDWIDTH_INST", "Instantaneous PCIe bandwidth"}},
    {AMDGpuMetricAttributeId_t::PCIE_L0_TO_RECOV_COUNT_ACC,
     {"PCIE_L0_TO_RECOV_COUNT_ACC", "Accumulator for PCIe L0 to recovery count"}},
    {AMDGpuMetricAttributeId_t::PCIE_REPLAY_COUNT_ACC,
     {"PCIE_REPLAY_COUNT_ACC", "Accumulator for PCIe replay count"}},
    {AMDGpuMetricAttributeId_t::PCIE_REPLAY_ROVER_COUNT_ACC,
     {"PCIE_REPLAY_ROVER_COUNT_ACC", "Accumulator for PCIe replay rover count"}},
    {AMDGpuMetricAttributeId_t::PCIE_NAK_SENT_COUNT_ACC,
     {"PCIE_NAK_SENT_COUNT_ACC", "Accumulator for PCIe NAK sent count"}},
    {AMDGpuMetricAttributeId_t::PCIE_NAK_RCVD_COUNT_ACC,
     {"PCIE_NAK_RCVD_COUNT_ACC", "Accumulator for PCIe NAK received count"}},
    {AMDGpuMetricAttributeId_t::XGMI_READ_DATA_ACC,
     {"XGMI_READ_DATA_ACC", "Accumulator for XGMI read data"}},
    {AMDGpuMetricAttributeId_t::XGMI_WRITE_DATA_ACC,
     {"XGMI_WRITE_DATA_ACC", "Accumulator for XGMI write data"}},
    {AMDGpuMetricAttributeId_t::XGMI_LINK_STATUS, {"XGMI_LINK_STATUS", "Status of the XGMI link"}},
    {AMDGpuMetricAttributeId_t::FIRMWARE_TIMESTAMP,
     {"Firmware Timestamp", "Timestamp from the firmware"}},
    {AMDGpuMetricAttributeId_t::CURRENT_GFXCLK,
     {"CURRENT_GFXCLK", "Current GFX clock frequency in MHz"}},
    {AMDGpuMetricAttributeId_t::CURRENT_SOCCLK,
     {"CURRENT_SOCCLK", "Current SOC clock frequency in MHz"}},
    {AMDGpuMetricAttributeId_t::CURRENT_VCLK0, {"CURRENT_VCLK0", "Current VCLK0 frequency in MHz"}},
    {AMDGpuMetricAttributeId_t::CURRENT_DCLK0, {"CURRENT_DCLK0", "Current DCLK0 frequency in MHz"}},
    {AMDGpuMetricAttributeId_t::CURRENT_UCLK, {"CURRENT_UCLK", "Current UCLK frequency in MHz"}},
    {AMDGpuMetricAttributeId_t::NUM_PARTITION, {"NUM_PARTITION", "Number of GPU partitions"}},
    {AMDGpuMetricAttributeId_t::PCIE_LC_PERF_OTHER_END_RECOVERY,
     {"PCIE_LC_PERF_OTHER_END_RECOVERY", "PCIe link controller performance other end recovery"}},
    {AMDGpuMetricAttributeId_t::GFX_BUSY_INST,
     {"GFX_BUSY_INST", "Instantaneous GFX busy percentage"}},
    {AMDGpuMetricAttributeId_t::JPEG_BUSY, {"JPEG_BUSY", "JPEG engine busy percentage"}},
    {AMDGpuMetricAttributeId_t::VCN_BUSY, {"VCN_BUSY", "Video Core Next engine busy percentage"}},
    {AMDGpuMetricAttributeId_t::GFX_BUSY_ACC,
     {"GFX_BUSY_ACC", "Accumulator for GFX busy percentage"}},
    {AMDGpuMetricAttributeId_t::GFX_BELOW_HOST_LIMIT_PPT_ACC,
     {"GFX_BELOW_HOST_LIMIT_PPT_ACC", "Accumulator for GFX below host limit due to PPT"}},
    {AMDGpuMetricAttributeId_t::GFX_BELOW_HOST_LIMIT_THM_ACC,
     {"GFX_BELOW_HOST_LIMIT_THM_ACC", "Accumulator for GFX below host limit due to thermal"}},
    {AMDGpuMetricAttributeId_t::GFX_LOW_UTILIZATION_ACC,
     {"GFX_LOW_UTILIZATION_ACC", "Accumulator for GFX low utilization"}},
    {AMDGpuMetricAttributeId_t::GFX_BELOW_HOST_LIMIT_TOTAL_ACC,
     {"GFX_BELOW_HOST_LIMIT_TOTAL_ACC", "Total accumulator for GFX below host limit"}},
    {AMDGpuMetricAttributeId_t::TEMPERATURE_HBM,
     {"TEMPERATURE_HBM", "Temperature of High Bandwidth Memory"}},
    {AMDGpuMetricAttributeId_t::TEMPERATURE_MID,
     {"TEMPERATURE_MID", "MID (Multimedia IO Die) temperature"}},
    {AMDGpuMetricAttributeId_t::TEMPERATURE_AID,
     {"TEMPERATURE_AID", "AID (Active Interposer Die) temperature"}},
    {AMDGpuMetricAttributeId_t::TEMPERATURE_XCD,
     {"TEMPERATURE_XCD", "XCD (Accelerator Compute Die) temperature"}},
};

/*
 * Unit types used by attribute instances
 */
enum class AMDGpuMetricUnitType_t {
  NONE,

  /*
   * Temperature units
   */
  CELSIUS,
  CELSIUS_ACCUMULATOR,

  /*
   * Bandwidth/Data Rate units
   */
  BIT_PER_SECOND,
  BYTE_PER_SECOND,
  KILOBYTE_PER_SECOND,
  KILOBYTE_PER_SECOND_ACCUMULATOR,
  GIGABYTE_PER_SECOND,
  GIGABYTE_PER_SECOND_ACCUMULATOR,

  /*
   * Power/Energy units
   */
  WATT,
  JOULE,

  /*
   * Electrical units
   */
  VOLTAGE,

  /*
   * Time/Frequency units
   */
  TIMESTAMP_NANOSECONDS,
  CLOCK_MEGAHERTZ,

  /*
   * Unitless or generic units
   */
  PERCENT,
  COUNT_ACCUMULATOR,
  QUANTITY,
  STATUS_FLAG
};

using AMDGpuMetricUnitTypeTranslationTable_t =
    std::unordered_map<AMDGpuMetricUnitType_t, AMDGpuDynamicTranslationTextInfo_t>;

static const auto AMDGpuMetricUnitTypeToString = AMDGpuMetricUnitTypeTranslationTable_t{
    {AMDGpuMetricUnitType_t::NONE, {"NONE", "No unit"}},
    {AMDGpuMetricUnitType_t::CELSIUS, {"CELSIUS", "Temperature (°C)"}},
    {AMDGpuMetricUnitType_t::CELSIUS_ACCUMULATOR,
     {"CELSIUS_ACCUMULATOR", "Accumulated temperature counter (°C)"}},
    {AMDGpuMetricUnitType_t::BIT_PER_SECOND, {"BIT_PER_SECOND", "Throughput (bit/s)"}},
    {AMDGpuMetricUnitType_t::BYTE_PER_SECOND, {"BYTE_PER_SECOND", "Throughput (B/s)"}},
    {AMDGpuMetricUnitType_t::KILOBYTE_PER_SECOND, {"KILOBYTE_PER_SECOND", "Throughput (KB/s)"}},
    {AMDGpuMetricUnitType_t::KILOBYTE_PER_SECOND_ACCUMULATOR,
     {"KILOBYTE_PER_SECOND_ACCUMULATOR", "Accumulated KB/s counter"}},
    {AMDGpuMetricUnitType_t::GIGABYTE_PER_SECOND, {"GIGABYTE_PER_SECOND", "Throughput (GB/s)"}},
    {AMDGpuMetricUnitType_t::GIGABYTE_PER_SECOND_ACCUMULATOR,
     {"GIGABYTE_PER_SECOND_ACCUMULATOR", "Accumulated GB/s counter"}},
    {AMDGpuMetricUnitType_t::WATT, {"WATT", "Power (W)"}},
    {AMDGpuMetricUnitType_t::JOULE, {"JOULE", "Energy (J)"}},
    {AMDGpuMetricUnitType_t::VOLTAGE, {"VOLTAGE", "Voltage (V)"}},
    {AMDGpuMetricUnitType_t::TIMESTAMP_NANOSECONDS,
     {"TIMESTAMP_NANOSECONDS", "Timestamp / time (ns)"}},
    {AMDGpuMetricUnitType_t::CLOCK_MEGAHERTZ, {"CLOCK_MEGAHERTZ", "Frequency (MHz)"}},
    {AMDGpuMetricUnitType_t::PERCENT, {"PERCENT", "Percentage (%)"}},
    {AMDGpuMetricUnitType_t::COUNT_ACCUMULATOR, {"COUNT_ACCUMULATOR", "Monotonic count"}},
    {AMDGpuMetricUnitType_t::QUANTITY, {"QUANTITY", "Unitless Quantity"}},
    {AMDGpuMetricUnitType_t::STATUS_FLAG, {"STATUS_FLAG", "Status bit/flag (bitmask)"}},
};

/*
 *  Header structure for dynamic GPU metrics
 */
struct AMDGpuDynamicMetricsHeader_v1_t {
 public:
  uint16_t m_structure_size;
  uint8_t m_format_revision;
  uint8_t m_content_revision;

  static constexpr auto get_size() -> std::size_t {
    return sizeof(AMDGpuDynamicMetricsHeader_v1_t);
  }

 private:
};

using AMDGpuDynamicMetricsVersion_t = std::set<std::pair<std::uint8_t, std::uint8_t>>;

/*
 * Attribute IDs for the GPU metrics
 */
constexpr auto get_metric_data_type_size(AMDGpuMetricAttributeType_t attrib_type) -> std::size_t;

struct AMDGpuMetricAttributeInstance_t {
 public:
  std::string m_name;
  std::string m_description;
  AMDGpuMetricAttributeId_t m_attribute_id;
  AMDGpuMetricAttributeType_t m_attribute_type;
  AMDGpuMetricUnitType_t m_unit_type;

  AMDGpuMetricAttributeInstance_t() = default;

  AMDGpuMetricAttributeInstance_t(const std::string& name, const std::string& description,
                                  AMDGpuMetricAttributeId_t attribute_id,
                                  AMDGpuMetricAttributeType_t attribute_type,
                                  AMDGpuMetricUnitType_t unit_type)
      : m_name(name),
        m_description(description),
        m_attribute_id(attribute_id),
        m_attribute_type(attribute_type),
        m_unit_type(unit_type) {
    m_unique_id = get_unique_attribute_id(attribute_id, attribute_type);

    /*
     *  The availability version is a set of pairs representing the major and minor version.
     *  This allows for tracking the availability of the metric across different versions.
     *  For now, we initialize it to an empty set, meaning the metric is available in all versions.
     */
    m_availability_version = {{0, 0}};
  }

  AMDGpuMetricAttributeInstance_t(const std::string& name, const std::string& description,
                                  AMDGpuMetricAttributeId_t attribute_id,
                                  AMDGpuMetricAttributeType_t attribute_type,
                                  AMDGpuMetricUnitType_t unit_type,
                                  const AMDGpuDynamicMetricsVersion_t& availability_version)
      : m_name(name),
        m_description(description),
        m_attribute_id(attribute_id),
        m_attribute_type(attribute_type),
        m_unit_type(unit_type),
        m_availability_version(availability_version) {
    m_unique_id = get_unique_attribute_id(attribute_id, attribute_type);
  }

  /*
   *  Get the unique ID of the metric instance.
   */
  constexpr auto get_unique_attribute_id(AMDGpuMetricAttributeId_t attribute_id,
                                         AMDGpuMetricAttributeType_t attribute_type)
      -> std::uint64_t {
    /*
     *  The unique ID is calculated based on the attribute ID and type.
     *  This allows for a unique identifier for each metric instance.
     *
     *  Example:
     *    If attribute_id is TEMPERATURE_MEM (1) and attribute_type is TYPE_INT32 (5),
     *    then m_unique_id will be 1 * 100 + 5 = 105.
     *
     *  We might need to revisit this, but for now, it serves as a unique identifier.
     */
    return (static_cast<std::uint64_t>(attribute_id) * 100 +
            static_cast<std::uint64_t>(attribute_type));
  }

  constexpr auto get_type_size() const -> std::size_t {
    return get_metric_data_type_size(m_attribute_type);
  }

 private:
  std::uint64_t m_unique_id;
  AMDGpuDynamicMetricsVersion_t m_availability_version;
};

/*
 *  Based on supported value types in `AMDGpuMetricAttributeType_t`
 */
using AMDGpuMetricAttributeValue_t =
    std::variant<std::uint8_t, std::int8_t, std::uint16_t, std::int16_t, std::uint32_t,
                 std::int32_t, std::uint64_t, std::int64_t, std::vector<std::uint8_t>,
                 std::vector<std::int8_t>, std::vector<std::uint16_t>, std::vector<std::int16_t>,
                 std::vector<std::uint32_t>, std::vector<std::int32_t>, std::vector<std::uint64_t>,
                 std::vector<std::int64_t>>;

struct AMDGpuMetricValueDataSizeVisitor_t {
 public:
  /*
   *  Helper to check if Tp is a std::vector
   */
  template <typename Tp>
  struct is_std_vector : std::false_type {};

  template <typename U, typename Alloc>
  struct is_std_vector<std::vector<U, Alloc>> : std::true_type {};

  /*
   *  Scalar types only
   */
  template <typename Tp>
  constexpr auto operator()(const Tp& value) const -> std::size_t {
    if constexpr (!is_std_vector<Tp>::value) {
      return sizeof(Tp);
    } else {
      return value.size() * sizeof(typename Tp::value_type);
    }
  }

 private:
};

struct AMDGpuMetricAttributeData_t {
 public:
  AMDGpuMetricAttributeInstance_t m_instance;
  AMDGpuMetricAttributeValue_t m_value;

  AMDGpuMetricAttributeData_t() = default;
  AMDGpuMetricAttributeData_t(const AMDGpuMetricAttributeInstance_t& metric_instance,
                              const AMDGpuMetricAttributeValue_t& metric_value)
      : m_instance(metric_instance), m_value(metric_value) {}

  auto is_multivalued() const -> bool {
    return (std::holds_alternative<std::vector<std::uint8_t>>(m_value) ||
            std::holds_alternative<std::vector<std::int8_t>>(m_value) ||
            std::holds_alternative<std::vector<std::uint16_t>>(m_value) ||
            std::holds_alternative<std::vector<std::int16_t>>(m_value) ||
            std::holds_alternative<std::vector<std::uint32_t>>(m_value) ||
            std::holds_alternative<std::vector<std::int32_t>>(m_value) ||
            std::holds_alternative<std::vector<std::uint64_t>>(m_value) ||
            std::holds_alternative<std::vector<std::int64_t>>(m_value));
  }

  constexpr auto get_metric_serialized_data_size() const -> std::size_t {
    return std::visit(AMDGpuMetricValueDataSizeVisitor_t{}, m_value);
  }

 private:
};

// Hash for enum-class keys
struct AttributeIdHash_t {
  size_t operator()(AMDGpuMetricAttributeId_t id) const noexcept {
    using U = std::underlying_type_t<AMDGpuMetricAttributeId_t>;
    return std::hash<U>{}(static_cast<U>(id));
  }
};

using AMDGpuMetricSchemaType_t = std::vector<AMDGpuMetricAttributeData_t>;

using AMDGpuMetricSchemaMapType_t =
    std::unordered_map<AMDGpuMetricAttributeId_t, AMDGpuMetricAttributeData_t, AttributeIdHash_t>;

// Check if type Tp has callable 0 arg member function named "is_multivalued()"
template <typename Tp, typename = void>
struct is_multivalued_attribute : std::false_type {};

template <typename Tp>
struct is_multivalued_attribute<Tp, std::void_t<decltype(std::declval<Tp>().is_multivalued())>>
    : std::true_type {};

constexpr auto get_metric_data_type_size(AMDGpuMetricAttributeType_t attrib_type) -> std::size_t {
  switch (attrib_type) {
    case (AMDGpuMetricAttributeType_t::TYPE_UINT8):
      return sizeof(std::uint8_t);

    case (AMDGpuMetricAttributeType_t::TYPE_INT8):
      return sizeof(std::int8_t);

    case (AMDGpuMetricAttributeType_t::TYPE_UINT16):
      return sizeof(std::uint16_t);

    case (AMDGpuMetricAttributeType_t::TYPE_INT16):
      return sizeof(std::int16_t);

    case (AMDGpuMetricAttributeType_t::TYPE_UINT32):
      return sizeof(std::uint32_t);

    case (AMDGpuMetricAttributeType_t::TYPE_INT32):
      return sizeof(std::int32_t);

    case (AMDGpuMetricAttributeType_t::TYPE_UINT64):
      return sizeof(std::uint64_t);

    case (AMDGpuMetricAttributeType_t::TYPE_INT64):
      return sizeof(std::int64_t);

    default:
      throw std::runtime_error("Error: Metric attribute type unknown... ");
  }
}

enum class AMDGpuMetricAttributeTypeFlag_t : std::uint32_t {
  ATTRIBUTE_FLAG_TYPE_NONE = (0x0),
  ATTRIBUTE_FLAG_TYPE8 = (0x1 << 0),
  ATTRIBUTE_FLAG_TYPE16 = (0x1 << 1),
  ATTRIBUTE_FLAG_TYPE32 = (0x1 << 2),
  ATTRIBUTE_FLAG_TYPE64 = (0x1 << 3),
};

// Used to determine how far to skip when parsing gpu_metrics file
constexpr auto get_metric_bytes(AMDGpuMetricAttributeType_t attrib_type) -> std::size_t {
  using Flag = AMDGpuMetricAttributeTypeFlag_t;

  switch (attrib_type) {
    case AMDGpuMetricAttributeType_t::TYPE_UINT8:
    case AMDGpuMetricAttributeType_t::TYPE_INT8:
      return static_cast<std::size_t>(Flag::ATTRIBUTE_FLAG_TYPE8);

    case AMDGpuMetricAttributeType_t::TYPE_UINT16:
    case AMDGpuMetricAttributeType_t::TYPE_INT16:
      return static_cast<std::size_t>(Flag::ATTRIBUTE_FLAG_TYPE16);

    case AMDGpuMetricAttributeType_t::TYPE_UINT32:
    case AMDGpuMetricAttributeType_t::TYPE_INT32:
      return static_cast<std::size_t>(Flag::ATTRIBUTE_FLAG_TYPE32);

    case AMDGpuMetricAttributeType_t::TYPE_UINT64:
    case AMDGpuMetricAttributeType_t::TYPE_INT64:
      return static_cast<std::size_t>(Flag::ATTRIBUTE_FLAG_TYPE64);
  }
  return 0;  // Unreachable
}

constexpr auto ATTR_INST_BITS = std::uint8_t(10);  // ATTR_INST_MASK (0x000003FF)
constexpr auto ATTR_ID_BITS = std::uint8_t(10);    // ATTR_ID_MASK   (0x000FFC00)
constexpr auto ATTR_TYPE_BITS = std::uint8_t(4);   // ATTR_TYPE_MASK (0x00F00000)
constexpr auto ATTR_UNIT_BITS = std::uint8_t(8);   // ATTR_UNIT_MASK (0xFF000000)

/*
 *  Bit shifting are constant, derived from bit sizes
 */
constexpr auto ATTR_ID_SHIFT = (ATTR_INST_BITS);                      // 10
constexpr auto ATTR_TYPE_SHIFT = (ATTR_ID_SHIFT + ATTR_ID_BITS);      // 20
constexpr auto ATTR_UNIT_SHIFT = (ATTR_TYPE_SHIFT + ATTR_TYPE_BITS);  // 24

/*
 *  Masks are constant and used for decoding values safely
 *  - They are derived from bit sizes and shifts
 *  - They help in isolating specific fields when encoding/decoding
 */
constexpr auto ATTR_INST_MASK = static_cast<std::uint64_t>((1ULL << ATTR_INST_BITS) - 1);
constexpr auto ATTR_ID_MASK =
    static_cast<std::uint64_t>(((1ULL << ATTR_ID_BITS) - 1) << ATTR_ID_SHIFT);
constexpr auto ATTR_TYPE_MASK =
    static_cast<std::uint64_t>(((1ULL << ATTR_TYPE_BITS) - 1) << ATTR_TYPE_SHIFT);
constexpr auto ATTR_UNIT_MASK =
    static_cast<std::uint64_t>(((1ULL << ATTR_UNIT_BITS) - 1) << ATTR_UNIT_SHIFT);

struct AMDGpuMetricAttributeDecode_t {
 public:
  uint64_t m_attr_unit;      // Unit type, currently unused
  uint64_t m_attr_type;      // Type (e.g., U8, S16, U32, etc.)
  uint64_t m_attr_id;        // Attribute ID (enumerated in `AMDGpuMetricAttributeId_t`)
  uint64_t m_attr_instance;  // Instance count (number of values for this attribute)

  constexpr auto operator==(const AMDGpuMetricAttributeDecode_t& other) const noexcept -> bool {
    return ((m_attr_unit == other.m_attr_unit) && (m_attr_type == other.m_attr_type) &&
            (m_attr_id == other.m_attr_id) && (m_attr_instance == other.m_attr_instance));
  }

 private:
};

/*
 *  Function to encode the attribute type, ID, and instance into a single uint32_t value.
 *  So we can do something like:
 *    auto attribute1 = amdgpu_metrics_enc_attr(AMDGpuMetricAttributeType_t::TYPE_UINT32,
 *                                              AMDGpuMetricAttributeId_t::GFX_BUSY_INST,
 *
 */
[[nodiscard]]
constexpr auto amdgpu_metrics_encode_attr(std::uint64_t attr_unit, std::uint64_t attr_type,
                                          std::uint64_t attr_id,
                                          std::uint64_t attr_instance) noexcept -> std::uint64_t {
  return ((attr_unit << ATTR_UNIT_SHIFT) | (attr_type << ATTR_TYPE_SHIFT) |
          (attr_id << ATTR_ID_SHIFT) | (attr_instance));
}

[[nodiscard]]
constexpr auto amdgpu_metrics_decode_attr(std::uint64_t encoded_attr) noexcept
    -> AMDGpuMetricAttributeDecode_t {
  return AMDGpuMetricAttributeDecode_t{
      .m_attr_unit = ((encoded_attr & ATTR_UNIT_MASK) >> ATTR_UNIT_SHIFT),
      .m_attr_type = ((encoded_attr & ATTR_TYPE_MASK) >> ATTR_TYPE_SHIFT),
      .m_attr_id = ((encoded_attr & ATTR_ID_MASK) >> ATTR_ID_SHIFT),
      .m_attr_instance = (encoded_attr & ATTR_INST_MASK)};
}

}  // namespace details

/*
 *  The AMDGpuMetricsBaseSchema is a predefined schema for GPU metrics.
 *  It contains a list of metric instances with their respective attributes and initial values.
 *  This schema is used to define the structure of the GPU metrics that can be collected.
 */
static const auto AMDGpuMetricsBaseSchema = details::AMDGpuMetricSchemaMapType_t{
    {details::AMDGpuMetricAttributeId_t::TEMPERATURE_HOTSPOT,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Temperature Hotspot", "Temperature of the GPU hotspot",
             details::AMDGpuMetricAttributeId_t::TEMPERATURE_HOTSPOT,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
             details::AMDGpuMetricUnitType_t::CELSIUS),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::TEMPERATURE_MEM,
     details::AMDGpuMetricAttributeData_t{details::AMDGpuMetricAttributeInstance_t(
                                              "Temperature Memory", "Temperature of the GPU memory",
                                              details::AMDGpuMetricAttributeId_t::TEMPERATURE_MEM,
                                              details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                              details::AMDGpuMetricUnitType_t::CELSIUS),
                                          static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::TEMPERATURE_VRSOC,
     details::AMDGpuMetricAttributeData_t{details::AMDGpuMetricAttributeInstance_t(
                                              "Temperature VR SOC", "Temperature of the VR SOC",
                                              details::AMDGpuMetricAttributeId_t::TEMPERATURE_VRSOC,
                                              details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                              details::AMDGpuMetricUnitType_t::CELSIUS),
                                          static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::CURR_SOCKET_POWER,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Current Socket Power", "Current power consumption of the socket",
             details::AMDGpuMetricAttributeId_t::CURR_SOCKET_POWER,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
             details::AMDGpuMetricUnitType_t::WATT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::AVERAGE_GFX_ACTIVITY,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Average GFX Activity", "Average GPU activity percentage",
             details::AMDGpuMetricAttributeId_t::AVERAGE_GFX_ACTIVITY,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
             details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::AVERAGE_UMC_ACTIVITY,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Average UMC Activity", "Average UMC activity percentage",
             details::AMDGpuMetricAttributeId_t::AVERAGE_UMC_ACTIVITY,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
             details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::MEM_MAX_BANDWIDTH,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Memory Max Bandwidth", "Maximum memory bandwidth in GB/s",
             details::AMDGpuMetricAttributeId_t::MEM_MAX_BANDWIDTH,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::GIGABYTE_PER_SECOND),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::ENERGY_ACCUMULATOR,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Energy Accumulator", "Energy consumed in Joules",
             details::AMDGpuMetricAttributeId_t::ENERGY_ACCUMULATOR,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::JOULE),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::SYSTEM_CLOCK_COUNTER,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "System Clock Counter", "System clock counter in nanoseconds",
             details::AMDGpuMetricAttributeId_t::SYSTEM_CLOCK_COUNTER,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::TIMESTAMP_NANOSECONDS),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::ACCUMULATION_COUNTER,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Accumulation Counter", "Counter for accumulated metrics",
             details::AMDGpuMetricAttributeId_t::ACCUMULATION_COUNTER,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::COUNT_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PROCHOT_RESIDENCY_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "ProcHot Residency Accumulator", "Accumulator for 'Processor Hot' residency time",
             details::AMDGpuMetricAttributeId_t::PROCHOT_RESIDENCY_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT32,
             details::AMDGpuMetricUnitType_t::CELSIUS_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PPT_RESIDENCY_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "PPT Residency Accumulator", "Accumulator for 'Package Power Tracking' residency time",
             details::AMDGpuMetricAttributeId_t::PPT_RESIDENCY_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT32,
             details::AMDGpuMetricUnitType_t::CELSIUS_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::SOCKET_THM_RESIDENCY_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Socket Thermal Residency Accumulator",
             "Accumulator for socket thermal residency time",
             details::AMDGpuMetricAttributeId_t::SOCKET_THM_RESIDENCY_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT32,
             details::AMDGpuMetricUnitType_t::CELSIUS_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::VR_THM_RESIDENCY_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "VR Thermal Residency Accumulator",
             "Accumulator for 'Voltage Regulator' thermal residency time",
             details::AMDGpuMetricAttributeId_t::VR_THM_RESIDENCY_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT32,
             details::AMDGpuMetricUnitType_t::CELSIUS_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::HBM_THM_RESIDENCY_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "HBM Thermal Residency Accumulator",
             "Accumulator for 'High Bandwidth Memory' thermal residency time",
             details::AMDGpuMetricAttributeId_t::HBM_THM_RESIDENCY_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT32,
             details::AMDGpuMetricUnitType_t::CELSIUS_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::GFXCLK_LOCK_STATUS,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "GFXCLK Lock Status", "Status of GFX clock lock",
             details::AMDGpuMetricAttributeId_t::GFXCLK_LOCK_STATUS,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT32,
             details::AMDGpuMetricUnitType_t::STATUS_FLAG),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PCIE_LINK_WIDTH,
     details::AMDGpuMetricAttributeData_t{details::AMDGpuMetricAttributeInstance_t(
                                              "PCIe Link Width", "Current PCIe link width",
                                              details::AMDGpuMetricAttributeId_t::PCIE_LINK_WIDTH,
                                              details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                              details::AMDGpuMetricUnitType_t::QUANTITY),
                                          static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PCIE_LINK_SPEED,
     details::AMDGpuMetricAttributeData_t{details::AMDGpuMetricAttributeInstance_t(
                                              "PCIe Link Speed", "Current PCIe link speed in GT/s",
                                              details::AMDGpuMetricAttributeId_t::PCIE_LINK_SPEED,
                                              details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                              details::AMDGpuMetricUnitType_t::GIGABYTE_PER_SECOND),
                                          static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::XGMI_LINK_WIDTH,
     details::AMDGpuMetricAttributeData_t{details::AMDGpuMetricAttributeInstance_t(
                                              "XGMI Link Width", "Current XGMI link width",
                                              details::AMDGpuMetricAttributeId_t::XGMI_LINK_WIDTH,
                                              details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                              details::AMDGpuMetricUnitType_t::QUANTITY),
                                          static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::XGMI_LINK_SPEED,
     details::AMDGpuMetricAttributeData_t{details::AMDGpuMetricAttributeInstance_t(
                                              "XGMI Link Speed", "Current XGMI link speed in GT/s",
                                              details::AMDGpuMetricAttributeId_t::XGMI_LINK_SPEED,
                                              details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                              details::AMDGpuMetricUnitType_t::GIGABYTE_PER_SECOND),
                                          static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::GFX_ACTIVITY_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "GFX Activity Accumulator", "Accumulator for GFX activity percentage",
             details::AMDGpuMetricAttributeId_t::GFX_ACTIVITY_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::MEM_ACTIVITY_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Memory Activity Accumulator", "Accumulator for memory activity percentage",
             details::AMDGpuMetricAttributeId_t::MEM_ACTIVITY_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PCIE_BANDWIDTH_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "PCIe Bandwidth Accumulator", "Accumulator for PCIe bandwidth in GB/s",
             details::AMDGpuMetricAttributeId_t::PCIE_BANDWIDTH_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::GIGABYTE_PER_SECOND_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PCIE_BANDWIDTH_INST,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "PCIe Bandwidth Instantaneous", "Instantaneous PCIe bandwidth in GB/s",
             details::AMDGpuMetricAttributeId_t::PCIE_BANDWIDTH_INST,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::GIGABYTE_PER_SECOND),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PCIE_L0_TO_RECOV_COUNT_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "PCIe L0 to Recovery Count Accumulator", "Accumulator for PCIe L0 to recovery count",
             details::AMDGpuMetricAttributeId_t::PCIE_L0_TO_RECOV_COUNT_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::COUNT_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PCIE_REPLAY_COUNT_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "PCIe Replay Count Accumulator", "Accumulator for PCIe replay count",
             details::AMDGpuMetricAttributeId_t::PCIE_REPLAY_COUNT_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::COUNT_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PCIE_REPLAY_ROVER_COUNT_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "PCIe Replay Rover Count Accumulator", "Accumulator for PCIe replay rover count",
             details::AMDGpuMetricAttributeId_t::PCIE_REPLAY_ROVER_COUNT_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::COUNT_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PCIE_NAK_SENT_COUNT_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "PCIe NAK Sent Count Accumulator", "Accumulator for PCIe NAK sent count",
             details::AMDGpuMetricAttributeId_t::PCIE_NAK_SENT_COUNT_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::COUNT_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PCIE_NAK_RCVD_COUNT_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "PCIe NAK Received Count Accumulator", "Accumulator for PCIe NAK received count",
             details::AMDGpuMetricAttributeId_t::PCIE_NAK_RCVD_COUNT_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::COUNT_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::XGMI_READ_DATA_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "XGMI Read Data Accumulator", "Accumulator for XGMI read data in bytes",
             details::AMDGpuMetricAttributeId_t::XGMI_READ_DATA_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::KILOBYTE_PER_SECOND_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::XGMI_WRITE_DATA_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "XGMI Write Data Accumulator", "Accumulator for XGMI write data in bytes",
             details::AMDGpuMetricAttributeId_t::XGMI_WRITE_DATA_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::KILOBYTE_PER_SECOND_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::XGMI_LINK_STATUS,
     details::AMDGpuMetricAttributeData_t{details::AMDGpuMetricAttributeInstance_t(
                                              "XGMI Link Status", "Status of the XGMI link",
                                              details::AMDGpuMetricAttributeId_t::XGMI_LINK_STATUS,
                                              details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                              details::AMDGpuMetricUnitType_t::STATUS_FLAG),
                                          static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::FIRMWARE_TIMESTAMP,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Firmware Timestamp", "Timestamp of the firmware in nanoseconds",
             details::AMDGpuMetricAttributeId_t::FIRMWARE_TIMESTAMP,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::TIMESTAMP_NANOSECONDS),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::CURRENT_GFXCLK,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Current GFX Clock", "Current GFX clock frequency in MHz",
             details::AMDGpuMetricAttributeId_t::CURRENT_GFXCLK,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
             details::AMDGpuMetricUnitType_t::CLOCK_MEGAHERTZ),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::CURRENT_SOCCLK,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Current SOC Clock", "Current SOC clock frequency in MHz",
             details::AMDGpuMetricAttributeId_t::CURRENT_SOCCLK,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
             details::AMDGpuMetricUnitType_t::CLOCK_MEGAHERTZ),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::CURRENT_VCLK0,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t("Current VCLK0", "Current VCLK0 frequency in MHz",
                                                  details::AMDGpuMetricAttributeId_t::CURRENT_VCLK0,
                                                  details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                                  details::AMDGpuMetricUnitType_t::CLOCK_MEGAHERTZ),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::CURRENT_DCLK0,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t("Current DCLK0", "Current DCLK0 frequency in MHz",
                                                  details::AMDGpuMetricAttributeId_t::CURRENT_DCLK0,
                                                  details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                                  details::AMDGpuMetricUnitType_t::CLOCK_MEGAHERTZ),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::CURRENT_UCLK,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t("Current UCLK", "Current UCLK frequency in MHz",
                                                  details::AMDGpuMetricAttributeId_t::CURRENT_UCLK,
                                                  details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                                  details::AMDGpuMetricUnitType_t::CLOCK_MEGAHERTZ),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::NUM_PARTITION,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t("Number of Partitions",
                                                  "Number of partitions in the GPU",
                                                  details::AMDGpuMetricAttributeId_t::NUM_PARTITION,
                                                  details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                                  details::AMDGpuMetricUnitType_t::QUANTITY),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::PCIE_LC_PERF_OTHER_END_RECOVERY,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "PCIe LC Perf Other End Recovery", "PCIe link control performance other end recovery",
             details::AMDGpuMetricAttributeId_t::PCIE_LC_PERF_OTHER_END_RECOVERY,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::COUNT_ACCUMULATOR),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::GFX_BUSY_INST,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t("GFX Busy Instantaneous",
                                                  "GFX Busy Instantaneous in percent",
                                                  details::AMDGpuMetricAttributeId_t::GFX_BUSY_INST,
                                                  details::AMDGpuMetricAttributeType_t::TYPE_UINT32,
                                                  details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::JPEG_BUSY,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t("JPEG Busy Instantaneous",
                                                  "JPEG Busy Instantaneous in percent",
                                                  details::AMDGpuMetricAttributeId_t::JPEG_BUSY,
                                                  details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                                  details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::VCN_BUSY,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t("VCN Busy Instantaneous",
                                                  "VCN Busy Instantaneous in percent",
                                                  details::AMDGpuMetricAttributeId_t::VCN_BUSY,
                                                  details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
                                                  details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::GFX_BUSY_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t("GFX Busy Accumulator",
                                                  "GFX Busy Accumulator in percent",
                                                  details::AMDGpuMetricAttributeId_t::GFX_BUSY_ACC,
                                                  details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
                                                  details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::GFX_BELOW_HOST_LIMIT_PPT_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "GFX Below Host Limit PPT Accumulator",
             "GFX Below Host Limit PPT Accumulator in percent",
             details::AMDGpuMetricAttributeId_t::GFX_BELOW_HOST_LIMIT_PPT_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::GFX_BELOW_HOST_LIMIT_THM_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "GFX Below Host Limit THM Accumulator",
             "GFX Below Host Limit THM Accumulator in percent",
             details::AMDGpuMetricAttributeId_t::GFX_BELOW_HOST_LIMIT_THM_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::GFX_LOW_UTILIZATION_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "GFX Low Utilization Accumulator", "GFX Low Utilization Accumulator in percent",
             details::AMDGpuMetricAttributeId_t::GFX_LOW_UTILIZATION_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::GFX_BELOW_HOST_LIMIT_TOTAL_ACC,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "GFX Below Host Limit Total Accumulator",
             "GFX Below Host Limit Total Accumulator in percent",
             details::AMDGpuMetricAttributeId_t::GFX_BELOW_HOST_LIMIT_TOTAL_ACC,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT64,
             details::AMDGpuMetricUnitType_t::PERCENT),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::TEMPERATURE_HBM,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Temperature HBM", "Temperature of High Bandwidth Memory",
             details::AMDGpuMetricAttributeId_t::TEMPERATURE_HBM,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
             details::AMDGpuMetricUnitType_t::CELSIUS),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::TEMPERATURE_MID,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Temperature MID", "MID (Multimedia IO Die) temperature measurement",
             details::AMDGpuMetricAttributeId_t::TEMPERATURE_MID,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
             details::AMDGpuMetricUnitType_t::CELSIUS),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::TEMPERATURE_AID,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Temperature AID", "AID (Active Interposer Die) temperature",
             details::AMDGpuMetricAttributeId_t::TEMPERATURE_AID,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
             details::AMDGpuMetricUnitType_t::CELSIUS),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},

    {details::AMDGpuMetricAttributeId_t::TEMPERATURE_XCD,
     details::AMDGpuMetricAttributeData_t{
         details::AMDGpuMetricAttributeInstance_t(
             "Temperature XCD", "XCD (Accelerator Compute Die) temperature",
             details::AMDGpuMetricAttributeId_t::TEMPERATURE_XCD,
             details::AMDGpuMetricAttributeType_t::TYPE_UINT16,
             details::AMDGpuMetricUnitType_t::CELSIUS),
         static_cast<details::AMDGpuMetricAttributeValue_t>(0)}},
};

using AMDGpuDynamicMetricsOffsetMap_t = std::map<std::uint64_t, std::uint32_t>;
using AMDGpuDynamicMetricsOffsetIt_t = AMDGpuDynamicMetricsOffsetMap_t::const_iterator;

/*
 *  This is the actual representation of the whole dynamic metrics data structure, for either:
 *    - 'gpu_metrics' file
 *    - 'xcp_metrics' file
 */
class AMDGpuDynamicMetrics_t {
 public:
  AMDGpuDynamicMetrics_t() = default;
  AMDGpuDynamicMetrics_t(const AMDGpuDynamicMetrics_t&) = delete;
  AMDGpuDynamicMetrics_t(AMDGpuDynamicMetrics_t&& inst) noexcept
      : m_header(inst.m_header),
        m_dynamic_metrics_data(std::move(inst.m_dynamic_metrics_data)),
        m_dynamic_metrics_data_offsets(std::move(inst.m_dynamic_metrics_data_offsets)) {}
  AMDGpuDynamicMetrics_t& operator=(const AMDGpuDynamicMetrics_t&) = delete;
  AMDGpuDynamicMetrics_t& operator=(AMDGpuDynamicMetrics_t&& inst) {
    m_header = inst.m_header;
    m_dynamic_metrics_data = std::move(inst.m_dynamic_metrics_data);
    m_dynamic_metrics_data_offsets = std::move(inst.m_dynamic_metrics_data_offsets);
    return *this;
  }
  ~AMDGpuDynamicMetrics_t() {
    //{
    //    std::unique_lock<std::shared_mutex> lock(m_mutex);
    //    m_current_conditional_var.notify_all();
    //}
  }

  // Parsing helpers
  auto parse_from_buffer(const std::byte* data, std::size_t size) noexcept -> rsmi_status_t;
  auto parse_from_file(const std::string& metrics_file_path, std::size_t read_size = 0)
      -> rsmi_status_t;

  auto get_metric_rows() const noexcept -> const details::AMDGpuMetricSchemaType_t& {
    return m_dynamic_metrics_data;
  }

  auto get_header() const noexcept -> const details::AMDGpuDynamicMetricsHeader_v1_t& {
    return m_header;
  }

  /*
   *  The Cursor here, is a helper class to help with navigation within the dynamic metrics data
   *  based on the data offsets
   *
   */
  class AMDGpuDynamicMetricsCursor_t {
   public:
    AMDGpuDynamicMetricsCursor_t(const AMDGpuDynamicMetrics_t& metrics_data,
                                 std::uint64_t start_offset = 0)
        : m_metrics(metrics_data),
          m_current_offset(start_offset),
          m_read_lock(metrics_data.m_mutex) {
      m_current_metric_attribute = m_metrics.m_dynamic_metrics_data_offsets.lower_bound(0);
    }

    ~AMDGpuDynamicMetricsCursor_t() = default;

   private:
    const AMDGpuDynamicMetrics_t& m_metrics;
    std::uint64_t m_current_offset{0};
    AMDGpuDynamicMetricsOffsetIt_t m_current_metric_attribute;
    mutable std::shared_lock<std::shared_mutex> m_read_lock;
  };

 private:
  std::string m_metric_source_file_path{};
  details::AMDGpuDynamicMetricsHeader_v1_t m_header;
  uint32_t m_attr_count;
  details::AMDGpuMetricSchemaType_t m_dynamic_metrics_data{};
  AMDGpuDynamicMetricsOffsetMap_t m_dynamic_metrics_data_offsets{};
  mutable std::shared_mutex m_mutex;
};

template <typename Tp>
constexpr auto is_multivalued_attribute_v = details::is_multivalued_attribute<Tp>::value;

using AMDGPUMetricsDynDataBuffer_t = std::vector<std::byte>;
rsmi_status_t read_dynamic_gpu_metrics_file(const std::string& metrics_file_path,
                                            const size_t read_size,
                                            AMDGPUMetricsDynDataBuffer_t& out);
}  // namespace amd::smi

#endif  // ROCM_SMI_ROCM_SMI_DYN_GPU_METRICS_H_
