#pragma once

#include <algorithm>
#include <array>
#include <sbe_interfaces.hpp>
#include <sstream>
#include <stdexcept>
#include <vector>

namespace openpower
{
namespace sbe
{

using sbe_word_t = uint32_t;

namespace internal
{

/**
 * @brief Helper function for invokeSBEChipOperation(),to write to the SBE FIFO
 * device and obtain the expected response .Internal device driver failures
 * will be conveyed via respective exceptions.
 *
 * Exceptions thrown for:
 * - Device driver internal failures
 *
 * @param[in] FIFO device path associated with SBE.
 * @param[in] Command buffer to be written to the SBE FIFO
 * @param[in] Length of command buffer
 * @param[in] Expected response buffer length
 *
 * @return Response buffer returned by the SBE for the input command.
 */
std::vector<sbe_word_t> writeToFifo(const char* devPath,
                                    const sbe_word_t* cmdBuffer,
                                    size_t cmdBufLen, size_t respBufLen);

/**
 * @brief Helper function for invokeSBEChipOperation(), to parse and validate
 * the data obtained from the SBE. Input buffer will be validated and on failure
 * the FFDC content will be extracted and returned to the caller via
 * respective exception. On success the input buffer will be modified to have
 * only valid response data after removing the header content.
 *
 * Exceptions thrown for:
 * - SBE Internal failures
 *
 * @param[in/out] On input  - SBE data obtained from the SBE FIFO device.
 *                On output - Chip operation data after removing the response
 *                            header.
 */
void parseResponse(std::vector<sbe_word_t>& sbeDataBuf);

} // namespace internal

/**
 * @brief Interface to invoke a SBE chip operation.It calls internal API to
 * write to the SBE FIFO and validates the data obtained by the SBE. It throws
 * exception for any SBE internal failures.
 *
 * Runtime exceptions thrown for:
 * - Device driver failures
 * - SBE internal failures
 *
 * @param[in] FIFO device path associated with the SBE.
 * @param[in] Request packet for the data to be read.
 * @param[in] Data obtained by the SBE.
 * @tparam S1 Length of request buffer to be send to SBE
 * @tparam S2 Expected length of data from the SBE
 */
template <size_t S1, size_t S2>
inline void invokeSBEChipOperation(const char* devPath,
                                   const std::array<sbe_word_t, S1>& request,
                                   std::array<sbe_word_t, S2>& chipOpData)
{
    // Write and read from the FIFO device.
    auto sbeFifoResp = internal::writeToFifo(devPath, request.data(),
                                             request.size(), chipOpData.size());

    // Parse the obtained data
    internal::parseResponse(sbeFifoResp);
    // Above interface would have stripped the SBE header content from the input
    // response buffer.
    if (sbeFifoResp.size() > chipOpData.size())
    {
        // TODO:use elog infrastructure
        std::ostringstream errMsg;
        errMsg << "Obtained chip operation response length ("
               << sbeFifoResp.size()
               << "from SBE is greater than maximum expected"
                  " length:"
               << chipOpData.size();

        throw std::runtime_error(errMsg.str().c_str());
    }

    // Move the contents of response buffer into the output buffer.
    std::move(sbeFifoResp.begin(), sbeFifoResp.end(), chipOpData.begin());
}

} // namespace sbe
} // namespace openpower