//
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef BOOST_MYSQL_DETAIL_TYPING_POS_MAP_HPP
#define BOOST_MYSQL_DETAIL_TYPING_POS_MAP_HPP

#include <boost/mysql/field_view.hpp>
#include <boost/mysql/metadata.hpp>
#include <boost/mysql/metadata_collection_view.hpp>
#include <boost/mysql/string_view.hpp>

#include <boost/assert.hpp>
#include <boost/core/span.hpp>

#include <cstddef>

namespace boost {
namespace mysql {
namespace detail {

// These functions map C++ type positions to positions to positions in the DB query

constexpr std::size_t pos_absent = static_cast<std::size_t>(-1);
using name_table_t = boost::span<const string_view>;

inline bool has_field_names(name_table_t name_table) noexcept { return !name_table.empty(); }

inline void pos_map_reset(span<std::size_t> self) noexcept
{
    for (std::size_t i = 0; i < self.size(); ++i)
        self.data()[i] = pos_absent;
}

inline void pos_map_add_field(
    span<std::size_t> self,
    name_table_t name_table,
    std::size_t db_index,
    string_view field_name
) noexcept
{
    if (has_field_names(name_table))
    {
        BOOST_ASSERT(self.size() == name_table.size());

        // We're mapping fields by name. Try to find where in our target struct
        // is the current field located
        auto it = std::find(name_table.begin(), name_table.end(), field_name);
        if (it != name_table.end())
        {
            std::size_t cpp_index = it - name_table.begin();
            self[cpp_index] = db_index;
        }
    }
    else
    {
        // We're mapping by position. Any extra trailing fields are discarded
        if (db_index < self.size())
        {
            self[db_index] = db_index;
        }
    }
}

inline field_view map_field_view(
    span<const std::size_t> self,
    std::size_t cpp_index,
    span<const field_view> array
) noexcept
{
    BOOST_ASSERT(cpp_index < self.size());
    return array[self[cpp_index]];
}

inline const metadata& map_metadata(
    span<const std::size_t> self,
    std::size_t cpp_index,
    metadata_collection_view meta
) noexcept
{
    BOOST_ASSERT(cpp_index < self.size());
    return meta[self[cpp_index]];
}

}  // namespace detail
}  // namespace mysql
}  // namespace boost

#endif
