#include "HeaderBar.h"

#include <cmath>
#include <string>

#include <QFontMetrics>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QPalette>
#include <QPushButton>
#include <QSignalMapper>
#include <QSize>

#include "Command.h"
#include "CommandRequestedEvent.h"
#include "EventManager.h"
#include "IconCaption.h"
#include "ItemIconCache.h"
#include "Simulation.h"
#include "SpeedChangeRequestedEvent.h"
#include "Tick.h"

namespace
{
    // Item id of the building blocks resource, whose icon stands in for the "Blocks"
    // word in the header stock and expand button (REQ-UI-BLOCKS-ICON).
    const char* const kBlockItemId = "building_block";
}

const double HeaderBar::kSpeeds[]   = { 0.0, 0.5, 1.0, 2.0, 10.0 };
const int    HeaderBar::kSpeedCount = 5;

HeaderBar::HeaderBar(const Simulation* sim, const GameConfig* config,
                     ItemIconCache* itemIcons, QWidget* parent)
    : QWidget(parent)
    , m_itemIcons(itemIcons)
    , m_sim(sim)
{
    QHBoxLayout* layout = new QHBoxLayout(this);
    layout->setContentsMargins(8, 4, 8, 4);
    layout->setSpacing(8);

    m_timeLabel      = new QLabel("00:00", this);
    m_blocksLabel    = new QLabel(this);
    if (config->world.buildingBlocksTooltip)
    {
        m_blocksLabel->setToolTip(
            QString::fromStdString(*config->world.buildingBlocksTooltip));
    }
    updateBlocksLabel();
    m_artifactsLabel = new QLabel(tr("Artifacts: 0/?"), this);
    if (config->world.artifactTooltip)
    {
        m_artifactsLabel->setToolTip(
            QString::fromStdString(*config->world.artifactTooltip));
    }
	m_bossWaveLabel = new QLabel(tr("Boss Wave #1"), this);
	m_nextBossLabel = new QLabel(tr("Next boss: 5:00"), this);

    // Asteroid expansion button, to the left of the speed buttons (REQ-UI-HEADER,
    // REQ-UI-EXPAND-BUTTON). Caption/enabled state are set on the first
    // ExpansionCostChangedEvent; clicking requests an ExpandAsteroidCommand.
    m_expandButton = new QPushButton(tr("Expand"), this);
    connect(m_expandButton, &QPushButton::clicked, this, []() {
        EventManager::getInstance()->sendEventImmediately(
            std::make_shared<CommandRequestedEvent>(
                std::make_shared<ExpandAsteroidCommand>()));
    });

	const int itemSpacing = 20;

	layout->addWidget(m_timeLabel);
	layout->addSpacing(itemSpacing);
	layout->addWidget(m_blocksLabel);
	layout->addSpacing(itemSpacing);
	layout->addWidget(m_artifactsLabel);
	layout->addStretch();
	layout->addWidget(m_bossWaveLabel);
	layout->addSpacing(itemSpacing);
	layout->addWidget(m_nextBossLabel);
	layout->addSpacing(itemSpacing);
	layout->addWidget(m_expandButton);
	layout->addSpacing(itemSpacing);

    const char* labels[] = { "0x", "0.5x", "1x", "2x", "10x" };
    QSignalMapper* mapper = new QSignalMapper(this);
    for (int i = 0; i < kSpeedCount; ++i)
    {
        QPushButton* btn = new QPushButton(labels[i], this);
        btn->setCheckable(true);
        btn->setChecked(i == 2);
        layout->addWidget(btn);
        m_speedButtons.push_back(btn);
        mapper->setMapping(btn, i);
        connect(btn, &QPushButton::clicked, mapper, qOverload<>(&QSignalMapper::map));
    }
    connect(mapper, qOverload<int>(&QSignalMapper::mapped), this, &HeaderBar::onSpeedButton);

    setFixedHeight(sizeHint().height());

    registerForEvents();
}

HeaderBar::~HeaderBar()
{
    unregisterForEvents();
}

void HeaderBar::handleEvent(std::shared_ptr<const TickAdvancedEvent> /*event*/)
{
    const int totalSeconds = static_cast<int>(ticksToSeconds(m_sim->getCurrentTick()));
    m_timeLabel->setText(
        QString("%1:%2")
            .arg(totalSeconds / 60, 2, 10, QChar('0'))
            .arg(totalSeconds % 60, 2, 10, QChar('0')));
}

void HeaderBar::handleEvent(std::shared_ptr<const BuildingBlocksChangedEvent> /*event*/)
{
    updateBlocksLabel();
    updateExpandButton();
}

void HeaderBar::handleEvent(std::shared_ptr<const ExpansionCostChangedEvent> /*event*/)
{
    updateExpandButton();
}

QPixmap HeaderBar::blockIcon() const
{
    if (!m_itemIcons->hasIcon(kBlockItemId)) { return QPixmap(); }
    // Sized to the header text height so it sits inline with the caption.
    const int sizePx = QFontMetrics(font()).height();
    return m_itemIcons->getPixmap(kBlockItemId, sizePx);
}

void HeaderBar::updateBlocksLabel()
{
    const int blocks = m_sim->getBuildingBlocksStock();

    const QPixmap icon = blockIcon();
    if (icon.isNull())
    {
        // Fallback text form when no building_block icon exists (REQ-UI-BLOCKS-ICON).
        m_blocksLabel->setText(tr("Stock: %1 Blocks").arg(blocks));
        return;
    }
    m_blocksLabel->setPixmap(renderCaptionWithIcon(
        tr("Stock: %1").arg(blocks), icon, font(),
        m_blocksLabel->palette().color(QPalette::WindowText)));
}

void HeaderBar::updateExpandButton()
{
    const int blocks        = m_sim->getBuildingBlocksStock();
    const int expansionCost = m_sim->getCurrentExpansionCost();

    m_expandButton->setEnabled(blocks >= expansionCost);

    const QPixmap icon = blockIcon();
    if (icon.isNull())
    {
        // Fallback text form when no building_block icon exists (REQ-UI-EXPAND-BUTTON).
        m_expandButton->setIcon(QIcon());
        m_expandButton->setText(tr("Expand: %1 Blocks").arg(expansionCost));
        return;
    }

    const QString text = tr("Expand: %1").arg(expansionCost);
    const QPalette& pal = m_expandButton->palette();
    const QPixmap normal = renderCaptionWithIcon(
        text, icon, m_expandButton->font(), pal.color(QPalette::ButtonText));
    const QPixmap greyed = renderCaptionWithIcon(
        text, icon, m_expandButton->font(),
        pal.color(QPalette::Disabled, QPalette::ButtonText));

    QIcon buttonIcon;
    buttonIcon.addPixmap(normal, QIcon::Normal);
    buttonIcon.addPixmap(greyed, QIcon::Disabled);
    m_expandButton->setText(QString());
    m_expandButton->setIcon(buttonIcon);
    const qreal dpr = normal.devicePixelRatio();
    m_expandButton->setIconSize(QSize(
        static_cast<int>(normal.width() / dpr),
        static_cast<int>(normal.height() / dpr)));
}

void HeaderBar::handleEvent(std::shared_ptr<const GameSpeedChangedEvent> event)
{
    for (int i = 0; i < kSpeedCount; ++i)
    {
        m_speedButtons[static_cast<std::size_t>(i)]->setChecked(
            std::abs(kSpeeds[i] - event->speed) < 0.001);
    }
}

void HeaderBar::handleEvent(std::shared_ptr<const BossWaveUpdatedEvent> /*event*/)
{
    const Tick countdownTicks = m_sim->getBossCountdownTicks();
    const int  bossSeconds    = static_cast<int>(
        ticksToSeconds(countdownTicks > 0 ? countdownTicks : 0));
	m_bossWaveLabel->setText(
		tr("Boss Wave #%1")
		.arg(m_sim->getBossWaveCounter()));
	m_nextBossLabel->setText(
		tr("Next boss: %1:%2")
		.arg(bossSeconds / 60)
		.arg(bossSeconds % 60, 2, 10, QChar('0')));
}

void HeaderBar::handleEvent(std::shared_ptr<const ArtifactCountChangedEvent> /*event*/)
{
    m_artifactsLabel->setText(
        tr("Artifacts: %1/%2")
            .arg(m_sim->getArtifactCount())
            .arg(m_sim->getConfig().world.artifacts.artifactWinCount));
}

void HeaderBar::onSpeedButton(int index)
{
    if (index >= 0 && index < kSpeedCount)
    {
        EventManager::getInstance()->sendEventImmediately(
            std::make_shared<SpeedChangeRequestedEvent>(kSpeeds[index]));
    }
}
