/*
 * 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 AMD_SMI_INCLUDE_AMD_SMI_COMMON_H_
#define AMD_SMI_INCLUDE_AMD_SMI_COMMON_H_

#include <map>

#include "amd_smi/amdsmi.h"
#include "rocm_smi/rocm_smi.h"

#ifdef ENABLE_ESMI_LIB
extern "C" {
#include <e_smi/e_smi.h>

#include <cstdint>
}
#endif

extern "C" {
#include "amd_smi/impl/nic/amdsmi_unified/interface/smi_nic_interface.h"
}
namespace amd::smi {

// Define a map of rsmi status codes to amdsmi status codes
const std::map<rsmi_status_t, amdsmi_status_t> rsmi_status_map = {
    {RSMI_STATUS_SUCCESS, AMDSMI_STATUS_SUCCESS},
    {RSMI_STATUS_INVALID_ARGS, AMDSMI_STATUS_INVAL},
    {RSMI_STATUS_NOT_SUPPORTED, AMDSMI_STATUS_NOT_SUPPORTED},
    {RSMI_STATUS_FILE_ERROR, AMDSMI_STATUS_FILE_ERROR},
    {RSMI_STATUS_PERMISSION, AMDSMI_STATUS_NO_PERM},
    {RSMI_STATUS_OUT_OF_RESOURCES, AMDSMI_STATUS_OUT_OF_RESOURCES},
    {RSMI_STATUS_INTERNAL_EXCEPTION, AMDSMI_STATUS_INTERNAL_EXCEPTION},
    {RSMI_STATUS_INPUT_OUT_OF_BOUNDS, AMDSMI_STATUS_INPUT_OUT_OF_BOUNDS},
    {RSMI_STATUS_INIT_ERROR, AMDSMI_STATUS_NOT_INIT},
    {RSMI_INITIALIZATION_ERROR, AMDSMI_STATUS_NOT_INIT},
    {RSMI_STATUS_NOT_YET_IMPLEMENTED, AMDSMI_STATUS_NOT_YET_IMPLEMENTED},
    {RSMI_STATUS_NOT_FOUND, AMDSMI_STATUS_NOT_FOUND},
    {RSMI_STATUS_INSUFFICIENT_SIZE, AMDSMI_STATUS_INSUFFICIENT_SIZE},
    {RSMI_STATUS_INTERRUPT, AMDSMI_STATUS_INTERRUPT},
    {RSMI_STATUS_UNEXPECTED_SIZE, AMDSMI_STATUS_UNEXPECTED_SIZE},
    {RSMI_STATUS_NO_DATA, AMDSMI_STATUS_NO_DATA},
    {RSMI_STATUS_UNEXPECTED_DATA, AMDSMI_STATUS_UNEXPECTED_DATA},
    {RSMI_STATUS_BUSY, AMDSMI_STATUS_BUSY},
    {RSMI_STATUS_REFCOUNT_OVERFLOW, AMDSMI_STATUS_REFCOUNT_OVERFLOW},
    {RSMI_STATUS_DIRECTORY_NOT_FOUND, AMDSMI_STATUS_DIRECTORY_NOT_FOUND},
    {RSMI_STATUS_SETTING_UNAVAILABLE, AMDSMI_STATUS_SETTING_UNAVAILABLE},
    {RSMI_STATUS_AMDGPU_RESTART_ERR, AMDSMI_STATUS_AMDGPU_RESTART_ERR},
    {RSMI_STATUS_DRIVER_NOT_LOADED, AMDSMI_STATUS_DRIVER_NOT_LOADED},
    {RSMI_STATUS_IPC_ERROR, AMDSMI_STATUS_IPC_ERROR},
    {RSMI_STATUS_UNKNOWN_ERROR, AMDSMI_STATUS_UNKNOWN_ERROR},
};

const std::map<unsigned, amdsmi_vram_type_t> vram_type_map = {
    {0, AMDSMI_VRAM_TYPE_UNKNOWN}, {1, AMDSMI_VRAM_TYPE_GDDR1},  {2, AMDSMI_VRAM_TYPE_DDR2},
    {3, AMDSMI_VRAM_TYPE_GDDR3},   {4, AMDSMI_VRAM_TYPE_GDDR4},  {5, AMDSMI_VRAM_TYPE_GDDR5},
    {6, AMDSMI_VRAM_TYPE_HBM},     {7, AMDSMI_VRAM_TYPE_DDR3},   {8, AMDSMI_VRAM_TYPE_DDR4},
    {9, AMDSMI_VRAM_TYPE_GDDR6},   {10, AMDSMI_VRAM_TYPE_DDR5},  {11, AMDSMI_VRAM_TYPE_LPDDR4},
    {12, AMDSMI_VRAM_TYPE_LPDDR5}, {13, AMDSMI_VRAM_TYPE_HBM3E},
};

amdsmi_status_t rsmi_to_amdsmi_status(rsmi_status_t status);

amdsmi_vram_type_t vram_type_value(unsigned type);

#ifdef ENABLE_ESMI_LIB
// Define a map of esmi status codes to amdsmi status codes
const std::map<esmi_status_t, amdsmi_status_t> esmi_status_map = {
    {ESMI_SUCCESS, AMDSMI_STATUS_SUCCESS},
    {ESMI_INITIALIZED, AMDSMI_STATUS_SUCCESS},
    {ESMI_INVALID_INPUT, AMDSMI_STATUS_INVAL},
    {ESMI_NOT_SUPPORTED, AMDSMI_STATUS_NOT_SUPPORTED},
    {ESMI_PERMISSION, AMDSMI_STATUS_NO_PERM},
    {ESMI_INTERRUPTED, AMDSMI_STATUS_INTERRUPT},
    {ESMI_IO_ERROR, AMDSMI_STATUS_IO},
    {ESMI_FILE_ERROR, AMDSMI_STATUS_FILE_ERROR},
    {ESMI_NO_MEMORY, AMDSMI_STATUS_OUT_OF_RESOURCES},
    {ESMI_DEV_BUSY, AMDSMI_STATUS_BUSY},
    {ESMI_NOT_INITIALIZED, AMDSMI_STATUS_NOT_INIT},
    {ESMI_UNEXPECTED_SIZE, AMDSMI_STATUS_UNEXPECTED_SIZE},
    {ESMI_UNKNOWN_ERROR, AMDSMI_STATUS_UNKNOWN_ERROR},
    {ESMI_NO_ENERGY_DRV, AMDSMI_STATUS_NO_ENERGY_DRV},
    {ESMI_NO_MSR_DRV, AMDSMI_STATUS_NO_MSR_DRV},
    {ESMI_NO_HSMP_DRV, AMDSMI_STATUS_NO_HSMP_DRV},
    {ESMI_NO_HSMP_SUP, AMDSMI_STATUS_NO_HSMP_SUP},
    {ESMI_NO_DRV, AMDSMI_STATUS_NO_DRV},
    {ESMI_FILE_NOT_FOUND, AMDSMI_STATUS_FILE_NOT_FOUND},
    {ESMI_ARG_PTR_NULL, AMDSMI_STATUS_ARG_PTR_NULL},
    {ESMI_HSMP_TIMEOUT, AMDSMI_STATUS_HSMP_TIMEOUT},
    {ESMI_NO_HSMP_MSG_SUP, AMDSMI_STATUS_NO_HSMP_MSG_SUP},
};

amdsmi_status_t esmi_to_amdsmi_status(esmi_status_t status);
#endif

// Define a map of smi nic status codes to amdsmi status codes
const std::map<smi_nic_status_t, amdsmi_status_t> ainic_status_map = {
    {SMI_NIC_STATUS_SUCCESS, AMDSMI_STATUS_SUCCESS},
    {SMI_NIC_STATUS_ERROR, AMDSMI_STATUS_API_FAILED},
    {SMI_NIC_STATUS_WRONG_PARAM, AMDSMI_STATUS_INVAL},
    {SMI_NIC_STATUS_NOT_FOUND, AMDSMI_STATUS_NOT_FOUND},
    {SMI_NIC_STATUS_NO_RESOURCE, AMDSMI_STATUS_OUT_OF_RESOURCES},
    {SMI_NIC_STATUS_NOT_SUPPORTED, AMDSMI_STATUS_NOT_YET_IMPLEMENTED},
    {SMI_NIC_STATUS_NOT_INIT, AMDSMI_STATUS_NOT_INIT},
    {SMI_NIC_STATUS_NO_DATA, AMDSMI_STATUS_NO_DATA},
    {SMI_NIC_STATUS_DRIVER_NOT_LOADED, AMDSMI_STATUS_DRIVER_NOT_LOADED}};
amdsmi_status_t ainic_to_amdsmi_status(smi_nic_status_t status);

/**
 *  AMDSMI Library init reference count (amdsmi_init / amdsmi_shut_down)
 *      - Lives in amd_smi_common.cc
 */
bool amdsmi_library_initialized();
void amdsmi_library_init_ref_acquire();

/**
 *  AMDSMI Decrements init ref; should run (count reached zero).
 *      - Returns true if AMDSmiSystem::cleanup()
 *
 */

bool amdsmi_library_init_ref_release();

}  // namespace amd::smi

// Verifies AMD SMI is initialized; returns AMDSMI_STATUS_NOT_INIT from the enclosing function.
#ifndef AMDSMI_CHECK_INIT
#define AMDSMI_CHECK_INIT()                        \
  do {                                             \
    if (!amd::smi::amdsmi_library_initialized()) { \
      return AMDSMI_STATUS_NOT_INIT;               \
    }                                              \
  } while (0)
#endif

#endif  // AMD_SMI_INCLUDE_AMD_SMI_COMMON_H_
