#include "ItemProducers.h"

#include <functional>

#include "GameConfig.h"
#include "RecipesConfig.h"
#include "Simulation.h"

namespace
{

// True when the recipe can currently be made to yield this item -- some group that
// contains it is one the pool would pick (REQ-LOCK-OUTPUT-POOL). A recipe that lists the
// item only in a group holding something still locked is no path to it: the pool drops
// that group whole, so the plant will never hand the player the item that way.
bool yieldsItem(const RecipeDef& recipe, const std::string& itemId, const Simulation& sim)
{
    const std::function<bool(const std::string&)> isItemUnlocked =
        [&sim](const std::string& id) { return sim.isItemUnlocked(id); };

    for (const RecipeOutputGroup& group : recipe.outputGroups)
    {
        if (!isOutputGroupUnlocked(group, isItemUnlocked)) { continue; }
        for (const RecipeOutput& out : group.items)
        {
            if (out.item == itemId) { return true; }
        }
    }
    return false;
}

bool isAvailable(const RecipeDef& recipe, const Simulation& sim)
{
    // Two independent conditions, both required: the recipe must be one the player can
    // run (REQ-LOCK-UI-RECIPE), and the building that runs it must be one they can place
    // -- there is no sense in naming a path through a plant they cannot build
    // (REQ-LOCK-BUILDING, REQ-UI-ITEM-TOOLTIP).
    return sim.isRecipeUnlocked(recipe.id) && sim.isBuildingUnlocked(recipe.building);
}

}  // namespace

ItemProduction findItemProduction(const std::string& itemId, const Simulation& sim,
                                  const GameConfig& config)
{
    ItemProduction result;

    bool anyProducer = false;
    for (const RecipeDef& recipe : config.recipes.recipes)
    {
        // Whether the item is craftable at all is a question about config alone, so it
        // ignores the unlock state entirely: that is what separates an item nothing makes
        // (scrap, salvaged) from one whose every path is still closed (undiscovered).
        if (!producesItem(recipe, itemId)) { continue; }

        anyProducer = true;
        if (isAvailable(recipe, sim) && yieldsItem(recipe, itemId, sim))
        {
            result.recipes.push_back(&recipe);
        }
    }

    if (!result.recipes.empty())
    {
        result.origin = ItemOrigin::Produced;
    }
    else
    {
        result.origin = anyProducer ? ItemOrigin::Undiscovered : ItemOrigin::Salvaged;
    }
    return result;
}
