#include "RetreatEvaluator.h"

#include <vector>

#include <QVector2D>

#include "AttackBehavior.h"
#include "BehaviorScores.h"
#include "BehaviorTargeting.h"
#include "EntityAdmin.h"
#include "FactionComponent.h"
#include "HealthComponent.h"
#include "PositionComponent.h"
#include "RepairBehavior.h"
#include "RetreatBehavior.h"
#include "SensorRangeComponent.h"
#include "ShipIdentityComponent.h"
#include "tracing.h"

void RetreatEvaluator::evaluate(EntityAdmin& admin)
{
    TRACE();

    // Snapshot enemy ship positions for threat detection.
    std::vector<QVector2D> enemyShips;
    admin.forEach<ShipIdentityComponent, PositionComponent, FactionComponent>(
        [&enemyShips](entt::entity /*e*/, const ShipIdentityComponent& /*si*/,
                      const PositionComponent& pos, const FactionComponent& f)
        {
            if (f.isEnemy) { enemyShips.push_back(pos.value); }
        });

    // Snapshot repairables so weaponless repair ships can decide whether there is
    // still a damaged ally worth holding ground for.
    const std::vector<RepairableInfo> repairables = buildRepairables(admin);

    admin.forEach<RetreatBehavior, PositionComponent, HealthComponent,
                  SensorRangeComponent, FactionComponent>(
        [&](entt::entity e, RetreatBehavior& retreat, const PositionComponent& pos,
            const HealthComponent& health, const SensorRangeComponent& sensor,
            const FactionComponent& faction)
        {
            const bool lowHp = (health.maxHp > 0.0f)
                && (health.hp / health.maxHp < retreat.retreatHpFraction);

            bool threatened = false;
            const bool hasWeapons = admin.hasAll<AttackBehavior>(e);
            if (!hasWeapons)
            {
                bool enemyInRange = false;
                for (const QVector2D& enemy : enemyShips)
                {
                    if ((enemy - pos.value).length() <= sensor.value_tiles)
                    {
                        enemyInRange = true;
                        break;
                    }
                }

                // A weaponless ship with a repair tool holds its ground while a
                // damaged ally remains within sensor range; it only flees once
                // there is nothing left to repair.
                bool repairTargetInRange = false;
                if (enemyInRange && admin.hasAll<RepairBehavior>(e))
                {
                    for (const RepairableInfo& r : repairables)
                    {
                        if (r.entity == e) { continue; }
                        if (r.isEnemy != faction.isEnemy) { continue; }
                        if (r.hp <= 0.0f || r.hp >= r.maxHp) { continue; }
                        if ((r.position - pos.value).length() <= sensor.value_tiles)
                        {
                            repairTargetInRange = true;
                            break;
                        }
                    }
                }

                threatened = enemyInRange && !repairTargetInRange;
            }

            retreat.score = (lowHp || threatened)
                ? BehaviorScores::kRetreat
                : BehaviorScores::kInactive;
        });
}
