#pragma once

#include <vector>

#include <Qt>

#include "BuildModeController.h"
#include "BuildingType.h"

// The single statement of what the player can do right now, and which input does it
// (REQ-UI-CONTROLS-CONTENT, REQ-UI-CONTROLS-ACCURACY).
//
// This file declares actions; it never performs them and it never names them. It knows
// an action's bindings and the situations in which it does something -- nothing about
// the simulation, the widgets, the events an action ends up firing, or the words shown
// to the player. Display text lives in ui/ControlActionText.h, which formats the
// bindings this hands it, so a badge is derived from the real binding rather than
// typed beside it.
//
// Three readers, all of them consuming this and none of them extending it:
//
//  * ControlsPanel calls getContextActions()/getAlwaysAvailableActions() and draws them.
//  * InputMapper calls resolveKeyAction() and fires the event the action stands for.
//  * GameWorldView calls resolveMouseAction() and runs the branch it already ran.
//
// Two bindings are deliberately absent. Build hotkeys (REQ-UI-HOTKEYS) are advertised
// on the build buttons instead of in the panel, and InputMapper::getBuildHotkeyLabel
// already derives their badges from the same table the handler switches on, so they
// have no drift to fix. F3/F4 are development controls and appear nowhere
// (REQ-UI-CONTROLS-ACCURACY).
//
// When bindings become player-configurable, only the binding tables in the .cpp turn
// from hard-coded data into loaded data. The actions, the availability rules, the
// panel, and every handler are unaffected.
enum class ControlAction
{
    None,  // no action is bound to the queried input in the queried context

    // Always available (REQ-UI-CONTROLS-CONTENT).
    Move,
    GameSpeed,
    TogglePause,
    PasteTemporary,
    OpenBlueprints,
    OpenMenu,

    // No build mode active.
    Select,
    SelectArea,
    AddToSelection,
    AddAreaToSelection,
    EnterDeconstruct,

    // No build mode active, with something selected.
    CopyTemporary,
    CreateBlueprint,
    ClearSelection,

    // Builder and blueprint placement mode.
    Place,
    ApplySettings,
    PlaceBeltLine,
    Rotate,
    CancelBeltLine,
    ExitMode,

    // Deconstruct mode.
    ToggleDeconstruct,
    DeconstructArea
};

// The mouse gestures that carry a binding. Each is a whole gesture rather than a raw
// event: a drag is one binding, not a press plus a release, because that is the unit
// the player and the panel both think in. Which events make up the gesture, and the
// state it runs on, stay with the widget that owns them.
enum class MouseBinding
{
    LeftClick,
    LeftDrag,
    CtrlLeftClick,
    CtrlLeftDrag,
    RightClick
};

// Which selection category is held, mirroring REQ-UI-SELECTION-CATEGORIES without
// depending on SelectionController.
enum class ControlSelection
{
    None,
    Buildings,
    FieldObjects
};

// Which card the panel is showing (REQ-UI-CONTROLS-CONTENT). Named rather than
// spelled, so the heading text stays a presentation concern.
enum class ControlContextKind
{
    General,
    Selection,
    Build,
    Blueprint,
    Deconstruct
};

// Everything the availability rules are allowed to depend on, as a plain snapshot.
//
// Taking a snapshot rather than references to the live controllers is what keeps this
// testable without a world, and it is what stops an action from reaching into the
// simulation: if a rule needs a fact, the fact is named here and the caller supplies it.
struct ControlContext
{
    BuildMode        mode                      = BuildMode::None;
    // While mode == Builder: the type a click would place at the current hover position,
    // not the type the mode was entered with — tunnel mode resolves to either end
    // (REQ-BLD-TUNNEL-MODE).
    BuildingType     builderType               = BuildingType::Belt;
    bool             draggingBelt              = false;
    // A single-building blueprint whose ghost is over a configuration-transfer target,
    // so clicking hands over settings rather than placing (REQ-UI-BLUEPRINT-TRANSFER).
    bool             hoveredGhostIsTransfer    = false;
    ControlSelection selection                 = ControlSelection::None;
    int              selectionCount            = 0;
    // At least one selected building is player-placeable, the condition under which
    // C and Ctrl+C do anything (REQ-UI-HOTKEYS).
    bool             placeableBuildingSelected = false;
    bool             temporaryBlueprintExists  = false;
};

// One input an action answers to, in structured form so the badge can be rendered from
// it. `modifiers` is what the badge shows; matching is looser than equality, see
// resolveKeyAction.
struct ControlBinding
{
    bool                  isMouse    = false;
    MouseBinding          mouse      = MouseBinding::LeftClick;
    int                   key        = 0;  // Qt::Key_*, when !isMouse
    Qt::KeyboardModifiers modifiers  = Qt::NoModifier;
};

// True when triggering the action in this context would do what its label says. The
// panel shows exactly the available actions, and the resolvers return only available
// ones, which is REQ-UI-CONTROLS-ACCURACY expressed as one function.
bool isControlActionAvailable(ControlAction action, const ControlContext& context);

// The inputs an action answers to, in the order the panel should badge them.
// Context-dependent because a binding can be taken over: while a belt drag is in
// progress the right mouse button cancels the drag, so ExitMode is left with its key
// binding alone (REQ-BLD-BELT-DRAG).
std::vector<ControlBinding> getControlActionBindings(ControlAction action,
                                                     const ControlContext& context);

// Which card is showing, and the rows it holds -- the context's own, then the block
// available everywhere (REQ-UI-CONTROLS-CARD). Both lists are already filtered to the
// available actions and ordered as REQ-UI-CONTROLS-CONTENT lists them.
ControlContextKind         getControlContextKind(const ControlContext& context);
std::vector<ControlAction> getContextActions(const ControlContext& context);
std::vector<ControlAction> getAlwaysAvailableActions(const ControlContext& context);

// The action a key press or a mouse gesture triggers here, or None when the input is
// unbound in this context. Both return only actions that are available, so a caller can
// act on the result without re-checking the situation.
//
// Rotation direction is not part of the action: R and Shift+R are one Rotate, and the
// caller reads the modifier for the direction, exactly as the digit of a build hotkey
// is read from the key. An action with a parameter keeps the parameter at the handler.
ControlAction resolveKeyAction(int key, Qt::KeyboardModifiers modifiers,
                               const ControlContext& context);
ControlAction resolveMouseAction(MouseBinding binding, const ControlContext& context);
