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

#include "FreezeWaterCommand.hpp"

#include "src/main/cpp/net/eagle0/shardok/library/actions/ActionRequiresHeroException.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/actions/UndeadFrozenAction.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/view_filters/OddsFilter.hpp"

namespace shardok {

using net::eagle0::shardok::common::ActionType;
using net::eagle0::shardok::storage::ResolvedUnit;

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

[[nodiscard]] auto FreezeWaterCommand::ExecuteWithRoll(
        double roll,
        const GameStateW& currentState,
        const std::shared_ptr<RandomGenerator>& generator) const -> vector<ActionResult> {
    const Unit* actor = currentState->units()->Get(actorId);
    if (!actor) { throw ActionRequiresHeroException("freeze water"); }

    auto actorAfter = *actor;
    MutatingSpendActionPoints(&actorAfter, cost);
    MutatingBumpWisdomXp(&actorAfter, wisdomXp);
    MutatingBumpAllKnowledgeToMinimum(&actorAfter, minimumKnowledgeAfter);

    ActionResult freezeResult{};
    freezeResult.mutable_player()->set_value(GetPlayerId());
    freezeResult.mutable_actor()->set_value(actorAfter.unit_id());
    *freezeResult.mutable_target_coords() = ToCoordsProto(target);
    AddChangedUnit(freezeResult, actorAfter);
    freezeResult.mutable_roll()->set_value(roll);
    *freezeResult.mutable_odds() = odds;

    vector<ActionResult> allResults{};

    if (PercentileRollSucceeds(odds, roll)) {
        freezeResult.set_type(ActionType::FREEZE_WATER);

        TileModifierProto newTileModifier = fb::ToTileModifierProto(targetTerrain->modifier());
        IncreaseFrozen(newTileModifier, freezeWaterIntegrity);
        *freezeResult.add_changed_tile_modifiers() = MakeTmc(target, newTileModifier);
    } else {
        freezeResult.set_type(ActionType::FREEZE_WATER_FAILED);
    }

    allResults.push_back(freezeResult);
    if (occupantId != -1 && PercentileRollSucceeds(odds, roll)) {
        auto destroyedResults = UndeadFrozenAction(currentState->units()->Get(occupantId))
                                        .Execute(currentState, generator);
        allResults.insert(allResults.end(), destroyedResults.begin(), destroyedResults.end());
    }

    return allResults;
}

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

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

    return proto;
}

auto FreezeWaterCommand::GetOddsPercentile() const -> int32_t {
    return OddsFilteredForPlayer(odds, GetPlayerId()).success_chance();
}

}  // namespace shardok
