#pragma once

#include <vector>

#include "BuildingId.h"
#include "entt/entity/entity.hpp"

// How a new hit combines with what is already selected.
enum class SelectionMode
{
    Replace,  // plain click or drag: the hit becomes the whole selection
    Toggle,   // Ctrl + click: the hit joins the selection, or leaves it if present
    Add       // Ctrl + box drag: the hits join the selection, never leave it
};

// The player's current selection across the three categories, and the rules that
// govern moving between them (REQ-UI-SELECTION-CATEGORIES).
//
// The rules were previously written out once for point-clicks and once for box
// drags, which is why they live here now: buildings win over field objects, so
// selecting a building clears actors and debris, and selecting either of those
// clears buildings — but actors and debris coexist with each other
// (REQ-UI-ENTITY-CLICK-SELECT, REQ-UI-DEBRIS-CLICK-SELECT, REQ-UI-MULTI-SELECT,
// REQ-UI-DEBRIS-MULTI-SELECT). A point-click and a box drag then differ only in
// the SelectionMode they pass and in how many hits they pass.
//
// Every mutator publishes the change events, so callers never emit them by hand.
// Hit-testing is not done here: callers resolve what was hit and pass the result
// in, which keeps this free of any simulation dependency.
class SelectionController
{
public:
    const std::vector<BuildingId>&   getSelectedBuildings() const;
    const std::vector<entt::entity>& getSelectedActors() const;
    const std::vector<entt::entity>& getSelectedDebris() const;

    bool isActorSelected(entt::entity actor) const;
    bool isDebrisSelected(entt::entity debris) const;

    // Selects buildings and/or construction sites, clearing any field selection.
    // Always publishes SelectionChangedEvent, even when the result is unchanged,
    // so a click on an already-selected building still refreshes its panel.
    void selectBuildings(const std::vector<BuildingId>& ids, SelectionMode mode);

    // Selects field objects, clearing any building selection. Actors and debris are
    // set together so a Replace can express "these actors and no debris" — which is
    // what a plain click on an actor means — while a Toggle or Add leaves the
    // category whose vector is empty untouched. Always publishes both field events.
    void selectFieldObjects(const std::vector<entt::entity>& actors,
                            const std::vector<entt::entity>& debris,
                            SelectionMode mode);

    // Empties all three categories, publishing only for those that were non-empty.
    void clearAll();

    // Replace one category outright, publishing only if it actually changed. For
    // the per-frame prune of entities that despawned or died: the liveness query
    // belongs to the caller, which is the one that can see the simulation.
    void setSelectedActors(std::vector<entt::entity> actors);
    void setSelectedDebris(std::vector<entt::entity> debris);

private:
    // Each returns whether anything changed, without publishing.
    bool clearBuildingsQuietly();
    bool clearActorsQuietly();
    bool clearDebrisQuietly();

    void publishBuildings() const;
    void publishActors() const;
    void publishDebris() const;

    std::vector<BuildingId>   m_buildings;
    std::vector<entt::entity> m_actors;
    std::vector<entt::entity> m_debris;
};
