#include "SchematicChoiceDialog.h"

#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>

#include "DisplayName.h"
#include "RecipeLineRow.h"
#include "RecipesConfig.h"

namespace
{

std::vector<RecipeLineRow::Amount> toAmounts(
    const std::vector<RecipeIngredient>& ingredients)
{
    std::vector<RecipeLineRow::Amount> amounts;
    amounts.reserve(ingredients.size());
    for (const RecipeIngredient& ingredient : ingredients)
    {
        amounts.push_back(RecipeLineRow::Amount{ ingredient.item, ingredient.amount });
    }
    return amounts;
}


// One captioned list of previewed recipes. Both lists are drawn identically -- the
// caption is what says whether the recipe is new to the player or newly better
// (REQ-DEF-SCHEMATIC-DROP), so each line states the whole recipe either way, including
// the groups it could already yield.
void addRecipePreview(QWidget* card, QVBoxLayout* cardLayout, const QString& caption,
                      const std::vector<PreviewedRecipe>& previewed, bool showNone,
                      const RecipesConfig& recipes, const ItemTooltipContext& context)
{
    QLabel* headerLabel = new QLabel(caption, card);
    QFont headerFont = headerLabel->font();
    headerFont.setBold(true);
    headerLabel->setFont(headerFont);
    headerLabel->setAlignment(Qt::AlignCenter);
    cardLayout->addWidget(headerLabel);

    if (previewed.empty())
    {
        if (showNone)
        {
            QLabel* noneLabel = new QLabel(SchematicChoiceDialog::tr("None"), card);
            noneLabel->setAlignment(Qt::AlignCenter);
            cardLayout->addWidget(noneLabel);
        }
        return;
    }

    for (const PreviewedRecipe& entry : previewed)
    {
        const RecipeDef* def = recipes.findRecipeDef(entry.recipeId);
        if (def == nullptr) { continue; }

        // The recipe drawn as it is everywhere else, led by the building that runs it
        // and its name, so the line says everything there is to say about the recipe
        // itself (REQ-DEF-SCHEMATIC-DROP). Its groups were decided against the unlock
        // state the award would bring about, which is why they arrive named rather than
        // being filtered here against the state the player is still in. The items it
        // names still explain themselves, and the card around them is not clicked -- the
        // Select button below it is -- so a click on one says it at once
        // (REQ-UI-ITEM-VALUE-TOOLTIP).
        RecipeLineRow::Spec spec;
        spec.building        = def->building;
        spec.name            = QString::fromStdString(toDisplayName(def->id));
        spec.inputs          = toAmounts(def->inputs);
        spec.outputGroups    = RecipeLineRow::toOutputGroups(*def, entry.outputGroupIndices);
        spec.durationSeconds = def->durationSeconds;

        RecipeLineRow* line =
            new RecipeLineRow(context.itemIcons, context.buildingIcons, card);
        line->setCardChrome(true);
        line->setItemTooltips(context, TooltipTrigger::Trigger::HoverAndClick);
        cardLayout->addWidget(line);
        line->setLine(spec);
    }
}

QString grantKindLabel(SchematicType type)
{
    switch (type)
    {
    case SchematicType::Ship:     return SchematicChoiceDialog::tr("Ship");
    case SchematicType::Module:   return SchematicChoiceDialog::tr("Module");
    case SchematicType::Building: return SchematicChoiceDialog::tr("Building");
    case SchematicType::Recipe:   return SchematicChoiceDialog::tr("Recipe");
    }
    return QString();
}

}  // namespace

SchematicChoiceDialog::SchematicChoiceDialog(
    const std::vector<SchematicChoiceOption>& options,
    const RecipesConfig& recipes, const ItemTooltipContext& context,
    QWidget* parent)
    : ModalDialog(parent)
    , m_chosenIndex(0)
{
    QVBoxLayout* mainLayout = new QVBoxLayout(this);

    // Its own title line, and the only one: a modal draws its title rather than wearing
    // one (REQ-UI-MODAL-CHROME), and this dialog carries no close button because there
    // is no way out but choosing (REQ-DEF-SCHEMATIC-DROP).
    QLabel* titleLabel = new QLabel(tr("Choose a schematic to unlock:"), this);
    QFont titleFont = titleLabel->font();
    titleFont.setPointSize(titleFont.pointSize() + 2);
    titleFont.setBold(true);
    titleLabel->setFont(titleFont);
    titleLabel->setAlignment(Qt::AlignCenter);
    mainLayout->addWidget(titleLabel);

    QHBoxLayout* optionsLayout = new QHBoxLayout();
    mainLayout->addLayout(optionsLayout);

    for (int i = 0; i < static_cast<int>(options.size()); ++i)
    {
        const SchematicChoiceOption& option = options[static_cast<std::size_t>(i)];

        QWidget* card = new QWidget(this);
        QVBoxLayout* cardLayout = new QVBoxLayout(card);
        // Scoped to the card by object name. An unscoped "QWidget" selector cascades to
        // every descendant, which boxed each label and each icon of the recipe lines
        // inside it as well.
        card->setObjectName(QStringLiteral("schematicChoiceCard"));
        card->setStyleSheet(QStringLiteral(
            "QWidget#schematicChoiceCard { border: 1px solid gray; padding: 8px; }"));

        QLabel* nameLabel = new QLabel(QString::fromStdString(option.displayName), card);
        QFont nameFont = nameLabel->font();
        nameFont.setPointSize(nameFont.pointSize() + 1);
        nameFont.setBold(true);
        nameLabel->setFont(nameFont);
        nameLabel->setAlignment(Qt::AlignCenter);
        cardLayout->addWidget(nameLabel);

        if (option.isArtifact)
        {
            QLabel* artifactTypeLabel = new QLabel(tr("Artifact"), card);
            artifactTypeLabel->setAlignment(Qt::AlignCenter);
            cardLayout->addWidget(artifactTypeLabel);
        }
        else
        {
            QLabel* grantsHeaderLabel = new QLabel(tr("Unlocks:"), card);
            QFont grantsHeaderFont = grantsHeaderLabel->font();
            grantsHeaderFont.setBold(true);
            grantsHeaderLabel->setFont(grantsHeaderFont);
            grantsHeaderLabel->setAlignment(Qt::AlignCenter);
            cardLayout->addWidget(grantsHeaderLabel);

            for (const GrantedSchematic& grant : option.grantedItems)
            {
                QLabel* grantLabel = new QLabel(
                    grantKindLabel(grant.type) + ": "
                        + QString::fromStdString(grant.displayName),
                    card);
                grantLabel->setAlignment(Qt::AlignCenter);
                cardLayout->addWidget(grantLabel);
            }

            // What the option would add to what the player can produce
            // (REQ-DEF-SCHEMATIC-DROP): recipes they could not use at all, then recipes
            // they already run that would start yielding something further. The second
            // list is left out entirely when empty -- an option that upgrades nothing is
            // the ordinary case, and a second "None" would only be noise.
            addRecipePreview(card, cardLayout, tr("Unlocks recipes:"),
                             option.newlyUsableRecipes, /*showNone=*/true,
                             recipes, context);
            if (!option.upgradedRecipes.empty())
            {
                addRecipePreview(card, cardLayout, tr("Upgrades recipes:"),
                                 option.upgradedRecipes, /*showNone=*/false,
                                 recipes, context);
            }
        }

        QPushButton* selectButton = new QPushButton(tr("Select"), card);
        cardLayout->addWidget(selectButton);

        const int index = i;
        connect(selectButton, &QPushButton::clicked, this, [this, index]()
        {
            onOptionClicked(index);
        });

        optionsLayout->addWidget(card);
    }
}

int SchematicChoiceDialog::getChosenIndex() const
{
    return m_chosenIndex;
}

void SchematicChoiceDialog::reject()
{
    // Deliberately empty: the dialog stays open until an option is clicked
    // (REQ-DEF-SCHEMATIC-DROP). The game is paused meanwhile, so nothing waits on it.
}

void SchematicChoiceDialog::onOptionClicked(int index)
{
    m_chosenIndex = index;
    accept();
}
