#pragma once

#include <optional>
#include <random>
#include <vector>

#include <QElapsedTimer>
#include <QOpenGLWidget>
#include <QTimer>
#include <QVector2D>

#include "EventHandler.h"
#include "BeamFiredEvent.h"

#include "entt/entity/entity.hpp"
#include "EntitySelectionChangedEvent.h"
#include "Tick.h"
#include "TickDriver.h"
#include "VisualsConfig.h"
#include "WorldCoordinates.h"

class ArenaSimulation;
class QPainter;

class ArenaView : public QOpenGLWidget,
                  public EventHandler<BeamFiredEvent>
{
    Q_OBJECT

public:
    ArenaView(ArenaSimulation* sim, const VisualsConfig* visuals,
              QWidget* parent = nullptr);
    ~ArenaView() override;

    void setGameSpeed(double multiplier);
    double getGameSpeed() const;
    void togglePause();
    void stopRendering();

protected:
    void paintGL() override;
    void mousePressEvent(QMouseEvent* event) override;
    void keyPressEvent(QKeyEvent* event) override;

private slots:
    void onFrame();

private:
    void handleEvent(std::shared_ptr<const BeamFiredEvent> event) override;

    void drawTiles(QPainter& painter, const WorldCoordinates& coordinates);
    void drawBuildings(QPainter& painter, const WorldCoordinates& coordinates);
    void drawStations(QPainter& painter, const WorldCoordinates& coordinates);
    void drawDebris(QPainter& painter, const WorldCoordinates& coordinates);
    void drawShips(QPainter& painter, const WorldCoordinates& coordinates);
    void drawDebugSensorRanges(QPainter& painter, const WorldCoordinates& coordinates);
    void drawDebugTargetLines(QPainter& painter, const WorldCoordinates& coordinates);
    void drawBeams(QPainter& painter, const WorldCoordinates& coordinates);

    // The world <-> widget transform for the current viewport size. The arena
    // shows the whole world at once and never scrolls, so this fits the arena's
    // full extent into the widget; like GameWorldView's, it is a per-frame
    // snapshot rather than cached state.
    WorldCoordinates getCoordinates() const;

    std::optional<QVector2D> entityPosition(entt::entity entity) const;

    struct ActiveBeam
    {
        BeamFiredEvent event;
        QVector2D targetOffset;
    };

    // Beam lifetime in game ticks so beams freeze with the simulation when
    // paused or slowed, instead of fading on wall-clock time (REQ-SHP-FIRING-BEAM).
    static constexpr Tick kBeamLifetimeTicks = secondsToTicks(0.3);

    ArenaSimulation*     m_sim;
    const VisualsConfig* m_visuals;

    TickDriver    m_tickDriver;
    QElapsedTimer m_frameTimer;
    std::mt19937  m_rng;
    double        m_gameSpeedMultiplier;
    double        m_prevNonZeroSpeed;

    QTimer* m_renderTimer;

    std::vector<ActiveBeam> m_activeBeams;
    bool m_finishedEmitted;

    std::optional<entt::entity> m_selectedEntity;

    bool m_debugDraw;
};
