#include "tile/base/option/key.h"

#include "tile/base/string.h"

#include <mutex>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

namespace tile {
namespace option {
namespace {
std::mutex dynamic_key_store_lock;
std::unordered_map<std::string, std::string> dynamic_key_store;
}// namespace

Key::Key(std::string name) : Key(FixedKey(std::move(name))) {}

std::string
Key::Get() const
{
    return impl_->Get();
}

bool
Key::Ready() const noexcept
{
    return impl_->Ready();
}

MultiKey::MultiKey(MultiKey &&first, Key &&second) : keys_(std::move(first.keys_))
{
    keys_.push_back(std::move(second));
}

MultiKey::MultiKey(MultiKey &&first, MultiKey &&second) : keys_(std::move(first.keys_))
{
    for (auto &key : second.keys_) { keys_.push_back(std::move(key)); }
}

MultiKey::MultiKey(std::string name) : MultiKey(Key(std::move(name))) {}

MultiKey::MultiKey(const char *name) : MultiKey(std::string(name)) {}

const std::vector<Key> &
MultiKey::GetKeys() const noexcept
{
    return keys_;
}

bool
MultiKey::Ready() const noexcept
{
    return std::all_of(keys_.begin(), keys_.end(), [](const Key &e) { return e.Ready(); });
}

std::string
MultiKey::ToString() const
{
    std::vector<std::string> strs;
    for (auto &&e : keys_) { strs.push_back(e.Get()); }
    return Join(strs, "/");
}

std::string
DynamicKey::Get() const
{
    std::lock_guard<std::mutex> _(dynamic_key_store_lock);
    auto iter = dynamic_key_store.find(name_);
    if (iter != dynamic_key_store.end()) { return iter->second; }
    return "(not ready yet.)";
}

bool
DynamicKey::Ready() const noexcept
{
    std::lock_guard<std::mutex> _(dynamic_key_store_lock);
    return dynamic_key_store.find(name_) != dynamic_key_store.end();
}

void
SetDynamicKey(const std::string &name, std::string value)
{
    // std::scoped_lock _(dynamic_key_store_lock);
    std::lock_guard<std::mutex> _(dynamic_key_store_lock);
    dynamic_key_store[name] = value;
}

}// namespace option
}// namespace tile
