#pragma once

#include <variant>

#include "app/GameContext.hpp"
#include "app/GameState.hpp"
#include "app/LobbyState.hpp"
#include "debug/DebugUI.hpp"
#include "debug/metrics/NetworkMetrics.hpp"
#include "debug/tools/PerformanceStatsGui.hpp"
#include "draw/WorldRenderer.hpp"
#include "io/InputState.hpp"
#include "runtime/LockStep.hpp"
#include "world/JoltPhysicsWorld.hpp"
#include "world/World.hpp"

#include "messages/PlayerMoveMessage.hpp"

namespace tw {

/**
 * Handles runtime of the client application.
 */
class Runtime {
private:
    io::Files m_files;
    std::unique_ptr<lft::win::Window> m_window;
    tw::World m_world;
    JoltPhysicsWorld m_physics_world;
    tw::drw::WorldRenderer m_world_renderer;
    tw::io::InputManager m_input_manager;
    tw::dbg::NetworkMetrics m_network_metrics;
    tw::LockStep m_lockstep;

    /**
     * Declared before the state so panels the state owns can still be taken out
     * of the menu while the state is being torn down.
     */
    tw::dbg::DebugUI m_debug_ui;

    tw::dbg::tools::PerformanceStatsGui m_perf_stats;
    std::variant<tw::app::LobbyState, tw::app::GameState> m_state;
    bool m_is_running;
    std::unordered_map<uint32_t, entt::entity> m_players;

    void send_player_positions();

    app::GameContext context();

    void update_state(double delta_time);

public:
    const bool is_running() const {
        return m_is_running;
    }

    Runtime(int argc, char** argv);
    ~Runtime();

    void run();
};

}
