//
// Created by Dan Crosby on 3/13/21.
//

#include "FearCommandFactory.hpp"

#include "src/main/cpp/net/eagle0/shardok/library/commands/FearCommand.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"

namespace shardok {

[[nodiscard]] auto CanFear(const SettingsGetter &settings, const Unit *unit) -> bool;

void FearCommandFactory::AddAvailableFearCommands(
        CommandList &commands,
        const Unit *unit,
        const ActionPoints remainingActionPoints,
        const Coords &position,
        const HexMap *hexMap,
        const Units *units,
        const vector<PlayerId> &allyPids) const {
    const auto &hero = unit->attached_hero();

    if (CanFear(settings, unit)) {
        const auto costForFear =
                settings.ActionCostFor(settings.Backing().fear_action_point_cost());

        if (costForFear.IsPossible(remainingActionPoints)) {
            const int range = settings.Backing().fear_range();
            for (int distance = 1; distance <= range; distance++) {
                const auto coordsSet = TilesWithExactDistance(hexMap, position, distance);

                for (const Coords &fearCoords : coordsSet) {
                    const auto *enemyOccupantOptional =
                            KnownEnemyOccupant(unit->player_id(), units, allyPids, fearCoords);
                    if (!enemyOccupantOptional) continue;
                    const Unit *potentialTarget = enemyOccupantOptional;
                    if (!settings.GetBattalionType(potentialTarget->battalion().type())
                                 ->adjustsMorale)
                        continue;

                    double believedEnemyCharisma = 50.0;
                    if (enemyOccupantOptional->has_attached_hero() &&
                        enemyOccupantOptional->opponent_knowledge()->Get(unit->player_id()) >=
                                settings.Backing().minimum_knowledge_for_hero_stats()) {
                        believedEnemyCharisma = enemyOccupantOptional->attached_hero().charisma();
                    }

                    PercentileRollOdds believedDifficulty = FearCommand::GetFearOdds(
                            settings.Backing().fear_base_odds(),
                            hero.charisma(),
                            hero.wisdom(),
                            believedEnemyCharisma);

                    commands.push_back(std::make_shared<FearCommand>(
                            settings.ActionCostFor(settings.Backing().fear_action_point_cost()),
                            unit->player_id(),
                            unit->unit_id(),
                            fearCoords,
                            potentialTarget->unit_id(),
                            believedDifficulty,
                            settings.Backing().fear_base_odds(),
                            settings.Backing().fear_debuf(),
                            settings.Backing().fear_failed_buf(),
                            settings.Backing().fear_charisma_xp(),
                            settings.Backing().minimum_knowledge_for_profession()));
                }
            }
        }
    }
}
void FearCommandFactory::AddAvailableCommands(
        CommandList &commands,
        const CommandFactory::CommandParams &params) const {
    AddAvailableFearCommands(
            commands,
            params.unit,
            params.remainingActionPoints,
            params.position,
            params.hexMap,
            params.units,
            params.allyPids);
}

auto CanFear(const SettingsGetter &settings, const Unit *unit) -> bool {
    if (!unit->has_attached_hero()) return false;
    return unit->attached_hero().profession_info().profession() ==
                   net::eagle0::shardok::storage::fb::Profession_NECROMANCER &&
           settings.GetBattalionType(unit->battalion().type())->allowsCasting;
}

}  // namespace shardok