//
// Created by Dan Crosby on 11/9/22.
//

#include "src/main/cpp/net/eagle0/shardok/library/action_result_applier/GameStateCopier.hpp"

#include <cstring>

namespace shardok {

using Unit = net::eagle0::shardok::storage::fb::Unit;

auto CopyWithExtraUnits(const GameStateW& original, int additionalCount) -> GameStateW {
    const auto* startGS = original.Get();

    net::eagle0::shardok::storage::fb::GameStateT endGST;
    startGS->UnPackTo(&endGST);

    // Add the requested units plus some extra slack for future use
    int extraSlack = std::max(5, additionalCount * 2);
    for (int i = 0; i < additionalCount + extraSlack; i++) {
        Unit unit;
        unit.mutate_unit_id(static_cast<int16_t>(endGST.units.size()));
        if (i < additionalCount) {
            unit.mutate_status(net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT);
        } else {
            unit.mutate_status(net::eagle0::shardok::storage::fb::UnitStatus_RESERVED_SLOT);
            // Set safe defaults for reserved slots
            unit.mutate_player_id(-1);
            unit.mutate_eagle_player_id(-1);
            unit.mutable_location().mutate_row(-1);
            unit.mutable_location().mutate_column(-1);
        }
        endGST.units.push_back(unit);
    }

    // Copy occupied tiles bitfield from original GameState (much faster than O(n) rebuild)
    if (startGS->occupied_tiles() && startGS->hex_map()) {
        const size_t originalBitfieldSize = startGS->occupied_tiles()->size();
        endGST.occupied_tiles.resize(originalBitfieldSize);

        // Fast O(bitfield_bytes) copy instead of O(units) rebuild
        std::memcpy(
                endGST.occupied_tiles.data(),
                startGS->occupied_tiles()->data(),
                originalBitfieldSize);
    } else if (endGST.hex_map) {
        // Fallback: create new bitfield only if original doesn't have one
        const int16_t rowCount = endGST.hex_map->row_count;
        const int16_t columnCount = endGST.hex_map->column_count;
        const size_t mapSize = rowCount * columnCount;
        const size_t bitfieldSize = (mapSize + 7) / 8;  // Ceiling division
        endGST.occupied_tiles.resize(bitfieldSize, 0);  // Initialize all bits to 0 (empty)

        // Populate bitfield based on unit positions (O(n) fallback)
        for (const auto& unit : endGST.units) {
            if (unit.status() == net::eagle0::shardok::storage::fb::UnitStatus_NORMAL_UNIT) {
                const auto& location = unit.location();
                if (location.row() >= 0 && location.row() < rowCount && location.column() >= 0 &&
                    location.column() < columnCount) {
                    const size_t tileIndex = location.row() * columnCount + location.column();
                    const size_t byteIndex = tileIndex / 8;
                    const size_t bitOffset = tileIndex % 8;
                    endGST.occupied_tiles[byteIndex] |= (1 << bitOffset);  // Set the bit
                }
            }
        }
    }

    flatbuffers::FlatBufferBuilder newFbb;
    newFbb.ForceDefaults(true);
    newFbb.Finish(net::eagle0::shardok::storage::fb::GameState::Pack(newFbb, &endGST));

    return GameStateW(newFbb);
}
}  // namespace shardok
