#include "coduo_engine_structs.h"
#include "../abi/vm_private.h"
#include "../core_runtime/core_runtime_private.h"
#include "../networking/netchan_private.h"
#include "server_private.h"
#include "sv_globals_private.h"

#if defined(_WIN32)
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif
#include <stdint.h>
#include <string.h>

enum {
    SV_MASTER_DEDICATED_SERVER = 2,
    SV_MASTER_HEARTBEAT_INTERVAL_MSEC = 180000,
    SV_MASTER_STATUS_INTERVAL_MSEC = 600000,
    SV_MASTER_PORT = 20610
};

static const char svMasterServerName[] = "coduomaster.activision.com";
static netadr_t svMasterAddress;

static qboolean coduomp_sv_is_dedicated_master_server(void)
{
    /* NOT_FROM_ORIGINAL_SOURCE: local predicate for the repeated dedicated==2 guard. */
    return dedicated != NULL &&
           dedicated->integer ==
               SV_MASTER_DEDICATED_SERVER;
}

/* NOT_FROM_ORIGINAL_SOURCE: clears the split native representation of the
 * original server-static shutdown span owned by this translation unit. */
void coduomp_sv_clear_master_address(void)
{
    memset(&svMasterAddress, 0, sizeof(svMasterAddress));
}

netadr_t *SV_ResolveMasterAddress(void)
{
    if (svMasterAddress.type != 0) {
        return &svMasterAddress;
    }

    Com_Printf("Resolving %s\n", svMasterServerName);
    if (NET_StringToAdr(svMasterServerName, &svMasterAddress) == qfalse) {
        Com_Printf("Couldn't resolve address: %s\n", svMasterServerName);
        return &svMasterAddress;
    }

    /* ORIGINAL_BINARY_BUG: the fixed needle and hostname are passed to
     * strstr in reverse order. See original-bugs/
     * coduomp-master-port-colon-test-reversed.md. */
    if (strstr(":", svMasterServerName) == NULL) {
        svMasterAddress.port = htons(SV_MASTER_PORT);
    }

    Com_Printf("%s resolved to %i.%i.%i.%i:%i\n", svMasterServerName,
               svMasterAddress.ip[0], svMasterAddress.ip[1],
               svMasterAddress.ip[2], svMasterAddress.ip[3],
               htons(svMasterAddress.port));
    return &svMasterAddress;
}

void SV_MasterHeartbeat(const char *heartbeat)
{
    netadr_t *masterAddress;

    if (coduomp_sv_is_dedicated_master_server() == qfalse) {
        return;
    }

    if (svs.nextHeartbeatTime <= svs.timeSecondary) {
        svs.nextHeartbeatTime =
            svs.timeSecondary + SV_MASTER_HEARTBEAT_INTERVAL_MSEC;
        masterAddress = SV_ResolveMasterAddress();
        if (masterAddress->type != CODUO_NA_BOT) {
            Com_Printf("Sending heartbeat to %s\n", svMasterServerName);
            NET_OutOfBandPrint(NS_SERVER, *masterAddress, "heartbeat %s\n",
                               heartbeat);
        }
    }

    if (svs.nextStatusResponseTime <= svs.timeSecondary) {
        svs.nextStatusResponseTime =
            svs.timeSecondary + SV_MASTER_STATUS_INTERVAL_MSEC;
        masterAddress = SV_ResolveMasterAddress();
        if (masterAddress->type != CODUO_NA_BOT) {
            SVC_Status(*masterAddress);
        }
    }
}

void SV_MasterGameCompleteStatus(void)
{
    netadr_t *masterAddress;

    if (coduomp_sv_is_dedicated_master_server() == qfalse) {
        return;
    }

    masterAddress = SV_ResolveMasterAddress();
    if (masterAddress->type != CODUO_NA_BOT) {
        Com_Printf("Sending gameCompleteStatus to %s\n", svMasterServerName);
        SVC_GameCompleteStatus(*masterAddress);
    }
}

int32_t SV_GetClientScore(client_t *client)
{
    if (sv_gameVM == NULL) {
        return 0;
    }

    /* The retail i386 subtraction produces the 32-bit client slot passed to
     * vmMain; make that fixed domain explicit on wider hosts. */
    return VM_Call(
        sv_gameVM, CODUO_GAME_GET_CLIENT_SCORE,
        (int32_t)(client - svs.clients));
}
