#pragma once

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

#include <QRect>
#include <QWidget>

#include "BuilderModeExitedEvent.h"
#include "BuildHotkeyPressedEvent.h"
#include "BuildingBlocksChangedEvent.h"
#include "BuildingType.h"
#include "DeconstructModeChangedEvent.h"
#include "EventHandler.h"
#include "GameConfig.h"
#include "UnlockedBuildingsChangedEvent.h"

class QPushButton;
class Simulation;
class BuildingIconCache;
class ItemIconCache;

// The build menu: one horizontal row of build buttons floating over the game world
// view (REQ-UI-BUILD-BAR). The bar is sized to its buttons; its owner hands it the
// world view's rect through anchorTo() and it centers itself along that rect's
// bottom edge.
class BuildButtonBar : public QWidget,
                       public CombinedEventHandler<BuilderModeExitedEvent,
                                                   DeconstructModeChangedEvent,
                                                   BuildHotkeyPressedEvent,
                                                   BuildingBlocksChangedEvent,
                                                   UnlockedBuildingsChangedEvent>
{
    Q_OBJECT

public:
    // buildingIcons supplies each button's chip icon (REQ-UI-BUILD-ICON) and itemIcons
    // the building_block icon shown in each button's cost (REQ-UI-BUILD-COST). Both are
    // window-wide caches; neither is owned, and both must outlive this widget.
    BuildButtonBar(Simulation* sim, const GameConfig* config,
                   BuildingIconCache* buildingIcons, ItemIconCache* itemIcons,
                   QWidget* parent = nullptr);
    ~BuildButtonBar() override;

    // Centers the bar along the bottom edge of the game world view's rect, given in
    // the bar's parent coordinates (REQ-UI-BUILD-BAR). The rect is remembered, so a
    // re-center later driven by an unlock needs no second call from the owner.
    void anchorTo(const QRect& worldViewRect);

    // Height of the strip the bar occupies along the bottom of the world view: its own
    // height plus the margin below it. The selection panel keeps out of this strip, and
    // the bar never moves for the panel in return (REQ-UI-BUILD-BAR).
    int getStripHeightPx() const;

    void clearActiveButton();

private:
    // Re-evaluates which build buttons are enabled from the current building block
    // stock (read from the simulation). If the currently selected tool can no longer
    // be afforded, it exits builder mode so the button does not stay selected.
    void updateAffordability();

    // Re-evaluates which build buttons are visible from the current per-building
    // unlock state (REQ-LOCK-BUILDING); a locked building type's button is hidden.
    void updateVisibility();

    // Shrinks the bar to its currently shown buttons and re-centers it in the
    // anchored rect (REQ-UI-BUILD-BAR). Does nothing until anchorTo() supplied that
    // rect, so the construction-time call is harmless.
    void recenter();

    void handleEvent(std::shared_ptr<const BuilderModeExitedEvent> event) override;
    void handleEvent(std::shared_ptr<const DeconstructModeChangedEvent> event) override;
    void handleEvent(std::shared_ptr<const BuildHotkeyPressedEvent> event) override;
    void handleEvent(std::shared_ptr<const BuildingBlocksChangedEvent> event) override;
    void handleEvent(std::shared_ptr<const UnlockedBuildingsChangedEvent> event) override;

private slots:
    void onBuildButton(int index);

private:
    Simulation*                      m_sim;
    const GameConfig*                m_config;
    BuildingIconCache*               m_buildingIcons;  // Not owned; lives in MainWindow.
    ItemIconCache*                   m_itemIcons;      // Not owned; lives in MainWindow.
    std::vector<BuildingType>        m_types;
    std::vector<QPushButton*>        m_buttons;
    std::map<BuildingType, int>      m_costs;
    std::optional<std::size_t>       m_activeIndex;
    QPushButton*                     m_deconstructButton;
    QRect                            m_viewRect;
};
