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

#include "ScoutCommandFactory.hpp"

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

namespace shardok {

[[nodiscard]] auto CanScout(const SettingsGetter& settings, const Unit* unit) -> bool;
[[nodiscard]] auto GetScoutingOdds(
        const SettingsGetter& settings,
        const Terrain* scoutedTerrain,
        double agility,
        double intelligence) -> PercentileRollOdds;
[[nodiscard]] auto ScoutTerrainModifier(const Terrain* terrain) -> int;

void ScoutCommandFactory::AddAvailableScoutCommands(
        CommandList& commands,
        const Unit* unit,
        const ActionPoints remainingActionPoints,
        const Coords& position,
        const HexMap* hexMap,
        const Units* units) const {
    if (CanScout(settings, unit)) {
        const auto& hero = unit->attached_hero();

        const auto costForScout =
                settings.ActionCostFor(settings.Backing().scout_action_point_cost());
        const int scoutRange = settings.Backing().scout_range();

        const Coords unitLocation = unit->location();

        if (costForScout.IsPossible(remainingActionPoints)) {
            for (int distance = 1; distance <= scoutRange; distance++) {
                CoordsSet coordsSet = TilesWithExactDistance(hexMap, unitLocation, distance);

                for (const Coords& scoutCoords : coordsSet) {
                    PercentileRollOdds difficulty = GetScoutingOdds(
                            settings,
                            GetTerrain(hexMap, scoutCoords),
                            hero.agility(),
                            hero.wisdom());

                    commands.push_back(std::make_shared<ScoutCommand>(
                            settings.ActionCostFor(settings.Backing().scout_action_point_cost()),
                            unit->player_id(),
                            unit->unit_id(),
                            scoutCoords,
                            difficulty,
                            settings.Backing().scout_direct_knowledge_increment(),
                            settings.Backing().scout_adjacent_knowledge_increment(),
                            settings.Backing().scout_agility_xp(),
                            settings.Backing().scout_wisdom_xp()));
                }
            }
        }
    }
}
void ScoutCommandFactory::AddAvailableCommands(CommandList& commands, const CommandParams& params)
        const {
    AddAvailableScoutCommands(
            commands,
            params.unit,
            params.remainingActionPoints,
            params.position,
            params.hexMap,
            params.units);
}

auto CanScout(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_RANGER &&
           settings.GetBattalionType(unit->battalion().type())->allowsStealth;
}

auto GetScoutingOdds(
        const SettingsGetter& settings,
        const Terrain* scoutedTerrain,
        double agility,
        double intelligence) -> PercentileRollOdds {
    return MakeOdds(
            settings.Backing().scout_base_odds(),
            ScoutTerrainModifier(scoutedTerrain),
            0 /* weather */,
            0 /* wind */,
            {agility, agility, intelligence});
}

auto ScoutTerrainModifier(const Terrain* terrain) -> int {
    const int baseVal = terrain->modifier().castle().present() ? -20 : 0;

    switch (terrain->type()) {
        case net::eagle0::shardok::storage::fb::Terrain_::Type_FOREST:
        case net::eagle0::shardok::storage::fb::Terrain_::Type_SWAMP: {
            return baseVal + 10;
        }

        case net::eagle0::shardok::storage::fb::Terrain_::Type_STILL_WATER:
        case net::eagle0::shardok::storage::fb::Terrain_::Type_RIVER: {
            return baseVal - 20;
        }

        default: {
            return baseVal;
        }
    }
}
}  // namespace shardok
