#pragma once

#include <optional>
#include <vector>

#include <QPoint>

#include "BeltDragPath.h"
#include "Blueprint.h"
#include "BuildingId.h"
#include "BuildingType.h"
#include "Rotation.h"

// Which of the mutually exclusive world-interaction modes is active
// (REQ-UI-HOTKEYS, REQ-BLD-GHOST, REQ-UI-BLUEPRINT-PLACE, REQ-BLD-DECONSTRUCT).
enum class BuildMode
{
    None,         // plain selection
    Builder,      // placing one building type, ghost following the cursor
    Blueprint,    // placing a saved multi-building layout
    Deconstruct   // marking buildings for demolition
};

// The active build mode and the transient state that belongs to it.
//
// These modes were previously three independent flags, and every entry point
// cleared the other two by hand — inconsistently, which is how entering builder
// mode came to drop a blueprint without announcing it. Here exclusivity is
// structural: one mode is active, and every transition runs through enterMode(),
// which exits whatever was active first and publishes the same events regardless
// of which way the player got there.
//
// Everything needing the simulation — placement validity, tunnel matching, belt
// path building — stays with the caller, which computes and hands back the result
// (setGhostValidity, setTunnelGhost, setBeltDragPath). That keeps this a plain
// value that can be tested without a world.
class BuildModeController
{
public:
    BuildMode getMode() const;
    bool      isBuilderMode() const;
    bool      isBlueprintMode() const;
    bool      isDeconstructMode() const;

    // --- transitions ----------------------------------------------------------
    // Each leaves the previously active mode with its proper exit event.
    void enterBuilderMode(BuildingType type);
    void enterBlueprintMode(Blueprint blueprint);
    // Leaves deconstruct mode if it is active, enters it otherwise
    // (REQ-BLD-DECONSTRUCT-CLICK).
    void toggleDeconstructMode();
    void exitBuilderMode();
    void exitBlueprintMode();
    // Backs out of whichever mode is active, if any (the Q key and right-click).
    void exitCurrentMode();

    // --- hover ----------------------------------------------------------------
    // Drops everything that follows from a cursor pointing at the world — both
    // ghost tiles, placement validity, the resolved tunnel end, the transfer flag,
    // the deconstruct hover — for a cursor that points at no tile at all, because it
    // rests on a panel or has left the window (REQ-BLD-GHOST). The active mode is
    // untouched: the player is still building, just not over anything. A belt drag's
    // path is untouched too, since a drag keeps hovering while the button is held.
    void clearHover();

    // --- builder mode ---------------------------------------------------------
    // Only meaningful while isBuilderMode().
    BuildingType getBuilderType() const;
    // True while the builder type is TunnelEntry, where the ghost resolves to an
    // entry or an exit by hovered position (REQ-BLD-TUNNEL-MODE).
    bool         isTunnelMode() const;
    // The type the ghost currently represents: the position-resolved tunnel type in
    // tunnel mode, the plain builder type otherwise.
    BuildingType getEffectiveBuilderType() const;

    // Unset while the cursor points at no tile (clearHover), which is the one case
    // where builder mode draws no ghost at all.
    const std::optional<QPoint>& getGhostTile() const;
    Rotation getGhostRotation() const;
    bool     isGhostValid() const;
    void     setGhostTile(QPoint tile);
    void     setGhostValidity(bool valid);
    // Turns the ghost one quarter turn. Validity is not rechecked here; the caller
    // does that and calls setGhostValidity, because only it can see the world.
    void     rotateGhost(bool clockwise);

    BuildingType                 getTunnelGhostType() const;
    const std::optional<QPoint>& getTunnelPartnerTile() const;
    void setTunnelGhost(BuildingType resolvedType, std::optional<QPoint> partnerTile);

    // --- belt drag placement (REQ-BLD-BELT-DRAG) ------------------------------
    bool   isDraggingBelt() const;
    QPoint getBeltDragAnchor() const;
    const std::vector<BeltPathTile>& getBeltDragPath() const;
    void   beginBeltDrag(QPoint anchorTile);
    void   setBeltDragPath(std::vector<BeltPathTile> path);
    // Drops the drag without placing anything, staying in builder mode.
    void   cancelBeltDrag();

    // --- blueprint mode -------------------------------------------------------
    // Only meaningful while isBlueprintMode().
    const Blueprint& getBlueprint() const;
    // Mutable so the caller can rotate the layout in place; rotating a blueprint
    // needs building footprints from the config, which does not belong here.
    Blueprint&       getMutableBlueprint();
    // Unset for a cursor pointing at no tile, as for the builder ghost above.
    const std::optional<QPoint>& getBlueprintGhostTile() const;
    void             setBlueprintGhostTile(QPoint tile);

    // Whether the ghost under the cursor would hand its settings to the building
    // already there rather than place anything (REQ-UI-BLUEPRINT-TRANSFER). Classifying
    // it needs the factory state, so the caller resolves it and stores the answer here,
    // as with setGhostValidity. Kept here rather than recomputed per reader so the
    // click, the ghost's colour, and the controls panel cannot disagree about what the
    // cursor is over.
    bool isHoveredGhostTransfer() const;
    void setHoveredGhostTransfer(bool transfer);

    // --- deconstruct mode -----------------------------------------------------
    const std::optional<BuildingId>& getDeconstructHoverBuildingId() const;
    void setDeconstructHoverBuildingId(std::optional<BuildingId> id);

private:
    // The single transition point: leaves the active mode, then enters `mode`.
    void enterMode(BuildMode mode);

    BuildMode m_mode = BuildMode::None;

    BuildingType          m_builderType    = BuildingType::Belt;
    std::optional<QPoint> m_ghostTile;
    Rotation              m_ghostRotation  = Rotation::East;
    bool                  m_ghostValid     = false;
    BuildingType          m_tunnelGhostType = BuildingType::TunnelEntry;
    std::optional<QPoint> m_tunnelPartnerTile;

    bool                      m_draggingBelt = false;
    QPoint                    m_beltDragAnchor;
    std::vector<BeltPathTile> m_beltDragPath;

    Blueprint             m_blueprint;
    std::optional<QPoint> m_blueprintGhostTile;
    bool                  m_hoveredGhostIsTransfer = false;

    std::optional<BuildingId> m_deconstructHoverBuildingId;
};
