#include "features/world/WorldFeature.h"

#include <utility>

#include "foundation/diagnostics/ExecutionPath.h"

namespace ta::features::world
{
    void WorldFeature::initialize()
    {
        settings_cache_.refresh(settings_);
    }

    void WorldFeature::on_application_tick()
    {
        const WorldSettings next = settings_.read();
        settings_cache_.publish_if_changed(next);
    }

    void WorldFeature::on_game_tick()
    {
        gateway_.game_tick(current_settings(), consume_commands());
    }

    void WorldFeature::reset_session() noexcept
    {
        if (auto command = unlock_command_.consume())
            ta::foundation::execution_path::fail(command->trace_id, "WorldFeature", "raid reset before execution");
        if (auto command = teleport_command_.consume())
            ta::foundation::execution_path::fail(command->trace_id, "WorldFeature", "raid reset before execution");
        diagnostics_.reset();
    }

    void WorldFeature::submit(WorldCommand command)
    {
        const char* action = action_name(command.type);
        if (command.trace_id == 0)
            command.trace_id = ta::foundation::execution_path::current_or_begin("World", action);

        ta::foundation::CoalescingCommandSlot<WorldCommand>& slot = command.type == WorldCommandType::UnlockDoors
            ? unlock_command_
            : teleport_command_;

        if (command.type == WorldCommandType::UnlockDoors)
            diagnostics_.mark_unlock_requested();
        else
            diagnostics_.mark_teleport_requested();

        ta::foundation::execution_path::step(command.trace_id, "WorldFeature", "queue command", action);
        if (auto replaced = slot.submit(std::move(command)))
            ta::foundation::execution_path::fail(replaced->trace_id, "WorldFeature", "superseded by a newer world command");
    }

    WorldSettings WorldFeature::current_settings() const
    {
        return settings_cache_.current_or_default();
    }

    std::vector<WorldCommand> WorldFeature::consume_commands()
    {
        std::vector<WorldCommand> commands;
        if (auto command = unlock_command_.consume())
        {
            diagnostics_.mark_unlock_consumed();
            commands.push_back(std::move(*command));
        }
        if (auto command = teleport_command_.consume())
        {
            diagnostics_.mark_teleport_consumed();
            commands.push_back(std::move(*command));
        }
        return commands;
    }

    const char* WorldFeature::action_name(const WorldCommandType type) noexcept
    {
        switch (type)
        {
        case WorldCommandType::UnlockDoors: return "unlock doors";
        case WorldCommandType::TeleportNormalItems: return "teleport normal items";
        case WorldCommandType::TeleportWishlistItems: return "teleport wishlist items";
        case WorldCommandType::TeleportQuestItems: return "teleport quest items";
        case WorldCommandType::TeleportCorpses: return "teleport corpses";
        default: return "world command";
        }
    }
}
