#include "relmode.h"

#include <boost/algorithm/string.hpp>
#include <sstream>

namespace maps::wiki::json2ymapsdf::tds::schema {

namespace {
const std::string STR_COMMA = ",";
} // namespace

RelationsMode::RelationsMode(
        RelationsDirection mainDirection,
        const std::string& invertedRoles)
    : mainDirection_(mainDirection)
{
    boost::split(invertedRoles_, invertedRoles, boost::is_any_of(STR_COMMA));
}

std::string
RelationsMode::invertedRoles() const
{
    return boost::join(invertedRoles_, STR_COMMA);
}

bool
operator == (const RelationsMode& m1, const RelationsMode& m2)
{
    return m1.mainDirection_ == m2.mainDirection_
        && m1.invertedRoles_ == m2.invertedRoles_;
}

bool
operator != (const RelationsMode& m1, const RelationsMode& m2)
{
    return !(m1 == m2);
}

namespace {

RelationsDirection
invert(RelationsDirection direction)
{
    if (direction == RelationsDirection::MasterToSlave) {
        return RelationsDirection::SlaveToMaster;
    } else if (direction == RelationsDirection::SlaveToMaster) {
        return RelationsDirection::MasterToSlave;
    } else {
        return RelationsDirection::Unknown;
    }
}

} // namespace

RelationsDirection
RelationsMode::direction(const std::string& role)
{
    return invertedRoles_.count(role)
        ? invert(mainDirection())
        : mainDirection();
}

bool
RelationsMode::unknown() const
{
    return mainDirection() == RelationsDirection::Unknown;
}

std::string
RelationsMode::info() const
{
    std::ostringstream infoStr;

    infoStr << "Relations direction: ";
    if (mainDirection() == RelationsDirection::MasterToSlave) {
        infoStr << "MasterToSlave ";
    } else if (mainDirection() == RelationsDirection::SlaveToMaster) {
        infoStr << "SlaveToMaster ";
    } else {
        infoStr << "Unknown";
    }
    infoStr << "\n";

    infoStr << "Inverted roles: ";
    for (const auto& role: invertedRoles_) {
        infoStr << role << ", ";
    }
    infoStr << "\n";
    return infoStr.str();
}

namespace {
const std::string RELATION_MASTER_TO_SLAVE = "Master ==> Slave";
const std::string RELATION_SLAVE_TO_MASTER = "Master <== Slave";
const std::string RELATION_UNKNOWN = "None";
} // namespace

std::istream&
operator >> (std::istream& is, RelationsDirection& direction)
{
    std::string directionName;
    std::getline(is, directionName);

    if (directionName == RELATION_MASTER_TO_SLAVE) {
        direction = RelationsDirection::MasterToSlave;
    } else if (directionName == RELATION_SLAVE_TO_MASTER) {
        direction = RelationsDirection::SlaveToMaster;
    } else {
        direction = RelationsDirection::Unknown;
    }

    return is;
}

std::ostream&
operator << (std::ostream& os, const RelationsDirection& direction)
{
    if (direction == RelationsDirection::MasterToSlave) {
        os << RELATION_MASTER_TO_SLAVE;
    } else if (direction == RelationsDirection::SlaveToMaster) {
        os << RELATION_SLAVE_TO_MASTER;
    } else {
        os << RELATION_UNKNOWN;
    }
    return os;
}

} // namespace maps::wiki::json2ymapsdf::tds::schema
