#pragma once

#include <deque>
#include <vector>

#include "Building.h"
#include "GameConfig.h"
#include "BuildingGrid.h"
#include "BuildingId.h"
#include "ItemType.h"
#include "Tick.h"

// One pending demolition of a fully-built building (REQ-BLD-DECON-QUEUE).
// completesAt == 0 means "queued but its timer has not started yet"
// (mirrors ConstructionSite). For a Splitter, the filters it had are captured
// here so cancelDeconstruction can restore them on re-registration.
struct DeconstructionEntry
{
    BuildingId            id          = kInvalidBuildingId;
    Tick                  completesAt = 0;
    std::vector<ItemType> splitterFilterA;
    std::vector<ItemType> splitterFilterB;
};

// The factory's world data: every building, the work queued on them, and the
// tile ownership index. This is the buildings-side counterpart to EntityAdmin —
// data with no behaviour of its own beyond what BuildingGrid encapsulates.
//
// Buildings deliberately stay a plain vector rather than becoming EnTT entities
// (see docs/architecture.md). Separating this data from the systems that operate
// on it is not a step toward putting them in the entity model; it is the same
// data/behaviour split the ecs/system/ classes already follow, where world data
// arrives as a tick argument instead of being owned by the system.
//
// Owned by Simulation (and by ArenaSimulation in the balancing tool), not by the
// systems that operate on it. Every system that touches the factory —
// BuildingSystem, ConstructionSystem, DeconstructionSystem — takes it as an argument
// and holds none of it, so each is stateless over the world it works on. Reading it
// needs no system at all: the queries are free functions over this struct
// (FactoryQueries.h, PlacementRules.h, ProductionRules.h).
//
// What is not here yet is the data those systems still reach back into Simulation
// for through callbacks: the building-id counter and the global building block stock,
// both of which are factory data living outside the factory's state.
struct FactoryState
{
    std::vector<Building>           buildings;
    std::deque<ConstructionSite>    constructionQueue;
    std::deque<DeconstructionEntry> deconstructionQueue;

    // The authority on which building owns which tile; every placement and removal
    // path claims and releases its body cells here.
    BuildingGrid                    grid;

    // Current buildable asteroid width, the left bound for placement. Grows as the
    // player buys expansions (REQ-EXP-UNLOCK). Deliberately not checksummed: it is
    // derived from config and Simulation's expansion count, which is folded already.
    // Seeded from config by BuildingSystem's constructor.
    int                             asteroidWidth_tiles = 0;

    // Next id to hand out for a building, a construction site, or a tile claimed by a
    // station entity. Strictly increasing and never reused, so an id names one thing for
    // the whole run -- which is what lets a command reference a building across a replay
    // (docs/replay_design.md). Allocated through allocateBuildingId (FactoryQueries.h).
    BuildingId                      nextBuildingId = 1;

    // The global building block stock (REQ-HQ-STARTING-BLOCKS, REQ-HQ-BELT-INPUT):
    // what placement spends, what deconstruction refunds, and what blocks delivered to
    // the HQ add to. Factory data, so it lives with the factory rather than being
    // reached through a callback by every system that credits it.
    //
    // Folded into the checksum by Simulation, in the place it has always occupied
    // (docs/replay_design.md).
    int                             buildingBlocksStock = 0;
};

// A fresh factory for a new run: nothing built, the asteroid bound and the starting
// block stock seeded from config. Every owner of a FactoryState creates it this way —
// the bound has no sensible default without the config, so a default-constructed state
// would refuse every placement on the asteroid, and the player would start with nothing
// to build from.
inline FactoryState makeFactoryState(const GameConfig& config)
{
    FactoryState state;
    state.asteroidWidth_tiles  = config.world.regions.asteroidWidth_tiles;
    state.buildingBlocksStock  = config.world.startingBuildingBlocks;
    return state;
}
