//
// Created by Dan Crosby on 5/27/20.
//

#include "ExtinguishFireCommand.hpp"

#include <utility>

#include "src/main/cpp/net/eagle0/shardok/library/actions/ActionRequiresHeroException.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/fb_helpers/TileModifierHelpers.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/map/TileModifierWithCoords.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/unit/Unit.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/ActionResultFlatbufferHelpers.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/view_filters/OddsFilter.hpp"

namespace shardok {

using net::eagle0::shardok::common::ActionType;
using TileModifierProto = net::eagle0::shardok::common::TileModifier;

auto ExtinguishFireCommand::InternalExecute(
        const GameStateW& currentState,
        const std::shared_ptr<RandomGenerator>& generator) const -> vector<ActionResult> {
    return ExecuteWithRoll(currentState, generator->Percentile());
}

[[nodiscard]] auto ExtinguishFireCommand::ExecuteWithRoll(
        const GameStateW& currentState,
        double roll) const -> vector<ActionResult> {
    const Unit* actorBefore = currentState->units()->Get(actorId);
    if (!actorBefore->has_attached_hero()) { throw ActionRequiresHeroException("extinguish fire"); }

    auto actorAfter = *actorBefore;
    MutatingSpendActionPoints(
            &actorAfter,
            ActionCost::UsesAllActionCost(settings.Backing().extinguish_action_point_cost()));
    actorAfter.mutate_hidden(false);
    MutatingBumpAgilityXp(&actorAfter, settings.Backing().extinguish_agility_xp());
    MutatingBumpWisdomXp(&actorAfter, settings.Backing().extinguish_wisdom_xp());

    ActionResult result{};
    result.mutable_player()->set_value(GetPlayerId());
    result.mutable_actor()->set_value(actorAfter.unit_id());
    *result.mutable_target_coords() = ToCoordsProto(target);
    AddChangedUnit(result, actorAfter);

    result.mutable_roll()->set_value(roll);
    *result.mutable_odds() = odds;

    if (PercentileRollSucceeds(odds, roll)) {
        TileModifierProto newTileModifier =
                fb::ToTileModifierProto(GetTerrain(currentState->hex_map(), target)->modifier());
        MutatingSetFire(newTileModifier, false);

        result.set_type(ActionType::EXTINGUISH_FIRE);
        *result.add_changed_tile_modifiers() = MakeTmc(target, newTileModifier);

        return {result};
    } else {
        result.set_type(ActionType::EXTINGUISH_FIRE_FAILED);

        return {result};
    }
}

ExtinguishFireCommand::ExtinguishFireCommand(
        const SettingsGetter& getter,
        const PlayerId playerId,
        const UnitId actorId,
        const Coords target,
        PercentileRollOdds odds)
    : ShardokCommand(playerId),
      settings(getter),
      actorId(actorId),
      target(target),
      odds(std::move(odds)) {}

auto ExtinguishFireCommand::GetCommandProto() const -> CommandProto {
    CommandProto proto{};

    proto.set_player(GetPlayerId());
    proto.set_type(net::eagle0::shardok::common::EXTINGUISH_FIRE_COMMAND);
    proto.mutable_actor()->set_value(actorId);
    *proto.mutable_target() = ToCoordsProto(target);
    *proto.mutable_odds() = OddsFilteredForPlayer(odds, GetPlayerId());

    return proto;
}

int32_t ExtinguishFireCommand::GetOddsPercentile() const {
    return OddsFilteredForPlayer(odds, GetPlayerId()).success_chance();
}

}  // namespace shardok
