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

#include "BuildBridgeCommand.hpp"

#include "src/main/cpp/net/eagle0/shardok/library/actions/ActionRequiresHeroException.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/protobuf/net/eagle0/shardok/common/tile_modifier.pb.h"

namespace shardok {

using net::eagle0::shardok::common::TileModifier;

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

auto BuildBridgeCommand::InternalExecuteWithRoll(
        const GameStateW& currentState,
        const std::shared_ptr<RandomGenerator>& generator,
        const std::optional<int32_t> roll) const -> vector<ActionResult> {
    const auto successRoll = 101 - roll.value_or(generator->Percentile());
    return ExecuteWithRoll(currentState, successRoll);
}

auto BuildBridgeCommand::ExecuteWithRoll(const GameStateW& currentState, double roll) const
        -> vector<ActionResult> {
    const auto* actor = currentState->units()->Get(actorId);
    if (!actor->has_attached_hero()) { throw ActionRequiresHeroException("build bridge"); }

    auto actorAfter = *actor;
    MutatingSpendActionPoints(&actorAfter, cost);
    MutatingBumpAgilityXp(&actorAfter, agilityXp);
    MutatingBumpStrengthXp(&actorAfter, strengthXp);
    MutatingBumpAllKnowledgeToMinimum(&actorAfter, minimumKnowledgeAfter);

    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.set_type(ActionType::BUILD_BRIDGE);

    auto newTileModifier = targetTerrain.modifier();
    const auto& actingHero = actor->attached_hero();

    auto maxBridgeIntegrity =
            (actingHero.agility() + actingHero.strength() + actingHero.wisdom()) / 3.0;
    if (hasNearbyForest) { maxBridgeIntegrity *= 2; }

    SetBridge(newTileModifier, (roll < 10.0 ? 10.0 : roll) * maxBridgeIntegrity / 100.0);
    *result.add_changed_tile_modifiers() = MakeTmc(target, newTileModifier);

    return {result};
}

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

    proto.set_player(GetPlayerId());
    proto.set_type(net::eagle0::shardok::common::BUILD_BRIDGE_COMMAND);
    proto.mutable_actor()->set_value(actorId);
    *proto.mutable_target() = ToCoordsProto(target);
    proto.mutable_roll_request()->set_roll_type(net::eagle0::shardok::api::ROLL_TYPE_D100);
    proto.mutable_roll_request()->set_command_type(
            net::eagle0::shardok::common::BUILD_BRIDGE_COMMAND);
    proto.mutable_roll_request()->set_acting_unit_id(actorId);

    return proto;
}

}  // namespace shardok
