#include <yandex/maps/wiki/topo/edge.h>

#include "edge_impl.h"

#include <yandex/maps/wiki/topo/exception.h>

namespace maps {
namespace wiki {
namespace topo {

Edge::Edge(EdgeID id, const geolib3::Polyline2& geom,
           NodeID startNode, NodeID endNode)
    : impl_(new Impl(id, geom, startNode, endNode))
{}

Edge::Edge(const Edge& oth)
    : impl_(new Impl(*oth.impl_))
{}

Edge& Edge::operator=(const Edge& oth)
{
    impl_.reset(new Impl(*oth.impl_));
    return *this;
}

Edge::Edge(Edge&& oth)
    : impl_(std::move(oth.impl_))
{}

Edge& Edge::operator=(Edge&& oth)
{
    impl_ = std::move(oth.impl_);
    return *this;
}

Edge::~Edge()
{ /* impl_ */ }

EdgeID Edge::id() const
{
    return impl_->id;
}

NodeID Edge::startNode() const
{
    return impl_->startNode;
}

NodeID Edge::endNode() const
{
    return impl_->endNode;
}

NodeID Edge::node(const IncidenceType& type) const
{
    return type == IncidenceType::Start ? impl_->startNode : impl_->endNode;
}

IncidentNodes Edge::incidentNodes() const
{
    return IncidentNodes{impl_->startNode, impl_->endNode};
}

const geolib3::Polyline2& Edge::geom() const
{
    return impl_->geom;
}

bool Edge::isIncident(NodeID node) const
{
    return node == impl_->startNode || node == impl_->endNode;
}

bool Edge::isStart(NodeID node) const
{
    return node == impl_->startNode;
}

bool Edge::isEnd(NodeID node) const
{
    return node == impl_->endNode;
}

NodeID Edge::oppositeNode(NodeID nodeId) const
{
    if (nodeId == impl_->startNode) {
        return impl_->endNode;
    } else if (nodeId == impl_->endNode) {
        return impl_->startNode;
    } else {
        throw LogicError()
            << " node " << nodeId << " is not incident to edge " << impl_->id;
    }
}


boost::optional<FaceIDSet> Edge::faceIds() const { return impl_->faceIds; }

bool Edge::isPartOfFace(FaceID faceId) const
{
    REQUIRE(impl_->faceIds, " faces were not loaded for edge " << impl_->id);
    return impl_->faceIds->count(faceId);
}

///  Edge::Impl

Edge::Impl::Impl(EdgeID id, const geolib3::Polyline2& geom,
        NodeID startNode, NodeID endNode)
    : id(id)
    , startNode(startNode)
    , endNode(endNode)
    , geom(geom)
{
    REQUIRE(startNode, " no start node set for edge " << id);
    REQUIRE(endNode, " no end node set for edge " << id);
}

} // namespace topo
} // namespace wiki
} // namespace maps
