#ifndef STATISTICS_H
#define STATISTICS_H

#include <limits>
#include <vector>
#include <deque>
#include <cmath>
#include <QIODevice>
#include "constants.h"

class StaticLabeling;
class LabeledPoint;
class DynamicLabeling;
class GraphingArea;

enum SummaryStatistic {
    FreeCount, FreeArea, LabelSpeeds
};

class Statistics
{
public:
    explicit Statistics(qreal duration);

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

    void writeAsCSV(QIODevice& out) const;

    void reset(qreal duration);
    void updateDuration(qreal duration);
    void recordLabeling(StaticLabeling const& labeling);

    void attachGraphOutput(GraphingArea& ga);
    void graphType(SummaryStatistic s);
    SummaryStatistic graphType() const { return _graphWhat; }
    void detachGraphOutput();
    void redraw() const;

    int freeAtTime(qreal time) const;
    int totalAtTime(qreal time) const;

    qreal firstUnrecordedTime() const;

private:
    void recordLabelingSummaries(std::size_t tick, StaticLabeling const& labeling);
    void recordLabelMovements(std::size_t tick, StaticLabeling const& labeling);
    void recordSpeed(std::size_t tick, qreal speed);

    std::size_t tickBeforeTime(qreal time) const { return (std::size_t)secondsToTicks(time); }
    std::size_t tickAfterTime(qreal time) const { return (std::size_t)std::ceil(secondsToTicks(time)); }
    std::size_t numTicks() const { return _summaries.size(); }

    void drawBar(std::size_t tick) const;

private:
    struct LabelingSummary {
        LabelingSummary()
            : freeArea(-1), summedArea(-1),
              minMovement(+std::numeric_limits<qreal>::infinity()),
              maxMovement(-std::numeric_limits<qreal>::infinity()),
              totalMovement(0),
              free(-1), total(-1),
              numMovements(0)
        {}
        qreal averageMovement() const { return numMovements > 0 ? totalMovement / numMovements : 0.0; }
        qreal minimumSpeed() const { return minMovement / secondsPerTick; }
        qreal maximumSpeed() const { return maxMovement / secondsPerTick; }
        qreal averageSpeed() const { return averageMovement() / secondsPerTick; }

        qreal freeArea, summedArea;
        qreal minMovement, maxMovement, totalMovement;
        int free, total, numMovements;
    };
    static std::size_t const INVALID_TICK = std::numeric_limits<std::size_t>::max();
    struct LabelMovement
    {
        LabelMovement() : firstTick(INVALID_TICK) {}
        void clear() { firstTick = INVALID_TICK; positions.clear(); }
        mutable std::vector<qreal> positions;
        std::size_t firstTick;
    };
    LabelMovement& labelMovementFor(LabeledPoint const& p);
    void normalizeLabelMovements() const;

private:
    std::vector<LabelingSummary> _summaries;   // indexed by tick
    std::deque<LabelMovement> _labelMovements; // indexed by AirplaneID
    GraphingArea* _graphOutput;
    mutable std::size_t _firstUnrecorded;
    int _labelPerimeter;
    SummaryStatistic _graphWhat;
};

#endif // STATISTICS_H
