#include "bios.hpp"

#include "libpldmresponder/utils.hpp"
#include "registration.hpp"
#include "xyz/openbmc_project/Common/error.hpp"

#include <array>
#include <boost/crc.hpp>
#include <chrono>
#include <ctime>
#include <memory>
#include <numeric>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <stdexcept>
#include <string>
#include <variant>
#include <vector>

using namespace pldm::responder::bios;
using namespace bios_parser;

namespace pldm
{

using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
using EpochTimeUS = uint64_t;
using BIOSTableRow = std::vector<uint8_t>;
using BIOSJsonName = std::string;

constexpr auto dbusProperties = "org.freedesktop.DBus.Properties";
constexpr auto padChksumMax = 7;

namespace responder
{

namespace utils
{

void epochToBCDTime(uint64_t timeSec, uint8_t& seconds, uint8_t& minutes,
                    uint8_t& hours, uint8_t& day, uint8_t& month,
                    uint16_t& year)
{
    auto t = time_t(timeSec);
    auto time = localtime(&t);

    seconds = decimalToBcd(time->tm_sec);
    minutes = decimalToBcd(time->tm_min);
    hours = decimalToBcd(time->tm_hour);
    day = decimalToBcd(time->tm_mday);
    month =
        decimalToBcd(time->tm_mon + 1); // The number of months in the range
                                        // 0 to 11.PLDM expects range 1 to 12
    year = decimalToBcd(time->tm_year + 1900); // The number of years since 1900
}

size_t getTableTotalsize(size_t sizeWithoutPad)
{
    auto padSize = getNumPadBytes(sizeWithoutPad);
    return sizeWithoutPad + padSize + sizeof(uint32_t) /* checksum */;
}

void padAndChecksum(Table& table)
{
    auto padSize = getNumPadBytes(table.size());
    table.insert(table.end(), padSize, 0);

    boost::crc_32_type result;
    size_t size = table.size();
    result.process_bytes(table.data(), size);
    uint32_t checkSum = result.checksum();
    uint8_t* checkSumPtr = reinterpret_cast<uint8_t*>(&checkSum);
    table.insert(table.end(), checkSumPtr, checkSumPtr + sizeof(checkSum));
}

} // namespace utils

Response getDateTime(const pldm_msg* request, size_t /*payloadLength*/)
{
    uint8_t seconds = 0;
    uint8_t minutes = 0;
    uint8_t hours = 0;
    uint8_t day = 0;
    uint8_t month = 0;
    uint16_t year = 0;

    constexpr auto timeInterface = "xyz.openbmc_project.Time.EpochTime";
    constexpr auto hostTimePath = "/xyz/openbmc_project/time/host";
    Response response(sizeof(pldm_msg_hdr) + PLDM_GET_DATE_TIME_RESP_BYTES, 0);
    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
    std::variant<EpochTimeUS> value;

    auto bus = sdbusplus::bus::new_default();
    try
    {
        auto service = getService(bus, hostTimePath, timeInterface);

        auto method = bus.new_method_call(service.c_str(), hostTimePath,
                                          dbusProperties, "Get");
        method.append(timeInterface, "Elapsed");

        auto reply = bus.call(method);
        reply.read(value);
    }

    catch (std::exception& e)
    {
        log<level::ERR>("Error getting time", entry("PATH=%s", hostTimePath),
                        entry("TIME INTERACE=%s", timeInterface));

        encode_get_date_time_resp(request->hdr.instance_id, PLDM_ERROR, seconds,
                                  minutes, hours, day, month, year,
                                  responsePtr);
        return response;
    }

    uint64_t timeUsec = std::get<EpochTimeUS>(value);

    uint64_t timeSec = std::chrono::duration_cast<std::chrono::seconds>(
                           std::chrono::microseconds(timeUsec))
                           .count();

    utils::epochToBCDTime(timeSec, seconds, minutes, hours, day, month, year);

    encode_get_date_time_resp(request->hdr.instance_id, PLDM_SUCCESS, seconds,
                              minutes, hours, day, month, year, responsePtr);
    return response;
}

/** @brief Construct the BIOS string table
 *
 *  @param[in] BIOSStringTable - the string table
 *  @param[in] transferHandle - transfer handle to identify part of transfer
 *  @param[in] transferOpFlag - flag to indicate which part of data being
 * transferred
 *  @param[in] instanceID - instance ID to identify the command
 */
Response getBIOSStringTable(BIOSTable& BIOSStringTable,
                            uint32_t /*transferHandle*/,
                            uint8_t /*transferOpFlag*/, uint8_t instanceID)

{
    Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
                      0);
    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
    if (!BIOSStringTable.isEmpty())
    {
        encode_get_bios_table_resp(instanceID, PLDM_SUCCESS,
                                   0, /* next transfer handle */
                                   PLDM_START_AND_END, nullptr, response.size(),
                                   responsePtr); // filling up the header here
        BIOSStringTable.load(response);
        return response;
    }
    auto biosStrings = bios_parser::getStrings();
    std::sort(biosStrings.begin(), biosStrings.end());
    // remove all duplicate strings received from bios json
    biosStrings.erase(std::unique(biosStrings.begin(), biosStrings.end()),
                      biosStrings.end());

    size_t sizeWithoutPad = std::accumulate(
        biosStrings.begin(), biosStrings.end(), 0,
        [](size_t sum, const std::string& elem) {
            return sum +
                   pldm_bios_table_string_entry_encode_length(elem.length());
        });

    Table stringTable;
    stringTable.reserve(utils::getTableTotalsize(sizeWithoutPad));

    stringTable.resize(sizeWithoutPad);
    auto tablePtr = stringTable.data();
    for (const auto& elem : biosStrings)
    {
        auto entry_length =
            pldm_bios_table_string_entry_encode_length(elem.length());
        pldm_bios_table_string_entry_encode(tablePtr, sizeWithoutPad,
                                            elem.c_str(), elem.length());
        tablePtr += entry_length;
        sizeWithoutPad -= entry_length;
    }

    utils::padAndChecksum(stringTable);
    BIOSStringTable.store(stringTable);
    response.resize(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
                        stringTable.size(),
                    0);
    responsePtr = reinterpret_cast<pldm_msg*>(response.data());
    encode_get_bios_table_resp(
        instanceID, PLDM_SUCCESS, 0 /* nxtTransferHandle */, PLDM_START_AND_END,
        stringTable.data(), response.size(), responsePtr);
    return response;
}

/** @brief Find the string handle from the BIOS string table given the name
 *
 *  @param[in] name - name of the BIOS string
 *  @param[in] BIOSStringTable - the string table
 *  @return - uint16_t - handle of the string
 */
StringHandle findStringHandle(const std::string& name,
                              const BIOSTable& BIOSStringTable)
{
    Table table;
    BIOSStringTable.load(table);
    auto stringEntry = pldm_bios_table_string_find_by_string(
        table.data(), table.size(), name.c_str());
    if (stringEntry == nullptr)
    {
        log<level::ERR>("Reached end of BIOS string table,did not find the "
                        "handle for the string",
                        entry("STRING=%s", name.c_str()));
        elog<InternalFailure>();
    }

    return pldm_bios_table_string_entry_decode_handle(stringEntry);
}

/** @brief Find the string name from the BIOS string table for a string handle
 *
 *  @param[in] stringHdl - string handle
 *  @param[in] BIOSStringTable - the string table
 *
 *  @return - std::string - name of the corresponding BIOS string
 */
std::string findStringName(StringHandle stringHdl,
                           const BIOSTable& BIOSStringTable)
{
    Table table;
    BIOSStringTable.load(table);
    auto stringEntry = pldm_bios_table_string_find_by_handle(
        table.data(), table.size(), stringHdl);
    std::string name;
    if (stringEntry == nullptr)
    {
        log<level::ERR>("Reached end of BIOS string table,did not find "
                        "string name for handle",
                        entry("STRING_HANDLE=%d", stringHdl));
    }
    auto strLength =
        pldm_bios_table_string_entry_decode_string_length(stringEntry);
    name.resize(strLength);
    pldm_bios_table_string_entry_decode_string(stringEntry, name.data(),
                                               name.size());
    return name;
}

namespace bios_type_enum
{

using namespace bios_parser::bios_enum;

/** @brief Find the indices  into the array of the possible values of string
 *  handles for the current values.This is used in attribute value table
 *
 *  @param[in] possiVals - vector of string handles comprising all the possible
 *                         values for an attribute
 *  @param[in] currVals - vector of strings comprising all current values
 *                        for an attribute
 *  @param[in] BIOSStringTable - the string table
 *
 *  @return - std::vector<uint8_t> - indices into the array of the possible
 *                                   values of string handles
 */
std::vector<uint8_t> findStrIndices(PossibleValuesByHandle possiVals,
                                    CurrentValues currVals,
                                    const BIOSTable& BIOSStringTable)
{
    std::vector<uint8_t> stringIndices;

    for (const auto& currVal : currVals)
    {
        StringHandle curHdl;
        try
        {
            curHdl = findStringHandle(currVal, BIOSStringTable);
        }
        catch (InternalFailure& e)
        {
            log<level::ERR>("Exception fetching handle for the string",
                            entry("STRING=%s", currVal.c_str()));
            continue;
        }

        uint8_t i = 0;
        for (auto possiHdl : possiVals)
        {
            if (possiHdl == curHdl)
            {
                stringIndices.push_back(i);
                break;
            }
            i++;
        }
    }
    return stringIndices;
}

/** @brief Find the indices into the array of the possible values of string
 *  handles for the default values. This is used in attribute table
 *
 *  @param[in] possiVals - vector of strings comprising all the possible values
 *                         for an attribute
 *  @param[in] defVals - vector of strings comprising all the default values
 *                       for an attribute
 *  @return - std::vector<uint8_t> - indices into the array of the possible
 *                                   values of string
 */
std::vector<uint8_t> findDefaultValHandle(const PossibleValues& possiVals,
                                          const DefaultValues& defVals)
{
    std::vector<uint8_t> defHdls;
    for (const auto& defs : defVals)
    {
        auto index = std::lower_bound(possiVals.begin(), possiVals.end(), defs);
        if (index != possiVals.end())
        {
            defHdls.push_back(index - possiVals.begin());
        }
    }

    return defHdls;
}

/** @brief Construct the attibute table for BIOS type Enumeration and
 *         Enumeration ReadOnly
 *  @param[in] BIOSStringTable - the string table
 *  @param[in] biosJsonDir - path where the BIOS json files are present
 *  @param[in,out] attributeTable - the attribute table
 *
 */
void constructAttrTable(const BIOSTable& BIOSStringTable, Table& attributeTable)
{
    const auto& attributeMap = getValues();
    StringHandle strHandle;

    for (const auto& [key, value] : attributeMap)
    {
        try
        {
            strHandle = findStringHandle(key, BIOSStringTable);
        }
        catch (InternalFailure& e)
        {
            log<level::ERR>("Could not find handle for BIOS string",
                            entry("ATTRIBUTE=%s", key.c_str()));
            continue;
        }
        bool readOnly = (std::get<0>(value));
        PossibleValues possiVals = std::get<1>(value);
        DefaultValues defVals = std::get<2>(value);
        // both the possible and default values are stored in sorted manner to
        // ease in fetching back/comparison
        std::sort(possiVals.begin(), possiVals.end());
        std::sort(defVals.begin(), defVals.end());

        std::vector<StringHandle> possiValsByHdl;
        for (const auto& elem : possiVals)
        {
            try
            {
                auto hdl = findStringHandle(elem, BIOSStringTable);
                possiValsByHdl.push_back(std::move(hdl));
            }
            catch (InternalFailure& e)
            {
                log<level::ERR>("Could not find handle for BIOS string",
                                entry("STRING=%s", elem.c_str()));
                continue;
            }
        }
        auto defValsByHdl = findDefaultValHandle(possiVals, defVals);
        auto entryLength = pldm_bios_table_attr_entry_enum_encode_length(
            possiValsByHdl.size(), defValsByHdl.size());

        auto attrTableSize = attributeTable.size();
        attributeTable.resize(attrTableSize + entryLength, 0);
        struct pldm_bios_table_attr_entry_enum_info info = {
            strHandle,
            readOnly,
            (uint8_t)possiValsByHdl.size(),
            possiValsByHdl.data(),
            (uint8_t)defValsByHdl.size(),
            defValsByHdl.data(),
        };
        pldm_bios_table_attr_entry_enum_encode(
            attributeTable.data() + attrTableSize, entryLength, &info);
    }
}

void constructAttrValueEntry(
    const struct pldm_bios_attr_table_entry* attrTableEntry,
    const std::string& attrName, const BIOSTable& BIOSStringTable,
    Table& attrValueTable)
{
    CurrentValues currVals;
    try
    {
        currVals = getAttrValue(attrName);
    }
    catch (const std::exception& e)
    {
        log<level::ERR>("getAttrValue returned error for attribute",
                        entry("NAME=%s", attrName.c_str()),
                        entry("ERROR=%s", e.what()));
        return;
    }
    uint8_t pv_num =
        pldm_bios_table_attr_entry_enum_decode_pv_num(attrTableEntry);
    PossibleValuesByHandle pvHdls(pv_num, 0);
    pldm_bios_table_attr_entry_enum_decode_pv_hdls(attrTableEntry,
                                                   pvHdls.data(), pv_num);
    std::sort(currVals.begin(), currVals.end());

    auto currValStrIndices = findStrIndices(pvHdls, currVals, BIOSStringTable);

    auto entryLength = pldm_bios_table_attr_value_entry_encode_enum_length(
        currValStrIndices.size());
    auto tableSize = attrValueTable.size();
    attrValueTable.resize(tableSize + entryLength);
    pldm_bios_table_attr_value_entry_encode_enum(
        attrValueTable.data() + tableSize, entryLength,
        attrTableEntry->attr_handle, attrTableEntry->attr_type,
        currValStrIndices.size(), currValStrIndices.data());
}

} // end namespace bios_type_enum

namespace bios_type_string
{

using namespace bios_parser::bios_string;

/** @brief Construct the attibute table for BIOS type String and
 *         String ReadOnly
 *  @param[in] BIOSStringTable - the string table
 *  @param[in] biosJsonDir - path where the BIOS json files are present
 *  @param[in,out] attributeTable - the attribute table
 *
 */
void constructAttrTable(const BIOSTable& BIOSStringTable, Table& attributeTable)
{
    const auto& attributeMap = getValues();
    StringHandle strHandle;
    for (const auto& [key, value] : attributeMap)
    {
        try
        {
            strHandle = findStringHandle(key, BIOSStringTable);
        }
        catch (InternalFailure& e)
        {
            log<level::ERR>("Could not find handle for BIOS string",
                            entry("ATTRIBUTE=%s", key.c_str()));
            continue;
        }

        const auto& [readOnly, strType, minStrLen, maxStrLen, defaultStrLen,
                     defaultStr] = value;
        auto entryLength =
            pldm_bios_table_attr_entry_string_encode_length(defaultStrLen);

        struct pldm_bios_table_attr_entry_string_info info = {
            strHandle, readOnly,      strType,           minStrLen,
            maxStrLen, defaultStrLen, defaultStr.data(),
        };
        auto attrTableSize = attributeTable.size();
        attributeTable.resize(attrTableSize + entryLength, 0);
        pldm_bios_table_attr_entry_string_encode(
            attributeTable.data() + attrTableSize, entryLength, &info);
    }
}

void constructAttrValueEntry(const pldm_bios_attr_table_entry* attrTableEntry,
                             const std::string& attrName,
                             const BIOSTable& BIOSStringTable,
                             Table& attrValueTable)
{
    std::ignore = BIOSStringTable;
    std::string currStr;
    uint16_t currStrLen = 0;
    try
    {
        currStr = getAttrValue(attrName);
        currStrLen = currStr.size();
    }
    catch (const std::exception& e)
    {
        log<level::ERR>("getAttrValue returned error for attribute",
                        entry("NAME=%s", attrName.c_str()),
                        entry("ERROR=%s", e.what()));
        return;
    }
    auto entryLength =
        pldm_bios_table_attr_value_entry_encode_string_length(currStrLen);
    auto tableSize = attrValueTable.size();
    attrValueTable.resize(tableSize + entryLength);
    pldm_bios_table_attr_value_entry_encode_string(
        attrValueTable.data() + tableSize, entryLength,
        attrTableEntry->attr_handle, attrTableEntry->attr_type, currStrLen,
        currStr.c_str());
}

} // end namespace bios_type_string

void traverseBIOSAttrTable(const Table& biosAttrTable,
                           AttrTableEntryHandler handler)
{
    std::unique_ptr<pldm_bios_table_iter, decltype(&pldm_bios_table_iter_free)>
        iter(pldm_bios_table_iter_create(biosAttrTable.data(),
                                         biosAttrTable.size(),
                                         PLDM_BIOS_ATTR_TABLE),
             pldm_bios_table_iter_free);
    while (!pldm_bios_table_iter_is_end(iter.get()))
    {
        auto table_entry = pldm_bios_table_iter_attr_entry_value(iter.get());
        try
        {
            handler(table_entry);
        }
        catch (const std::exception& e)
        {
            log<level::ERR>("handler fails when traversing BIOSAttrTable",
                            entry("ERROR=%s", e.what()));
        }
        pldm_bios_table_iter_next(iter.get());
    }
}

using typeHandler = std::function<void(const BIOSTable& BIOSStringTable,
                                       Table& attributeTable)>;
std::map<BIOSJsonName, typeHandler> attrTypeHandlers{
    {bios_parser::bIOSEnumJson, bios_type_enum::constructAttrTable},
    {bios_parser::bIOSStrJson, bios_type_string::constructAttrTable}};

/** @brief Construct the BIOS attribute table
 *
 *  @param[in] BIOSAttributeTable - the attribute table
 *  @param[in] BIOSStringTable - the string table
 *  @param[in] transferHandle - transfer handle to identify part of transfer
 *  @param[in] transferOpFlag - flag to indicate which part of data being
 * transferred
 *  @param[in] instanceID - instance ID to identify the command
 *  @param[in] biosJsonDir - path where the BIOS json files are present
 */
Response getBIOSAttributeTable(BIOSTable& BIOSAttributeTable,
                               const BIOSTable& BIOSStringTable,
                               uint32_t /*transferHandle*/,
                               uint8_t /*transferOpFlag*/, uint8_t instanceID,
                               const char* biosJsonDir)
{
    Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
                      0);
    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
    uint32_t nxtTransferHandle = 0;
    uint8_t transferFlag = PLDM_START_AND_END;

    if (BIOSAttributeTable.isEmpty())
    { // no persisted table, constructing fresh table and response
        Table attributeTable;
        fs::path dir(biosJsonDir);

        for (auto it = attrTypeHandlers.begin(); it != attrTypeHandlers.end();
             it++)
        {
            fs::path file = dir / it->first;
            if (fs::exists(file))
            {
                it->second(BIOSStringTable, attributeTable);
            }
        }

        if (attributeTable.empty())
        { // no available json file is found
            encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE,
                                       nxtTransferHandle, transferFlag, nullptr,
                                       response.size(), responsePtr);
            return response;
        }
        utils::padAndChecksum(attributeTable);
        BIOSAttributeTable.store(attributeTable);
        response.resize(sizeof(pldm_msg_hdr) +
                        PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
                        attributeTable.size());
        responsePtr = reinterpret_cast<pldm_msg*>(response.data());
        encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
                                   transferFlag, attributeTable.data(),
                                   response.size(), responsePtr);
    }
    else
    { // persisted table present, constructing response
        encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
                                   transferFlag, nullptr, response.size(),
                                   responsePtr); // filling up the header here
        BIOSAttributeTable.load(response);
    }

    return response;
}

using AttrValTableEntryConstructHandler =
    std::function<void(const struct pldm_bios_attr_table_entry* tableEntry,
                       const std::string& attrName,
                       const BIOSTable& BIOSStringTable, Table& table)>;

using AttrType = uint8_t;
const std::map<AttrType, AttrValTableEntryConstructHandler>
    AttrValTableConstructMap{
        {PLDM_BIOS_STRING, bios_type_string::constructAttrValueEntry},
        {PLDM_BIOS_STRING_READ_ONLY, bios_type_string::constructAttrValueEntry},
        {PLDM_BIOS_ENUMERATION, bios_type_enum::constructAttrValueEntry},
        {PLDM_BIOS_ENUMERATION_READ_ONLY,
         bios_type_enum::constructAttrValueEntry},
    };

void constructAttrValueTableEntry(
    const struct pldm_bios_attr_table_entry* attrEntry,
    const BIOSTable& BIOSStringTable, Table& attributeValueTable)
{
    auto attrName = findStringName(attrEntry->string_handle, BIOSStringTable);
    if (attrName.empty())
    {
        log<level::ERR>("invalid string handle",
                        entry("STRING_HANDLE=%d", attrEntry->string_handle));
        return;
    }

    AttrValTableConstructMap.at(attrEntry->attr_type)(
        attrEntry, attrName, BIOSStringTable, attributeValueTable);
}

/** @brief Construct the BIOS attribute value table
 *
 *  @param[in] BIOSAttributeValueTable - the attribute value table
 *  @param[in] BIOSAttributeTable - the attribute table
 *  @param[in] BIOSStringTable - the string table
 *  @param[in] transferHandle - transfer handle to identify part of transfer
 *  @param[in] transferOpFlag - flag to indicate which part of data being
 * transferred
 *  @param[in] instanceID - instance ID to identify the command
 */
Response getBIOSAttributeValueTable(BIOSTable& BIOSAttributeValueTable,
                                    const BIOSTable& BIOSAttributeTable,
                                    const BIOSTable& BIOSStringTable,
                                    uint32_t& /*transferHandle*/,
                                    uint8_t& /*transferOpFlag*/,
                                    uint8_t instanceID)
{
    Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
                      0);
    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
    uint32_t nxtTransferHandle = 0;
    uint8_t transferFlag = PLDM_START_AND_END;

    if (!BIOSAttributeValueTable.isEmpty())
    {
        encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
                                   transferFlag, nullptr, response.size(),
                                   responsePtr); // filling up the header here
        BIOSAttributeValueTable.load(response);
        return response;
    }

    Table attributeValueTable;
    Table attributeTable;
    BIOSAttributeTable.load(attributeTable);
    traverseBIOSAttrTable(
        attributeTable,
        [&BIOSStringTable, &attributeValueTable](
            const struct pldm_bios_attr_table_entry* tableEntry) {
            constructAttrValueTableEntry(tableEntry, BIOSStringTable,
                                         attributeValueTable);
        });
    if (attributeValueTable.empty())
    {
        encode_get_bios_table_resp(instanceID, PLDM_BIOS_TABLE_UNAVAILABLE,
                                   nxtTransferHandle, transferFlag, nullptr,
                                   response.size(), responsePtr);
        return response;
    }
    utils::padAndChecksum(attributeValueTable);
    BIOSAttributeValueTable.store(attributeValueTable);

    response.resize(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES +
                    attributeValueTable.size());
    responsePtr = reinterpret_cast<pldm_msg*>(response.data());
    encode_get_bios_table_resp(instanceID, PLDM_SUCCESS, nxtTransferHandle,
                               transferFlag, attributeValueTable.data(),
                               response.size(), responsePtr);

    return response;
}

Response getBIOSTable(const pldm_msg* request, size_t payloadLength)
{
    fs::create_directory(BIOS_TABLES_DIR);
    auto response = internal::buildBIOSTables(request, payloadLength,
                                              BIOS_JSONS_DIR, BIOS_TABLES_DIR);

    return response;
}

namespace bios
{

void registerHandlers()
{
    registerHandler(PLDM_BIOS, PLDM_GET_DATE_TIME, std::move(getDateTime));
    registerHandler(PLDM_BIOS, PLDM_GET_BIOS_TABLE, std::move(getBIOSTable));
}

namespace internal
{

Response buildBIOSTables(const pldm_msg* request, size_t payloadLength,
                         const char* biosJsonDir, const char* biosTablePath)
{
    Response response(sizeof(pldm_msg_hdr) + PLDM_GET_BIOS_TABLE_MIN_RESP_BYTES,
                      0);
    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());

    if (setupConfig(biosJsonDir) != 0)
    {
        encode_get_bios_table_resp(
            request->hdr.instance_id, PLDM_BIOS_TABLE_UNAVAILABLE,
            0 /* nxtTransferHandle */, PLDM_START_AND_END, nullptr,
            response.size(), responsePtr);
        return response;
    }

    uint32_t transferHandle{};
    uint8_t transferOpFlag{};
    uint8_t tableType{};

    auto rc = decode_get_bios_table_req(request, payloadLength, &transferHandle,
                                        &transferOpFlag, &tableType);
    if (rc == PLDM_SUCCESS)
    {
        BIOSTable BIOSStringTable(
            ((std::string(biosTablePath) + "/stringTable")).c_str());
        BIOSTable BIOSAttributeTable(
            ((std::string(biosTablePath) + "/attributeTable")).c_str());
        BIOSTable BIOSAttributeValueTable(
            ((std::string(biosTablePath) + "/attributeValueTable")).c_str());
        switch (tableType)
        {
            case PLDM_BIOS_STRING_TABLE:

                response = getBIOSStringTable(BIOSStringTable, transferHandle,
                                              transferOpFlag,
                                              request->hdr.instance_id);
                break;
            case PLDM_BIOS_ATTR_TABLE:

                if (BIOSStringTable.isEmpty())
                {
                    rc = PLDM_BIOS_TABLE_UNAVAILABLE;
                }
                else
                {
                    response = getBIOSAttributeTable(
                        BIOSAttributeTable, BIOSStringTable, transferHandle,
                        transferOpFlag, request->hdr.instance_id, biosJsonDir);
                }
                break;
            case PLDM_BIOS_ATTR_VAL_TABLE:
                if (BIOSAttributeTable.isEmpty())
                {
                    rc = PLDM_BIOS_TABLE_UNAVAILABLE;
                }
                else
                {
                    response = getBIOSAttributeValueTable(
                        BIOSAttributeValueTable, BIOSAttributeTable,
                        BIOSStringTable, transferHandle, transferOpFlag,
                        request->hdr.instance_id);
                }
                break;
            default:
                rc = PLDM_INVALID_BIOS_TABLE_TYPE;
                break;
        }
    }

    if (rc != PLDM_SUCCESS)
    {
        uint32_t nxtTransferHandle{};
        uint8_t transferFlag{};
        size_t respPayloadLength{};

        encode_get_bios_table_resp(request->hdr.instance_id, rc,
                                   nxtTransferHandle, transferFlag, nullptr,
                                   respPayloadLength, responsePtr);
    }

    return response;
}

} // end namespace internal
} // namespace bios

} // namespace responder
} // namespace pldm