#pragma once

#include <yandex/maps/wiki/topo/events.h>
#include <yandex/maps/wiki/topo/common.h>

#include <maps/libs/geolib/include/polyline.h>
#include <maps/libs/geolib/include/point.h>

#include <boost/optional.hpp>

#include <memory>

namespace maps {
namespace wiki {
namespace topo {

class Edge;
class Node;

const bool EXISTS = true;
const bool NOT_EXISTS = false;

struct AddEdgeEventData {
    AddEdgeEventData(EdgeID id,
            const SourceEdgeID& sourceId,
            NodeID start, NodeID end, const geolib3::Polyline2& geom)
        : id(id)
        , sourceId(sourceId)
        , start(start)
        , end(end)
        , geom(geom)
    {}

    EdgeID id;
    const SourceEdgeID sourceId;
    NodeID start;
    NodeID end;
    geolib3::Polyline2 geom;
};

struct DeleteNodeEventData {
    DeleteNodeEventData(NodeID id)
        : id(id)
    {}

    NodeID id;
};


struct DeleteEdgeEventData {
    DeleteEdgeEventData(EdgeID id, NodeID startNodeId, NodeID endNodeId)
        : id(id)
        , startNodeId(startNodeId)
        , endNodeId(endNodeId)
    {}

    EdgeID id;
    NodeID startNodeId;
    NodeID endNodeId;
};


namespace test {

class IntersectionTest;

} // namespace test

struct SplitEdgeEventData {

    SplitEdgeEventData(
            const SourceEdgeID& sourceId,
            bool isEditedEdge,
            const geolib3::Polyline2& polyline,
            const SplitPointPtrVector& splitPoints,
            const SplitPolylinePtrVector& splitPolylines,
            const OptionalIndex partToKeepIDIndex = boost::none)
        : sourceId(sourceId)
        , isEditedEdge(isEditedEdge)
        , polyline(polyline)
        , splitPoints(splitPoints)
        , splitPolylines(splitPolylines)
        , partToKeepIDIndex(partToKeepIDIndex)
    {}

    SourceEdgeID sourceId;
    bool isEditedEdge;
    geolib3::Polyline2 polyline;
    SplitPointPtrVector splitPoints;
    SplitPolylinePtrVector splitPolylines;
    OptionalIndex partToKeepIDIndex;
    boost::optional<IncidentNodes> oldIncidentNodes;
};

struct MergeEdgesEventData {
    MergeEdgesEventData(EdgeID merged, EdgeID deleted,
            NodeID mergeNode,
            const geolib3::PointsVector& newGeomPoints,
            bool isGeomDirectionContinuous)
        : mergedId(merged)
        , deletedId(deleted)
        , mergeNodeId(mergeNode)
        , newGeomPoints(newGeomPoints)
        , isGeomDirectionContinuous(isGeomDirectionContinuous)
    {}

    EdgeID mergedId;
    EdgeID deletedId;
    NodeID mergeNodeId;
    geolib3::PointsVector newGeomPoints;
    const bool isGeomDirectionContinuous;
};

struct MoveEdgeEventData {
    MoveEdgeEventData(EdgeID id,
            OptionalNodeID newStartNode,
            OptionalNodeID newEndNode,
            const geolib3::Polyline2& newGeom,
            const IncidentNodes& oldIncidentNodes)
        : id(id)
        , newStartNode(newStartNode)
        , newEndNode(newEndNode)
        , newGeom(newGeom)
        , oldIncidentNodes(oldIncidentNodes)
    {}

    EdgeID id;
    OptionalNodeID newStartNode;
    OptionalNodeID newEndNode;
    geolib3::Polyline2 newGeom;
    IncidentNodes oldIncidentNodes;
};

struct MergeNodesEventData {
    MergeNodesEventData(NodeID idFrom, NodeID idTo, const geolib3::Point2& pos)
        : idFrom(idFrom)
        , idTo(idTo)
        , pos(pos)
    {}

    NodeID idFrom;
    NodeID idTo;
    geolib3::Point2 pos;
};

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