#include <stddef.h>
#include <stdint.h>

#include "coduo_engine_structs.h"
#include "../core_cvar/cvar_private.h"
#include "../core_runtime/core_runtime_private.h"
#include "sv_globals_private.h"

#define SV_GAME_BRIDGE_COM_ERROR_DROP 1

static const char svSvEntityForGentityBadEntityError[] =
    "\x15"
    "SV_SvEntityForGentity: bad gEnt";
static const char svGetServerinfoBadBufferSizeError[] =
    "\x15"
    "SV_GetServerinfo: bufferSize == %i";
static const char svGetUsercmdBadClientNumError[] =
    "\x15"
    "SV_GetUsercmd: bad clientNum:%i";

int32_t SV_NumForGentity(const sharedEntity_t *gentity)
{
    return (int32_t)(((const uint8_t *)gentity -
                      (const uint8_t *)sv_gameData.gentities) /
                     sv_gameData.gentitySize);
}

sharedEntity_t *SV_GentityNum(int32_t entityNum)
{
    return (sharedEntity_t *)((uint8_t *)sv_gameData.gentities +
                                     sv_gameData.gentitySize * entityNum);
}

playerState_t *SV_GameClientNum(int32_t clientNum)
{
    return (playerState_t *)((uint8_t *)sv_gameData.clients +
                             sv_gameData.clientSize * clientNum);
}

svEntity_t *
SV_SvEntityForGentity(const sharedEntity_t *gentity)
{
    int32_t entityNum;

    if (gentity == NULL || gentity->entityState.s_number < 0 ||
        gentity->entityState.s_number >= CODUO_MAX_GENTITIES) {
        Com_Error(SV_GAME_BRIDGE_COM_ERROR_DROP,
                  svSvEntityForGentityBadEntityError);
    }

    entityNum = gentity->entityState.s_number;
    return &sv_entityLinks[entityNum];
}

sharedEntity_t *
SV_GEntityForSvEntity(const svEntity_t *svEntity)
{
    return SV_GentityNum((int32_t)(svEntity - &sv_entityLinks[0]));
}

void SV_GetServerinfo(char *buffer,
                      int32_t bufferSize)
{
    if (bufferSize <= 0) {
        Com_Error(SV_GAME_BRIDGE_COM_ERROR_DROP,
                  svGetServerinfoBadBufferSizeError, bufferSize);
    }

    Q_strncpyz(buffer, Cvar_InfoString(CODUO_CVAR_SERVERINFO_SYNC_MASK),
               bufferSize);
}

void SV_GetUsercmd(int32_t clientNum, usercmd_t *usercmdOut)
{
    if (clientNum < 0 ||
        host_cvar_sv_maxclients_pointer_slot->integer <= clientNum) {
        Com_Error(SV_GAME_BRIDGE_COM_ERROR_DROP,
                  svGetUsercmdBadClientNumError, clientNum);
    }

    *usercmdOut = svs.clients[clientNum].usercmd;
}
