#ifndef ROUTENETWORK_H
#define ROUTENETWORK_H

#include <unordered_map>
#include <boost/graph/adjacency_list.hpp>
#include <QPointF>
#include <boost/range/iterator_range.hpp>
#include "drawoptions.h"

struct vertex_position_t {
    typedef boost::vertex_property_tag kind;
};
struct edge_length_t {
    typedef boost::edge_property_tag kind;
};

typedef boost::property<vertex_position_t, QPointF> VertexProperties;
typedef boost::property<edge_length_t, qreal> EdgeProperties;
typedef boost::adjacency_list<
            boost::vecS,        // OutEdgeList
            boost::vecS,        // VertexList
            boost::undirectedS, // Directed
            VertexProperties,   // VertexProperties
            EdgeProperties      // EdgeProperties
            /* default */       // GraphProperties
            /* default */       // EdgeList
    > Graph;
typedef boost::property_map<Graph, vertex_position_t>::type PositionMap;
typedef boost::property_map<Graph, edge_length_t>::type LengthMap;
typedef boost::property_map<Graph, vertex_position_t>::const_type ConstPositionMap;
typedef boost::property_map<Graph, edge_length_t>::const_type ConstLengthMap;

typedef Graph::vertex_descriptor VertexId;
typedef Graph::edge_descriptor EdgeId;

typedef std::unordered_map<VertexId, VertexId> PredecessorMap;
typedef std::unordered_map<VertexId, qreal> DistanceMap;

class DrawingArea;

class RouteNetwork
{
public:
    RouteNetwork();

    RouteNetwork(RouteNetwork const&) = delete;
    RouteNetwork& operator=(RouteNetwork const&) = delete;
    RouteNetwork(RouteNetwork&& other);
    RouteNetwork& operator=(RouteNetwork&& other);

    typedef Graph::vertex_iterator vertex_iterator;
    typedef Graph::edge_iterator   edge_iterator;
    typedef boost::iterator_range<vertex_iterator> vertex_range;
    typedef boost::iterator_range<edge_iterator>   edge_range;
    vertex_range vertices() const;
    edge_range edges() const;

    VertexId addVertex(QPointF const& position);
    EdgeId addEdge(VertexId start, VertexId end);
    void rerouteEdgesWithin(VertexId vertex, qreal maxDistance);

    void fixConsistencyProblems();

    bool edgeExists(VertexId start, VertexId end) const;
    bool isVertexIsolated(VertexId v) const;
    QPointF vertexPosition(VertexId v) const;
    QPointF edgeSourcePosition(EdgeId e) const;
    QPointF edgeTargetPosition(EdgeId e) const;
    qreal edgeLength(EdgeId e) const;
    qreal edgeLength(VertexId start, VertexId end) const;

    void draw(DrawingArea* drawing, DrawOptions const& options) const;

    bool closestVertexWithin(QPointF const& position, qreal maxDistance, VertexId& result) const;

    bool closestIntersectionWithin(QPointF const& position, qreal maxDistance, QPointF& result) const;

    void singleSourceShortestPaths(VertexId startVertex,
                                   PredecessorMap& predecessors, DistanceMap& distances) const;

private:
    bool edgeIntersection(EdgeId e1, EdgeId e2, QPointF& result) const;

private:
    Graph _graph;
};

#endif // ROUTENETWORK_H
