#include "id_map.h"
#include "magic_strings.h"

#include <tuple>

namespace maps::wiki::masstransit {

bool
IdMap::MtrId::operator< (const MtrId& rhs) const {
    return std::tie(id, type) < std::tie(rhs.id, rhs.type);
}

IdMap::IdMap()
    : lastDBID_(0)
{ }

void
IdMap::initLastDBID(DBID maxId)
{
    lastDBID_ = maxId;
}

void
IdMap::add(DBID dbId, const std::string& mtrIdName, MtrType mtrType)
{
    if (mtrIdName.empty()) {
        return;
    }
    std::lock_guard<std::mutex> lock(mapMutex_);
    registerIdPair(dbId, MtrId{mtrIdName, mtrType});
}

DBID
IdMap::newDBID()
{
    std::lock_guard<std::mutex> lock(generateMutex_);
    const auto newId = ++lastDBID_;
    return newId;
}

DBID
IdMap::getDBID(const std::string& mtrIdName, MtrType mtrType)
{
    REQUIRE(!mtrIdName.empty(), "Unable to process empty mtr id");

    MtrId mtrId{mtrIdName, mtrType};

    std::lock_guard<std::mutex> lock(mapMutex_);

    const auto it = mtr2db_.find(mtrId);
    if (it != mtr2db_.end()) {
        return it->second;
    }
    const auto dbId = newDBID();
    registerIdPair(dbId, mtrId);
    return dbId;
}

std::string
IdMap::getMtrId(DBID dbId) const
{
    std::lock_guard<std::mutex> lock(mapMutex_);
    const auto it = db2mtr_.find(dbId);
    if (it != db2mtr_.end()) {
        return it->second.id;
    }
    return std::to_string(dbId);
}

void
IdMap::registerIdPair(DBID dbId, const MtrId& mtrId)
{
    REQUIRE(!db2mtr_.count(dbId),
         "DBID=" << dbId << " is registered already for MTRID=" << db2mtr_[dbId].id
         << ", trying to register MTRID=" << mtrId.id);

    REQUIRE(!mtr2db_.count(mtrId),
         "MTRID=" << mtrId.id << " for " << toString(mtrId.type) << " is registered already for DBID="
         << mtr2db_[mtrId]
         << ", trying to register DBID=" << dbId);

    db2mtr_.emplace(dbId, mtrId);
    mtr2db_.emplace(mtrId, dbId);
    if (dbId > lastDBID_) {
        lastDBID_ = dbId;
    }
}

} // namespace maps::wiki::masstransit
