#include "ShipStatsPanel.h"

#include <QString>
#include <QVBoxLayout>

#include "BarRow.h"
#include "GameConfig.h"
#include "SectionBox.h"
#include "SelectionNames.h"
#include "ShipStatsCalculator.h"
#include "StatRow.h"
#include "ThreatCostCalculator.h"

namespace
{

QString fmt(float value)
{
    return QString::number(static_cast<double>(value), 'f', 1);
}

}  // namespace


ShipStatsPanel::ShipStatsPanel(const GameConfig* config, QWidget* parent)
    : QWidget(parent)
    , m_config(config)
{
    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->setSpacing(2);
    layout->setAlignment(Qt::AlignTop);

    // Hull stats -- always visible, except cargo capacity, which only a ship that can
    // carry anything shows (REQ-MOD-UI-STATS-PANEL).
    m_hpBar               = new BarRow(tr("HP"), this);
    m_speedRow            = new StatRow(tr("Max speed"), this);
    m_sensorRangeRow      = new StatRow(tr("Sensor range"), this);
    m_mainAccelRow        = new StatRow(tr("Main accel"), this);
    m_maneuveringAccelRow = new StatRow(tr("Maneuvering accel"), this);
    m_angularAccelRow     = new StatRow(tr("Angular accel"), this);
    m_maxRotSpeedRow      = new StatRow(tr("Max rotation"), this);
    m_cargoCapacityRow    = new StatRow(tr("Cargo capacity"), this);
    m_cargoCapacityRow->hide();

    layout->addWidget(m_hpBar);
    layout->addWidget(m_speedRow);
    layout->addWidget(m_sensorRangeRow);
    layout->addWidget(m_mainAccelRow);
    layout->addWidget(m_maneuveringAccelRow);
    layout->addWidget(m_angularAccelRow);
    layout->addWidget(m_maxRotSpeedRow);
    layout->addWidget(m_cargoCapacityRow);

    // One section per capability module type, each shown only while at least one such
    // module is installed (REQ-MOD-UI-STATS-PANEL).
    m_weaponSection  = new SectionBox(tr("Weapons"), this);
    m_weaponDpsRow   = new StatRow(tr("DPS"), m_weaponSection);
    m_weaponRangeRow = new StatRow(tr("Range"), m_weaponSection);
    m_weaponSection->getContentLayout()->addWidget(m_weaponDpsRow);
    m_weaponSection->getContentLayout()->addWidget(m_weaponRangeRow);
    m_weaponSection->hide();
    layout->addWidget(m_weaponSection);

    m_salvageSection  = new SectionBox(tr("Salvage"), this);
    m_salvageRateRow  = new StatRow(tr("Collection rate"), m_salvageSection);
    m_salvageRangeRow = new StatRow(tr("Range"), m_salvageSection);
    m_salvageSection->getContentLayout()->addWidget(m_salvageRateRow);
    m_salvageSection->getContentLayout()->addWidget(m_salvageRangeRow);
    m_salvageSection->hide();
    layout->addWidget(m_salvageSection);

    m_repairSection  = new SectionBox(tr("Repair"), this);
    m_repairRateRow  = new StatRow(tr("Repair rate"), m_repairSection);
    m_repairRangeRow = new StatRow(tr("Range"), m_repairSection);
    m_repairSection->getContentLayout()->addWidget(m_repairRateRow);
    m_repairSection->getContentLayout()->addWidget(m_repairRangeRow);
    m_repairSection->hide();
    layout->addWidget(m_repairSection);

    // Live entities only; the design preview has no behavior to show
    // (REQ-UI-SHIP-BEHAVIOR).
    m_behaviorRow = new StatRow(tr("Behavior"), this);
    m_behaviorRow->hide();
    layout->addWidget(m_behaviorRow);

    // Threat cost -- shown only while debug draw is active (REQ-UI-SHIP-STATS-PANEL).
    m_threatCostRow = new StatRow(tr("Threat cost"), this);
    m_threatCostRow->hide();
    layout->addWidget(m_threatCostRow);
}

void ShipStatsPanel::setBehavior(BehaviorKind kind)
{
    const QString label = getBehaviorLabel(kind);
    m_behaviorRow->setValue(label);
    m_behaviorRow->setVisible(!label.isEmpty());
}

void ShipStatsPanel::refresh(const std::string& shipId,
                             const std::vector<PlacedModule>& modules)
{
    const ShipStats stats = calculateShipStats(*m_config, shipId, modules);
    applyStats(stats, 1.0, QString::number(static_cast<int>(stats.hp + 0.5f)));

    setThreatCost(calculateShipThreatCost(m_config->threatCosts, *m_config,
                                          shipId, modules));
}

void ShipStatsPanel::refreshFromLive(const ShipStats& stats, float currentHp)
{
    const double fraction = (stats.hp > 0.0f)
        ? static_cast<double>(currentHp) / stats.hp
        : 0.0;
    applyStats(stats, fraction, tr("%1 / %2")
        .arg(static_cast<int>(currentHp + 0.5f))
        .arg(static_cast<int>(stats.hp + 0.5f)));
}

void ShipStatsPanel::applyStats(const ShipStats& stats, double hpFraction,
                                const QString& hpText)
{
    m_hpBar->setValue(hpFraction, hpText);
    m_speedRow->setValue(tr("%1 tiles/s").arg(fmt(stats.maxSpeed_tps)));
    m_sensorRangeRow->setValue(tr("%1 tiles").arg(fmt(stats.sensorRange_tiles)));
    m_mainAccelRow->setValue(
        tr("%1 tiles/s\xc2\xb2").arg(fmt(stats.mainAcceleration_tpss)));
    m_maneuveringAccelRow->setValue(
        tr("%1 tiles/s\xc2\xb2").arg(fmt(stats.maneuveringAcceleration_tpss)));
    m_angularAccelRow->setValue(
        tr("%1 rad/s\xc2\xb2").arg(fmt(stats.angularAcceleration_radpss)));
    m_maxRotSpeedRow->setValue(tr("%1 rad/s").arg(fmt(stats.maxRotationSpeed_radps)));

    // Cargo capacity is shown only when the ship can actually hold cargo
    // (REQ-MOD-UI-STATS-PANEL).
    m_cargoCapacityRow->setVisible(stats.cargoCapacity > 0);
    if (stats.cargoCapacity > 0)
    {
        m_cargoCapacityRow->setValue(QString::number(stats.cargoCapacity));
    }

    m_weaponSection->setVisible(stats.weapons.has_value());
    if (stats.weapons.has_value())
    {
        m_weaponDpsRow->setValue(fmt(stats.weapons->combinedDps));
        m_weaponRangeRow->setValue(tr("%1 tiles").arg(fmt(stats.weapons->maxRange_tiles)));
    }

    m_salvageSection->setVisible(stats.salvage.has_value());
    if (stats.salvage.has_value())
    {
        m_salvageRateRow->setValue(
            tr("%1 /s").arg(fmt(stats.salvage->combinedCollectionRate)));
        m_salvageRangeRow->setValue(tr("%1 tiles").arg(fmt(stats.salvage->maxRange_tiles)));
    }

    m_repairSection->setVisible(stats.repair.has_value());
    if (stats.repair.has_value())
    {
        m_repairRateRow->setValue(
            tr("%1 HP/s").arg(fmt(stats.repair->combinedRepairRate_hps)));
        m_repairRangeRow->setValue(tr("%1 tiles").arg(fmt(stats.repair->maxRange_tiles)));
    }
}

void ShipStatsPanel::setThreatCost(double cost)
{
    m_threatCostRow->setValue(QString::number(cost, 'f', 1));
    m_threatCostRow->setVisible(m_debugDraw);
}

void ShipStatsPanel::setDebugDrawEnabled(bool enabled)
{
    m_debugDraw = enabled;
    m_threatCostRow->setVisible(m_debugDraw);
}
