#pragma once

#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>

#include <QColor>
#include <QPoint>
#include <QPointF>
#include <QRectF>
#include <QVector2D>

#include "BeamFiredEvent.h"
#include "BuildModeController.h"
#include "BuildingId.h"
#include "BuildingType.h"
#include "Rotation.h"
#include "SelectionController.h"
#include "VisualsConfig.h"
#include "WorldCoordinates.h"

class ItemIconCache;
class Simulation;
class QPainter;
class QSvgRenderer;

// A beam still being drawn. Lifetime is counted in game ticks so beams freeze with
// the simulation when it is paused or slowed (REQ-SHP-FIRING-BEAM); the view owns
// the ageing, the renderer only draws what it is given.
struct ActiveBeam
{
    BeamFiredEvent event;
    QVector2D      targetOffset;
};

// How a placement ghost is colored. Normal shows the building type's own colors
// (REQ-BLD-GHOST); the other two override them (REQ-BLD-PLACE-VALID,
// REQ-UI-BLUEPRINT-TRANSFER).
enum class GhostTint
{
    Normal,
    Invalid,
    Transfer
};

// Everything the renderer draws that the simulation does not know about: what the
// player has selected, which build mode is active, and the other transient bits of
// interaction state the view owns. Assembled fresh each frame and passed by
// reference, so the renderer holds no copy that could go stale.
struct WorldRenderFrame
{
    const SelectionController&     selection;
    const BuildModeController&     buildMode;
    const std::vector<ActiveBeam>& beams;
    // The box being dragged, in world coordinates and normalized, or nullopt when no
    // drag is in progress — a press that has not passed the movement threshold is
    // still a click and offers none (REQ-UI-MULTI-SELECT). It is the same rectangle
    // the view selects by, so what is drawn and what is selected cannot disagree.
    std::optional<QRectF>          boxWorldRect;
    bool                           isDebugDrawEnabled;
};

// Draws the game world: terrain, buildings, items, ships, effects and the build
// overlays, in back-to-front order (see the Layer Order section of
// docs/architecture.md).
//
// Reads the simulation and never writes it, and knows nothing about input — the
// view resolves clicks and owns the interaction state, and hands the parts the
// renderer needs over in a WorldRenderFrame.
//
// Everything here is positioned in tiles. Screen-anchored chrome — the pause and
// deconstruct vignettes, the replay overlay, the debug stats panel — stays with
// the view, which is why nothing in this class draws translatable text.
class WorldRenderer
{
public:
    // `itemIcons` is the window-wide per-item icon cache (REQ-UI-ITEM-ICON); not
    // owned, must outlive this renderer. `configDir` is used once, to load the
    // per-building world icons.
    WorldRenderer(const Simulation& sim, const VisualsConfig& visuals,
                  ItemIconCache* itemIcons, const std::string& configDir);
    ~WorldRenderer();

    void render(QPainter& painter, const WorldCoordinates& coordinates,
                const WorldRenderFrame& frame);

private:
    void drawTiles(QPainter& painter, const WorldCoordinates& coordinates,
                   const WorldRenderFrame& frame);
    void drawBuildings(QPainter& painter, const WorldCoordinates& coordinates,
                       const WorldRenderFrame& frame);
    void drawSelectionHighlights(QPainter& painter, const WorldCoordinates& coordinates,
                                 const WorldRenderFrame& frame);
    void drawPortItems(QPainter& painter, const WorldCoordinates& coordinates,
                       const WorldRenderFrame& frame);
    void drawStations(QPainter& painter, const WorldCoordinates& coordinates,
                      const WorldRenderFrame& frame);
    void drawBeltItems(QPainter& painter, const WorldCoordinates& coordinates,
                       const WorldRenderFrame& frame);
    void drawDebris(QPainter& painter, const WorldCoordinates& coordinates,
                    const WorldRenderFrame& frame);
    void drawShips(QPainter& painter, const WorldCoordinates& coordinates,
                   const WorldRenderFrame& frame);
    void drawDebugSensorRanges(QPainter& painter, const WorldCoordinates& coordinates,
                               const WorldRenderFrame& frame);
    void drawDebugTargetLines(QPainter& painter, const WorldCoordinates& coordinates,
                              const WorldRenderFrame& frame);
    void drawBeams(QPainter& painter, const WorldCoordinates& coordinates,
                   const WorldRenderFrame& frame);
    void drawSelectedTunnelConnections(QPainter& painter,
                                       const WorldCoordinates& coordinates,
                                       const WorldRenderFrame& frame);
    void drawOverlays(QPainter& painter, const WorldCoordinates& coordinates,
                      const WorldRenderFrame& frame);
    // Draws a single item centered at widget-space `center`, spanning `halfPx` in
    // each direction (a half-tile): the colored square from visuals.toml, carrying
    // the item's icon inset on top of it when one exists (REQ-UI-ITEM-ICON).
    // Shared by drawBeltItems and drawPortItems.
    void drawWorldItem(QPainter& painter, const std::string& itemId,
                       QPointF center, float halfPx);
    void drawPortGlyph(QPainter& painter, const WorldCoordinates& coordinates,
                       QPoint tile, Rotation direction, const QColor& color,
                       bool centered);
    void drawBuildingGhost(QPainter& painter, const WorldCoordinates& coordinates,
                           BuildingType type, QPoint anchorTile, Rotation rotation,
                           GhostTint tint, bool showPortTargetGlyphs);

    // Loads the per-building world icons (REQ-UI-WORLD-ICON) from
    // <configDir>/../icons/buildings once at construction. Only the building
    // types with a world icon are loaded (production buildings, HQ, stations);
    // belts, splitters, and tunnels are deliberately excluded so their
    // orientation stays readable. The SVG's chip background is stripped; the
    // glyph is pre-rendered in both white and dark ink for auto-contrast.
    void loadBuildingIcons(const std::string& configDir);
    // Draws a building's world icon glyph centered in box, choosing the white or
    // dark pre-rendered variant by fill luminance so it stays legible. Returns
    // false if the type has no world icon (caller falls back to the text glyph).
    bool drawBuildingIcon(QPainter& painter, const WorldCoordinates& coordinates,
                          BuildingType type, const QRectF& box,
                          const QColor& fill) const;

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

    // The renderer reads the simulation and never writes it.
    const Simulation&    m_sim;
    const VisualsConfig& m_visuals;

    // Per-item icon cache (REQ-UI-ITEM-ICON), shared window-wide and owned by
    // MainWindow. Shared draw path for belt and port items; pixmaps are cached
    // per target size.
    ItemIconCache* m_itemIcons;

    // World icon glyph renderers per building type (REQ-UI-WORLD-ICON), in a
    // white and a dark variant so drawBuildingIcon can auto-contrast against the
    // building's fill. Rendered as vector at the view scale each draw so they
    // stay crisp. Populated once by loadBuildingIcons().
    struct BuildingIconRenderers
    {
        std::unique_ptr<QSvgRenderer> white;
        std::unique_ptr<QSvgRenderer> dark;
    };
    std::map<BuildingType, BuildingIconRenderers> m_buildingIcons;
};
