#include "SelectionContentFactory.h"

#include "BeltContent.h"
#include "DebrisContent.h"
#include "FactoryQueries.h"
#include "FieldMultiContent.h"
#include "HqContent.h"
#include "MultiBuildingContent.h"
#include "RecipeProductionContent.h"
#include "ShipContent.h"
#include "ShipIdentityComponent.h"
#include "ShipyardContent.h"
#include "SplitterContent.h"
#include "StationBodyComponent.h"
#include "StationContent.h"
#include "StorageContent.h"
#include "Simulation.h"

namespace
{

// The catalog row a single building type belongs to (REQ-UI-SELECTION-CONTENT).
SelectionContentKind getKindForType(BuildingType type)
{
    switch (type)
    {
        case BuildingType::Miner:
        case BuildingType::Assembler:
        case BuildingType::Smelter:
        case BuildingType::ReprocessingPlant:
            return SelectionContentKind::RecipeProduction;
        case BuildingType::Shipyard:
            return SelectionContentKind::Shipyard;
        case BuildingType::SalvageBay:
            return SelectionContentKind::Storage;
        case BuildingType::Hq:
            return SelectionContentKind::Hq;
        case BuildingType::Belt:
        case BuildingType::TunnelEntry:
        case BuildingType::TunnelExit:
            return SelectionContentKind::Belt;
        case BuildingType::Splitter:
            return SelectionContentKind::Splitter;
        case BuildingType::PlayerDefenceStation:
        case BuildingType::EnemyDefenceStation:
            // Defence stations are field objects backed by entities; these two enum
            // values exist only for cost and visuals lookup and never reach the panel as
            // a building selection. The count summary is the harmless fallback.
            return SelectionContentKind::MultiBuilding;
    }
    return SelectionContentKind::MultiBuilding;
}

ContentKey chooseBuildingContent(const SelectionRequest& request, Simulation& sim)
{
    const FactoryState& state = sim.getFactoryState();

    if (request.buildings.size() == 1)
    {
        const BuildingId id = request.buildings.front();
        const Building* building = findBuilding(state, id);
        if (building)
        {
            return { getKindForType(building->type), false };
        }
        const ConstructionSite* site = findSite(state, id);
        if (site)
        {
            return { getKindForType(site->type), true };
        }
        // The building went away under the panel; the selection is stale and there is
        // nothing to show (REQ-UI-EMPTY-SELECTION).
        return {};
    }

    // Several buildings aggregate into one card only when every part of that card's
    // runtime content aggregates (REQ-UI-SELECTION-AGGREGATE). Belt-subsystem tiles do:
    // the item list and the clear action already read and act on the whole selection
    // (REQ-UI-BELT-ITEMS, REQ-UI-BELT-CLEAR). Splitters are among them -- their output
    // filters are per-object configuration, which an aggregate simply does not show.
    //
    // Construction sites are excluded: findBuilding fails for a site, whose card is its
    // own construction progress and whose tile the belt subsystem does not know yet.
    bool allBeltTiles = true;
    for (BuildingId id : request.buildings)
    {
        const Building* building = findBuilding(state, id);
        if (!building || !isBeltSubsystemType(building->type))
        {
            allBeltTiles = false;
            break;
        }
    }
    if (allBeltTiles)
    {
        return { SelectionContentKind::Belt, false };
    }
    return { SelectionContentKind::MultiBuilding, false };
}

ContentKey chooseFieldContent(const SelectionRequest& request, Simulation& sim)
{
    // A full single-object card is shown for a lone actor, and for debris whether one
    // piece or several -- debris is the field category's other aggregating content
    // (REQ-UI-FIELD-MULTI-SELECTION, REQ-UI-SELECTION-AGGREGATE).
    if (request.actors.size() == 1 && request.debris.empty())
    {
        EntityAdmin& admin = sim.getAdmin();
        const entt::entity actor = request.actors.front();
        if (admin.isValid(actor) && admin.hasAll<ShipIdentityComponent>(actor))
        {
            return { SelectionContentKind::Ship, false };
        }
        if (admin.isValid(actor) && admin.hasAll<StationBodyComponent>(actor))
        {
            return { SelectionContentKind::Station, false };
        }
        return {};
    }

    if (request.actors.empty() && !request.debris.empty())
    {
        return { SelectionContentKind::Debris, false };
    }
    return { SelectionContentKind::FieldMulti, false };
}

}  // namespace


ContentKey chooseContent(const SelectionRequest& request, Simulation& sim)
{
    if (request.isEmpty())
    {
        return {};
    }
    // Buildings win, so a non-empty building selection is the whole selection
    // (REQ-UI-SELECTION-CATEGORIES).
    if (!request.buildings.empty())
    {
        return chooseBuildingContent(request, sim);
    }
    return chooseFieldContent(request, sim);
}

SelectionContent* createContent(const ContentKey& key, const SelectionRequest& request,
                                const SelectionContext& context, QWidget* parent)
{
    switch (key.kind)
    {
        case SelectionContentKind::None:
            return nullptr;
        case SelectionContentKind::RecipeProduction:
            return new RecipeProductionContent(context, request, parent);
        case SelectionContentKind::Shipyard:
            return new ShipyardContent(context, request, parent);
        case SelectionContentKind::Storage:
            return new StorageContent(context, request, parent);
        case SelectionContentKind::Hq:
            return new HqContent(context, request, parent);
        case SelectionContentKind::Belt:
            return new BeltContent(context, request, parent);
        case SelectionContentKind::Splitter:
            return new SplitterContent(context, request, parent);
        case SelectionContentKind::MultiBuilding:
            return new MultiBuildingContent(context, request, parent);
        case SelectionContentKind::Ship:
            return new ShipContent(context, request, parent);
        case SelectionContentKind::Station:
            return new StationContent(context, request, parent);
        case SelectionContentKind::Debris:
            return new DebrisContent(context, request, parent);
        case SelectionContentKind::FieldMulti:
            return new FieldMultiContent(context, request, parent);
    }
    return nullptr;
}
