#include "AdvanceExecutor.h"

#include <optional>

#include <QVector2D>

#include "AdvanceBehavior.h"
#include "BehaviorKind.h"
#include "Centroid.h"
#include "EntityAdmin.h"
#include "FactionComponent.h"
#include "HealthComponent.h"
#include "HqProxyComponent.h"
#include "MovementIntentComponent.h"
#include "PositionComponent.h"
#include "SelectedBehaviorComponent.h"
#include "StationBodyComponent.h"
#include "tracing.h"

void AdvanceExecutor::execute(EntityAdmin& admin)
{
    TRACE();

    // Centroid of each faction's alive defence stations. In the arena the HQ is
    // spawned as a station, so it is part of this centroid; in the main game the
    // enemy side has only its defence stations.
    Centroid enemyStations;
    Centroid playerStations;
    admin.forEach<StationBodyComponent, PositionComponent, FactionComponent, HealthComponent>(
        [&enemyStations, &playerStations](entt::entity /*e*/,
            const StationBodyComponent& /*sb*/, const PositionComponent& pos,
            const FactionComponent& faction, const HealthComponent& health)
        {
            if (health.hp <= 0.0f) { return; }
            Centroid& centroid = faction.isEnemy ? enemyStations : playerStations;
            centroid.add(pos.value);
        });

    // Fallback target per faction: the HQ proxy (main game only), used when a side
    // has lost all of its defence stations.
    Centroid enemyHq;
    Centroid playerHq;
    admin.forEach<HqProxyComponent, PositionComponent, FactionComponent, HealthComponent>(
        [&enemyHq, &playerHq](entt::entity /*e*/, const HqProxyComponent& /*hq*/,
            const PositionComponent& pos, const FactionComponent& faction,
            const HealthComponent& health)
        {
            if (health.hp <= 0.0f) { return; }
            Centroid& centroid = faction.isEnemy ? enemyHq : playerHq;
            centroid.add(pos.value);
        });

    const std::optional<QVector2D> enemyStationCenter  = enemyStations.value();
    const std::optional<QVector2D> playerStationCenter = playerStations.value();
    const std::optional<QVector2D> enemyHqCenter       = enemyHq.value();
    const std::optional<QVector2D> playerHqCenter      = playerHq.value();

    admin.forEach<AdvanceBehavior, SelectedBehaviorComponent, PositionComponent,
                  FactionComponent, MovementIntentComponent>(
        [&](entt::entity /*e*/, const AdvanceBehavior& /*advance*/,
            const SelectedBehaviorComponent& selected, const PositionComponent& pos,
            const FactionComponent& faction, MovementIntentComponent& intent)
        {
            if (selected.winner != BehaviorKind::Advance) { return; }

            // Aim at the center between the opposing side's defence stations; fall
            // back to the opposing HQ, then to an off-world point in the advance
            // direction so the ship keeps moving when no target structure exists.
            const std::optional<QVector2D>& stationCenter =
                faction.isEnemy ? playerStationCenter : enemyStationCenter;
            const std::optional<QVector2D>& hqCenter =
                faction.isEnemy ? playerHqCenter : enemyHqCenter;

            QVector2D target;
            if (stationCenter)
            {
                target = *stationCenter;
            }
            else if (hqCenter)
            {
                target = *hqCenter;
            }
            else
            {
                target = faction.isEnemy
                    ? QVector2D(-10000.0f, pos.value.y())
                    : QVector2D(pos.value.x() + 1000.0f, pos.value.y());
            }
            intent = MovementIntentComponent{true, target};
        });
}
