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

#include "GameStateFilter.hpp"

#include "UnitFilter.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/fb_helpers/GameStateHelpers.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/fb_helpers/HexMapHelpers.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/PlayerUtils.hpp"

namespace shardok {

using net::eagle0::shardok::api::GameStateView;
using net::eagle0::shardok::api::UnitView;

auto GameStateFilteredForPlayer(
        const SettingsGetter& settings,
        const GameState* gameState,
        const PlayerId askingPlayer) -> GameStateView {
    GameStateView gsv{};

    gsv.set_asking_player(askingPlayer);

    gsv.set_game_id(gameState->game_id()->str());
    gsv.set_month(gameState->month());

    for (const auto& info : *gameState->player_infos()) {
        *gsv.add_player_infos() = fb::ToPlayerInfoProto(info);
    }

    const auto& alliedPids = AlliedPids(gameState, askingPlayer);

    for (const auto& unit : *gameState->units()) {
        switch (unit->status()) {
            case net::eagle0::shardok::storage::fb::UnitStatus_RESERVE_UNIT:
            case net::eagle0::shardok::storage::fb::UnitStatus_NEVER_ENTERED_UNIT:
                *gsv.add_reserve_units() =
                        UnitFilteredForPlayer(settings, unit, askingPlayer, alliedPids);
                break;

            case net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT:
                *gsv.add_units() = UnitFilteredForPlayer(settings, unit, askingPlayer, alliedPids);
                break;

            case net::eagle0::shardok::storage::fb::UnitStatus_DESTROYED_SUMMONED_UNIT:
            case net::eagle0::shardok::storage::fb::UnitStatus_CAPTURED_UNIT:
            case net::eagle0::shardok::storage::fb::UnitStatus_FLED_UNIT:
            case net::eagle0::shardok::storage::fb::UnitStatus_RETREATED_UNIT:
            case net::eagle0::shardok::storage::fb::UnitStatus_OUTLAWED_UNIT:
            case net::eagle0::shardok::storage::fb::UnitStatus_RESERVED_SLOT: break;

            case net::eagle0::shardok::storage::fb::UnitStatus_UNKNOWN_UNIT:
                throw ShardokInternalErrorException("Unknown unit status");
        }
    }

    const auto criticalTiles = GetCriticalTileLocations(gameState->hex_map());

    *gsv.mutable_hex_map() = fb::ToProto(gameState->hex_map());
    *gsv.mutable_weather() = fb::ToWeatherProto(gameState->weather());
    gsv.set_critical_tile_count(criticalTiles.size());
    gsv.set_current_round(gameState->current_round());
    gsv.set_current_player(gameState->current_player());

    if (gameState->eligible_charger_id() > -1) {
        gsv.mutable_eligible_charger_id()->set_value(gameState->eligible_charger_id());
    }
    gsv.mutable_possible_chargee_ids()->Add(
            gameState->possible_chargee_ids()->begin(),
            gameState->possible_chargee_ids()->end());

    *gsv.mutable_status() = fb::ToProto(gameState->status());

    for (const auto* player : *gameState->player_infos()) {
        auto* newTotal = gsv.add_player_totals();
        newTotal->set_player_id(player->player_id());

        int heroCount = 0;
        int troopCount = 0;
        int criticalTileCount = 0;

        for (const auto* unit : *gameState->units()) {
            if (unit->player_id() != player->player_id()) continue;

            switch (unit->status()) {
                case net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT:
                    if (criticalTiles.Contains(unit->location())) { criticalTileCount++; }
                    // drop through deliberately
                case net::eagle0::shardok::storage::fb::UnitStatus_RESERVE_UNIT:
                case net::eagle0::shardok::storage::fb::UnitStatus_NEVER_ENTERED_UNIT:
                    heroCount++;
                    troopCount += unit->battalion().size();
                    break;

                case net::eagle0::shardok::storage::fb::UnitStatus_DESTROYED_SUMMONED_UNIT:
                case net::eagle0::shardok::storage::fb::UnitStatus_CAPTURED_UNIT:
                case net::eagle0::shardok::storage::fb::UnitStatus_FLED_UNIT:
                case net::eagle0::shardok::storage::fb::UnitStatus_RETREATED_UNIT:
                case net::eagle0::shardok::storage::fb::UnitStatus_OUTLAWED_UNIT:
                case net::eagle0::shardok::storage::fb::UnitStatus_RESERVED_SLOT:
                case net::eagle0::shardok::storage::fb::UnitStatus_UNKNOWN_UNIT: break;
            }
        }
        newTotal->set_hero_count(heroCount);
        newTotal->set_troop_count(troopCount);
        newTotal->set_occupied_critical_tile_count(criticalTileCount);
    }

    return gsv;
}
}  // namespace shardok