#include "http_headers.h"
#include "tile/base/logging.h"

namespace tile {
HttpHeaders::HttpHeaders(const HttpHeaders &other) : header_idx_()
{
    for (auto &&item : other) { Append(item.first.ToString(), item.second.ToString()); }
}

HttpHeaders &
HttpHeaders::operator=(const HttpHeaders &other)
{
    if (&other == this) { return *this; }
    this->~HttpHeaders();
    new (this) HttpHeaders(other);
    return *this;
}

void
HttpHeaders::clear() noexcept
{
    header_block_.first.reset();
    header_block_.second = 0;
    owning_strs_2_.clear();
    header_idx_.clear();
    fields_.clear();
}

std::vector<Slice>
HttpHeaders::TryGetMultiple(Slice key) const noexcept
{
    std::vector<Slice> values;
    for (auto &&item : fields_) {
        // I really shouldn't have done this.
        if (EqualsIgnoreCase(item.first, key)) { values.push_back(item.second); }
    }
    return values;
}

void
HttpHeaders::Set(std::string key, std::string value) noexcept
{
    TILE_CHECK_EQ(Trim(key), key, "Field key may not be surrounded by whitespaces.");
    // To be precise, surrounding `value` with whitespace is not an error, it
    // just does not make much sense.
    //
    // FIXME: Should we trim `value` automatically?
    TILE_CHECK_EQ(Trim(value), value,
                  "There's hardly any point in surrounding field value with "
                  "whitespaces.");

    auto iter = header_idx_.find(key);
    if (iter == header_idx_.end()) {
        Append(std::move(key), std::move(value));
    } else {
        owning_strs_2_.emplace_back(std::move(value));
        fields_[iter->second].second = owning_strs_2_.back();
    }
}

void
HttpHeaders::Append(std::string key, std::string value) noexcept
{
    TILE_CHECK_EQ(Trim(key), key, "Field key may not be surrounded by whitespaces.");
    // To be precise, surrounding `value` with whitespace is not an error, it
    // just does not make much sense.
    //
    // FIXME: Should we trim `value` automatically?
    TILE_CHECK_EQ(Trim(value), value,
                  "There's hardly any point in surrounding field value with "
                  "whitespaces.");
    owning_strs_2_.emplace_back(std::move(key));
    Slice k = owning_strs_2_.back();
    owning_strs_2_.emplace_back(std::move(value));
    Slice v = owning_strs_2_.back();

    if (header_idx_.find(k) == header_idx_.end()) { header_idx_[k] = fields_.size(); }
    fields_.emplace_back(k, v);
}

void
HttpHeaders::Append(std::initializer_list<std::pair<Slice, Slice>> fields) noexcept
{
    for (auto &&item : fields) { Append(item.first.ToString(), item.second.ToString()); }
}

bool
HttpHeaders::Remove(Slice key) noexcept
{
    if (!TryGet(key)) { return false; }

    header_idx_.clear();
    std::size_t i = 0;
    auto iter     = fields_.begin();
    while (iter != fields_.end()) {
        if (internal::detail::CaseInsensitiveEqualTo()(iter->first, key)) {
            iter = fields_.erase(iter);
        } else {
            if (header_idx_.find(iter->first) == header_idx_.end()) { header_idx_[iter->first] = i; }
            ++i;
            ++iter;
        }
    }

    return true;
}

std::string
HttpHeaders::ToString() const
{
    std::string result;
    for (auto &&item : fields_) {
        result.append(item.first.data(), item.first.size());
        result.append(": ");
        result.append(item.second.data(), item.second.size());
        result.append("\r\n");
    }
    return result;
}

Slice
HttpHeaders::RetrieveHeaderStorage(std::pair<std::unique_ptr<char[]>, std::size_t> &&s)
{
    owning_strs_2_.clear();
    header_idx_.clear();
    header_block_ = std::move(s);
    return Slice(header_block_.first.get(), header_block_.second);
}

void
HttpHeaders::RetrieveFields(NonowningFields &&fields)
{
    fields_ = std::move(fields);
    for (std::size_t index = 0; index != fields_.size(); ++index) {
        auto &&key = fields_[index].first;

        if (header_idx_.find(key) == header_idx_.end()) { header_idx_[key] = index; }
    }
}
}// namespace tile
