//
//  FleeCommandFactory.cpp
//  eagle0
//
//  Created by Dan Crosby on 12/30/19.
//  Copyright © 2019 none. All rights reserved.
//

#include "FleeCommandFactory.hpp"

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

namespace shardok {

FleeCommandFactory::FleeCommandFactory(const SettingsGetter &getter) : settings(getter) {}

void FleeCommandFactory::AddAvailableFleeCommands(
        CommandList &commands,
        const Unit *unit,
        const Units *allUnits,
        const HexMap *map,
        const vector<PlayerId> &allyPids,
        const ActionPoints remainingActionPoints) const {
    if (!settings.ActionCostFor(settings.Backing().flee_action_point_cost())
                 .IsPossible(remainingActionPoints))
        return;
    // Only heroes can flee
    if (!unit->has_attached_hero()) return;
    PercentileRollOdds odds = MakeOdds(100);

    if (!unit->attached_hero().is_vip()) {
        int32_t adjacentEnemiesMod = 0;
        int32_t adjacentFriendliesMod = 0;
        int32_t adjacentImpassableMod = 0;
        auto perAdjacentFriendly = settings.Backing().flee_success_per_adjacent_friendly();
        auto perAdjacentEnemy = settings.Backing().flee_success_per_adjacent_enemy();
        auto perImpassableTile = settings.Backing().flee_success_per_adjacent_impassable_tile();

        for (const auto &adjTile : HexMapUtils::GetAdjacentTiles(map, unit->location())) {
            const auto *occupant = Occupant(allUnits, adjTile.coords);
            if (occupant && !occupant->hidden()) {
                if (common::Contains(allyPids, occupant->player_id())) {
                    adjacentFriendliesMod += perAdjacentFriendly;
                } else {
                    adjacentEnemiesMod += perAdjacentEnemy;
                }
            } else if (!IsTraversible(*GetTerrain(map, adjTile.coords))) {
                adjacentImpassableMod += perImpassableTile;
            }
        }

        auto adjustmentForTwoAway = settings.Backing().flee_success_adjustment_for_two_away();
        for (const auto &twoAwayCoords : TilesWithExactDistance(map, unit->location(), 2)) {
            const auto *occupant = Occupant(allUnits, twoAwayCoords);
            if (occupant && !occupant->hidden()) {
                if (common::Contains(allyPids, occupant->player_id())) {
                    adjacentFriendliesMod += int32_t(perAdjacentFriendly * adjustmentForTwoAway);
                } else {
                    adjacentEnemiesMod += int32_t(perAdjacentEnemy * adjustmentForTwoAway);
                }
            }
        }

        odds = MakeOdds(
                settings.Backing().flee_success_base_chance(),
                0,
                0,
                0,
                0,
                {MakeOtherFactor(adjacentFriendliesMod, "adjacentFriendlies"),
                 MakeOtherFactor(adjacentEnemiesMod, "adjacentEnemies"),
                 MakeOtherFactor(adjacentImpassableMod, "adjacentImpassable")});
    }

    if (unit->can_flee()) {
        commands.push_back(std::make_shared<FleeCommand>(
                settings.ActionCostFor(settings.Backing().flee_action_point_cost()),
                unit->player_id(),
                unit->unit_id(),
                odds));
    } else {
        commands.push_back(std::make_shared<BecomeOutlawCommand>(
                settings.ActionCostFor(settings.Backing().flee_action_point_cost()),
                unit->player_id(),
                unit->unit_id(),
                odds));
    }
}
void FleeCommandFactory::AddAvailableCommands(
        CommandList &commands,
        const CommandFactory::CommandParams &params) const {
    AddAvailableFleeCommands(
            commands,
            params.unit,
            params.units,
            params.hexMap,
            params.allyPids,
            params.remainingActionPoints);
}

}  // namespace shardok
