//
// Created by Dan Crosby on 3/23/20.
//

#include "UnitFilter.hpp"

#include "HeroFilter.hpp"
#include "src/main/cpp/net/eagle0/common/ContainerUtils.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/map/Coordinates.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/unit/Unit.hpp"
#include "src/main/protobuf/net/eagle0/shardok/common/command_type.pb.h"
#include "src/main/protobuf/net/eagle0/shardok/common/hostility.pb.h"

namespace shardok {

using net::eagle0::shardok::api::UnitView;
using net::eagle0::shardok::common::CommandType;
using std::vector;

auto UnknownUnit(UnitId uid, PlayerId pid, BattalionTypeId bt, int size, bool hidden) -> UnitView;

[[nodiscard]] auto UnitFilteredForPlayer(
        const SettingsGetter &settings,
        const Unit *unit,
        const PlayerId askingPlayer,
        const std::vector<PlayerId> &alliedPids) -> UnitView {
    const bool visibleToAsker =
            (askingPlayer == unit->player_id()) || common::Contains(alliedPids, unit->player_id());

    const int askerKnowledge = OpponentKnowledge(unit, askingPlayer);
    if (unit->hidden() && !visibleToAsker) {
        return UnknownUnit(
                unit->unit_id(),
                unit->player_id(),
                unit->battalion().type(),
                unit->battalion().size(),
                true);
    }
    if (IsUnplaced(unit->location()) && !visibleToAsker) {
        return UnknownUnit(
                unit->unit_id(),
                unit->player_id(),
                unit->battalion().type(),
                unit->battalion().size(),
                false);
    }

    UnitView filtered{};
    if (unit->has_attached_hero()) {
        *filtered.mutable_attached_hero() = HeroFilteredForPlayer(
                settings,
                unit->attached_hero(),
                askerKnowledge,
                visibleToAsker);
    }

    if (askingPlayer == unit->player_id()) {
        filtered.set_hostility_status(net::eagle0::shardok::common::SELF_HOSTILITY);
    } else if (common::Contains(alliedPids, unit->player_id())) {
        filtered.set_hostility_status(net::eagle0::shardok::common::ALLIED_HOSTILITY);
    } else if (unit->player_id() == UNCONTROLLED_PLAYER_ID) {
        filtered.set_hostility_status(net::eagle0::shardok::common::UNCONTROLLED_HOSTILITY);
    } else {
        filtered.set_hostility_status(net::eagle0::shardok::common::ENEMY_HOSTILITY);
    }

    filtered.set_unit_id(unit->unit_id());
    filtered.set_player_id(unit->player_id());
    *filtered.mutable_location() = ToCoordsProto(unit->location());
    filtered.set_hidden(unit->hidden());
    filtered.set_fortified(unit->fortified());

    if (visibleToAsker || askerKnowledge >= settings.Backing().minimum_knowledge_for_hero_stats()) {
        if (unit->can_archery() && unit->volleys_remaining() > 0) {
            filtered.set_can_archery(true);
        }
        if (unit->can_start_fire()) { filtered.set_can_start_fire(true); }

        filtered.mutable_vigor_low()->set_value(
                unit->has_attached_hero() &&
                unit->attached_hero().vigor() < settings.Backing().minimum_vigor_to_act());
    } else if (
            settings.GetBattalionType(unit->battalion().type())->alwaysArcheryCapable &&
            unit->volleys_remaining() > 0) {
        filtered.set_can_archery(true);
    }

    if (visibleToAsker || askerKnowledge >= settings.Backing().minimum_knowledge_for_name()) {
        filtered.mutable_stun_rounds_remaining()->set_value(unit->stun_rounds_remaining());
    } else if (unit->stun_rounds_remaining() > 0) {
        filtered.mutable_stun_rounds_remaining()->set_value(1);
    }

    if (unit->commanding_unit_id() != -1) {
        filtered.mutable_commanding_unit_id()->set_value(unit->commanding_unit_id());
    }

    if (visibleToAsker ||
        askerKnowledge >= settings.Backing().minimum_knowledge_for_battalion_name()) {
        filtered.mutable_battalion()->mutable_eagle_battalion_id()->set_value(
                unit->battalion().eagle_battalion_id());
    }

    if (visibleToAsker ||
        askerKnowledge >= settings.Backing().minimum_knowledge_for_battalion_stats()) {
        filtered.mutable_battalion()->mutable_armament()->set_value(unit->battalion().armament());
        filtered.mutable_battalion()->mutable_training()->set_value(unit->battalion().training());
        filtered.mutable_battalion()->mutable_morale()->set_value(unit->battalion().morale());

        filtered.mutable_volleys_remaining()->set_value(unit->volleys_remaining());

        filtered.mutable_morale_low()->set_value(
                unit->battalion().morale() < settings.Backing().minimum_morale_to_act());

        filtered.mutable_food_remaining()->set_value(unit->food_remaining());
    }

    filtered.mutable_battalion()->set_type(
            static_cast<net::eagle0::shardok::api::BattalionView_BattalionTypeId>(
                    unit->battalion().type()));
    filtered.mutable_battalion()->set_size(unit->battalion().size());

    if (visibleToAsker) {
        filtered.mutable_remaining_action_points()->set_value(unit->remaining_action_points());
    }

    filtered.set_has_moved_in_zoc(unit->has_moved_in_zoc());
    filtered.set_my_knowledge(askerKnowledge);

    if (unit->starting_position_index() != -1) {
        filtered.mutable_starting_position_index()->set_value(unit->starting_position_index());
    }
    filtered.set_can_flee(unit->can_flee());

    return filtered;
}

auto UnknownUnit(
        const UnitId uid,
        const PlayerId pid,
        BattalionTypeId bt,
        const int size,
        const bool hidden) -> UnitView {
    UnitView uv;
    uv.set_unit_id(uid);
    uv.set_player_id(pid);
    uv.mutable_location()->set_row(-1);
    uv.mutable_location()->set_column(-1);
    uv.set_hostility_status(net::eagle0::shardok::common::SELF_HOSTILITY);
    uv.mutable_battalion()->set_type(
            static_cast<net::eagle0::shardok::api::BattalionView_BattalionTypeId>(bt));
    uv.mutable_battalion()->set_size(size);
    uv.set_hidden(hidden);

    uv.set_my_knowledge(0);

    return uv;
}

}  // namespace shardok
