#pragma once

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

#include <QPoint>

#include "GameConfig.h"
#include "ItemTooltipContext.h"
#include "ModalDialog.h"
#include "Rotation.h"
#include "ShipLayout.h"
#include "ShipLayoutBlueprint.h"

class ItemIconCache;
class QPushButton;
class RecipeLineRow;
class SectionBox;
class ShipStatsPanel;

class ShipLayoutDialog : public ModalDialog
{
    Q_OBJECT

public:
    // The context's item cache draws what each module costs and what the layout costs in
    // total (REQ-MOD-UI-DIALOG, REQ-MOD-UI-STATS-PANEL), and the rest of it lets the
    // build cost explain the items it names (REQ-UI-ITEM-VALUE-TOOLTIP). Nothing in it is
    // owned.
    ShipLayoutDialog(const std::string& shipId,
                     const ShipLayoutConfig& currentLayout,
                     std::vector<ShipLayoutBlueprint>& allBlueprints,
                     std::set<std::string> unlockedModuleIds,
                     bool debugDraw,
                     const ItemTooltipContext& context,
                     QWidget* parent = nullptr);

    std::optional<ShipLayoutConfig> getResult() const;

    bool isDismissible() const override;

public slots:
    // Backs out one level per dismissal, as Q does in the game world: the module being
    // placed, then remove mode, then the dialog itself, which discards the session
    // (REQ-UI-DIALOG-DISMISS). So a single press or click never both leaves a mode and
    // throws the session away.
    void requestDismiss() override;

protected:
    void keyPressEvent(QKeyEvent* event) override;

signals:
    void gridCellClicked(QPoint cell);

private slots:
    void onModuleButtonClicked(int index);
    // Enters remove mode, or leaves it when it is already active (REQ-MOD-REMOVE).
    // Reached by the Remove button and by a dismissal, which leaves the mode on its way
    // out of the dialog (REQ-UI-DIALOG-DISMISS).
    void onRemoveButtonClicked();
    void onConfirm();
    void onCancel();

public:
    struct CellInfo
    {
        bool buildable;
        std::optional<int> moduleIndex;  // nullopt if empty
    };

private:

    void rebuildOccupancy();
    void updateGridWidget();
    void updateStats();
    bool canPlaceModule(const ModuleDef& def, QPoint position, Rotation rotation) const;
    std::vector<std::string> rotatedMask(const ModuleDef& def, Rotation rotation) const;
    void loadLayoutBlueprint(const std::vector<PlacedModule>& modules);

    ItemTooltipContext m_context;
    const GameConfig* m_config;
    ItemIconCache* m_itemIcons;
    std::string m_shipId;
    std::set<std::string> m_unlockedModuleIds;
    std::vector<std::string> m_shipLayout;
    int m_rows;
    int m_cols;

    std::vector<PlacedModule> m_placedModules;
    std::vector<std::vector<CellInfo>> m_grid;

    // The module to place, as an index into config modules; nullopt when no
    // module is selected for placement. m_removeMode is a separate mode in which
    // clicking a cell removes the module there (mutually exclusive with a
    // selected module).
    std::optional<int> m_activeModuleIndex;
    bool m_removeMode = false;
    Rotation m_currentRotation;

    std::vector<QPushButton*> m_moduleButtons;
    QPushButton* m_removeButton;
    QWidget* m_gridWidget;
    ShipStatsPanel* m_statsPanel;
    SectionBox* m_buildCostSection;
    RecipeLineRow* m_buildCostLine;
    bool m_debugDraw;

    std::optional<ShipLayoutConfig> m_result;
};
