#ifndef AIRPLANEROUTE_H
#define AIRPLANEROUTE_H

#include <QPointF>
#include <QString>
#include <vector>
#include <forward_list>
#include <random>
#include "airplane.h"
#include <boost/range/iterator_range.hpp>

class AirplanePool;
class DrawingArea;
class ProblemInstance;

typedef unsigned int AirplaneRouteID;

class AirplaneRoute
{
public:
    class Vertex
    {
        friend class AirplaneRoute;

        void appendVertex(Vertex* next);
        void reverse();

        void recomputeTurnedness();

    public:
        explicit Vertex(QPointF const& pos); // NOTE: I would like this to be private, but then Vertex'es cannot be emplaced by AirplaneRoute
                                             // std::vector<Vertex> would have to become a friend class for that
        bool isLeftTurn() const;             // (or perhaps even some implementation-specific class used by std::vector)
        bool isRightTurn() const;

        QPointF position() const;
        QPointF directionBefore() const;
        QPointF directionAfter() const;
        qreal distanceFromStart() const;
        qreal timeFromStart(qreal airplaneSpeed) const;

    private:
        QPointF _position, _directionBefore, _directionAfter;
        qreal _distanceFromStart, _turnedness;
    };

public:
    AirplaneRoute(ProblemInstance& problemInstance, AirplanePool& pool);

    AirplaneRoute(AirplaneRoute const&) = delete;
    AirplaneRoute& operator=(AirplaneRoute const&) = delete;

    AirplaneRouteID id() const { return _uniqueID; }

    void appendVertex(QPointF const& position);
    void reverse();

    void draw(DrawingArea* drawing) const;

    QPointF positionAtTime(qreal time) const;
    QPointF directionAtTime(qreal time) const;

    QString labelTemplate() const { return _labelTemplate; }
    void labelTemplate(QString s);

    qreal airplaneSpeed() const { return _airplaneSpeed; }
    void airplaneSpeed(qreal v);

    bool usesPoissonProcess() const { return _usesPoisson; }
    void usesPoissonProcess(bool b);

    qreal averageInterarrivalTime() const;
    void averageInterarrivalTime(qreal t);

    unsigned int rngSeed() const { return _initialSeed; }
    void rngSeed(unsigned int seed);
    void rerollSeed();

    qreal length() const;
    qreal timeLength() const { return length() / airplaneSpeed(); }

    QString labelText(AirplaneID id, AirplaneID rid) const;
    QString biggestLabelText() const { return labelText(998, 998); }

public:
    typedef std::vector<Vertex>::const_iterator const_iterator;
    typedef boost::iterator_range<const_iterator> const_range;

    const_range vertices() const;
    const_range verticesBetween(qreal time1, qreal time2) const;

    void spawnAirplanesUpto(qreal time) const;
private:
    void resetSpawnedAirplanes();

    qreal firstArrivalTime() const;
    qreal nextInterarrivalTime() const;
    static unsigned int randomSeed();

    const_iterator firstVertexAtOrAfter(qreal time) const;
    const_iterator firstVertexAfter(qreal time) const;
    void findRouteSegment(qreal time, const_iterator& beforeOrAt, qreal& alpha) const;

private:
    mutable std::mt19937 _rng;
    mutable std::exponential_distribution<qreal> _expDist;
    typedef std::exponential_distribution<qreal>::param_type lambda;
    mutable qreal _nextSpawnTime;
    unsigned int _initialSeed;
    bool _usesPoisson;

    mutable std::forward_list<Airplane*> _myAirplanes;
    mutable AirplanePool* _airplanePool;
    mutable AirplaneID _nextAirplaneID;

    static AirplaneRouteID _nextID;
    AirplaneRouteID _uniqueID;

    std::vector<Vertex> _vertices;
    QString _labelTemplate;
    qreal _airplaneSpeed;

    ProblemInstance* _problemInstance;
};


#endif // AIRPLANEROUTE_H
