#ifndef _SITHMULTI_H
#define _SITHMULTI_H

#include "types.h"
#include "globals.h"

#include "stdPlatform.h"

#ifdef __cplusplus
extern "C" {
#endif

#define sithMulti_Startup_ADDR (0x004C9AE0)
#define sithMulti_Shutdown_ADDR (0x004C9CB0)
#define sithMulti_CheckPlayers_ADDR (0x004C9D00)
#define sithMulti_CreatePlayer_ADDR (0x004C9FC0)
#define sithMulti_InitTick_ADDR (0x004CA140)
#define sithMulti_LobbyMessage_ADDR (0x004CA1B0)
#define sithMulti_map_init_related_ADDR (0x004CA310)
#define sithMulti_sub_4CA3B0_ADDR (0x004CA3B0)
#define sithMulti_sub_4CA410_ADDR (0x004CA410)
#define sithMulti_ProcessPlayerLost_ADDR (0x004CA470)
#define sithMulti_RemovePlayer_ADDR (0x004CA610)
#define sithMulti_SendWelcome_ADDR (0x004CA710)
#define sithMulti_ProcessWelcome_ADDR (0x004CA780)
#define sithMulti_ProcessPlayerJoin_ADDR (0x004CA910)
#define sithMulti_SyncPlayers_ADDR (0x004CA9C0)
#define sithMulti_ProcessSyncPlayers_ADDR (0x004CAAF0)
#define sithMulti_SendJoinRequest_ADDR (0x004CADB0)
#define sithMulti_ProcessJoinRequest_ADDR (0x004CAE50)
#define sithMulti_FinishJoining_ADDR (0x004CB200)
#define sithMulti_SendChat_ADDR (0x004CB250)
#define sithMulti_ProcessChat_ADDR (0x004CB2E0)
#define sithMulti_Ping_ADDR (0x004CB390)
#define sithMulti_ProcessPing_ADDR (0x004CB3E0)
#define sithMulti_ProcessPong_ADDR (0x004CB410)
#define sithMulti_QuitPlayer_ADDR (0x004CB4A0)
#define sithMulti_ProcessQuit_ADDR (0x004CB4F0)
#define sithMulti_Update_ADDR (0x004CB690)
#define sithMulti_SyncScores_ADDR (0x004CBC00)
#define sithMulti_GetPlayerIndexByID_ADDR (0x004CBC10)
#define sithMulti_SetHandleridk_ADDR (0x004CBC40)
#define sithMulti_ProcessKilledPlayer_ADDR (0x004CBC50)
#define sithMulti_ProcessScore_ADDR (0x004CBDE0)
#define sithMulti_QuitGame_ADDR (0x004CBF90)
#define sithMulti_GetSpawnIdx_ADDR (0x004CBFC0)
#define sithMulti_RemoveStaticThing_ADDR (0x004CC110)

#define NETMSG_START intptr_t craftingPacket = (intptr_t)&sithComm_netMsgTmp.pktData[0];
#define NETMSG_START_2 intptr_t craftingPacket = (intptr_t)&stdComm_cogMsgTmp.pktData[0];

// TODO: Just rewrite all of this with memcpys
#ifdef TARGET_RETRO_HOMEBREW // Prevent unaligned stores/loads
#define FIX_ALIGN(t) ((craftingPacket & (sizeof(t)-1)) ? /*stdPlatform_Printf("Bump %p\n", craftingPacket),*/ craftingPacket += (sizeof(t) - (craftingPacket & (sizeof(t)-1))), 1 : 1)
#else
#define FIX_ALIGN(t) (1)
#endif

#define NETMSG_PUSHU8(x) {*(uint8_t*)craftingPacket = (uint8_t)(x); craftingPacket += sizeof(uint8_t);};
#define NETMSG_PUSHU16(x) {FIX_ALIGN(uint16_t); *(uint16_t*)craftingPacket = (uint16_t)(x); craftingPacket += sizeof(uint16_t);};
#define NETMSG_PUSHS16(x) {FIX_ALIGN(int16_t); *(int16_t*)craftingPacket = (int16_t)(x); craftingPacket += sizeof(int16_t);};
#define NETMSG_PUSHU32(x) {FIX_ALIGN(uint32_t); *(uint32_t*)craftingPacket = (uint32_t)(x); craftingPacket += sizeof(uint32_t);};
#define NETMSG_PUSHS32(x) {FIX_ALIGN(int32_t); *(int32_t*)craftingPacket = (int32_t)(x); craftingPacket += sizeof(int32_t);};
#define NETMSG_PUSHF32(x) {FIX_ALIGN(flex32_t); *(flex32_t*)craftingPacket = (flex32_t)(x); craftingPacket += sizeof(flex32_t);};
#define NETMSG_PUSHVEC2(x) {FIX_ALIGN(uint32_t); *(rdVector2*)craftingPacket = (x); craftingPacket += sizeof(rdVector2);};
#define NETMSG_PUSHVEC3(x) {FIX_ALIGN(uint32_t); *(rdVector3*)craftingPacket = (x); craftingPacket += sizeof(rdVector3);};
#define NETMSG_PUSHVEC3I(x) {FIX_ALIGN(uint32_t); *(rdVector3i*)craftingPacket = (x); craftingPacket += sizeof(rdVector3i);};
#define NETMSG_PUSHMAT34(x) {FIX_ALIGN(uint32_t); *(rdMatrix34*)craftingPacket = (x); craftingPacket += sizeof(rdMatrix34);};
#define NETMSG_PUSHSTR(x,l) {_strncpy((char*)craftingPacket, (x), (l)-1); ((char*)craftingPacket)[(l)-1] = 0; craftingPacket += (l);};
#define NETMSG_PUSHWSTR(x,l) {FIX_ALIGN(uint16_t); _wcsncpy((char16_t*)craftingPacket, (x), (l)-1); ((char16_t*)craftingPacket)[(l)-1] = 0; craftingPacket += (l*sizeof(char16_t));};
#define NETMSG_END(msgid) { size_t len = (intptr_t)craftingPacket - (intptr_t)&sithComm_netMsgTmp.pktData[0]; \
                            sithComm_netMsgTmp.netMsg.flag_maybe = 0; \
                            sithComm_netMsgTmp.netMsg.cogMsgId = msgid; \
                            sithComm_netMsgTmp.netMsg.msg_size = len; \
                          };
#define NETMSG_LEN(msgid) ((intptr_t)craftingPacket - (intptr_t)&sithComm_netMsgTmp.pktData[0])

#define NETMSG_END_2(msgid) { size_t len = (intptr_t)craftingPacket - (intptr_t)&stdComm_cogMsgTmp.pktData[0]; \
                            stdComm_cogMsgTmp.netMsg.flag_maybe = 0; \
                            stdComm_cogMsgTmp.netMsg.cogMsgId = msgid; \
                            stdComm_cogMsgTmp.netMsg.msg_size = len; \
                          };
#define NETMSG_LEN_2(msgid) ((intptr_t)craftingPacket - (intptr_t)&stdComm_cogMsgTmp.pktData[0])

#define NETMSG_IN_START(x) intptr_t _readingPacket = (intptr_t)&x->pktData[0]; uint8_t _readingOutU8; \
uint16_t _readingOutU16; int16_t _readingOutS16; uint32_t _readingOutU32; \
int32_t _readingOutS32; flex32_t _readingOutFloat; rdVector2 _readingOutV2; \
rdVector3 _readingOutV3; rdVector3i _readingOutV3i; rdMatrix34 _readingOutM34;

// TODO: Just rewrite all of this with memcpys
#ifdef TARGET_RETRO_HOMEBREW // Prevent unaligned stores and loads
#define CHECK_ALIGN(t) ((((intptr_t)_readingPacket) & (sizeof(t)-1)) ? _readingPacket += (sizeof(t) - (((intptr_t)_readingPacket) & (sizeof(t)-1))), 1 : 1)
#else
#define CHECK_ALIGN(t) (1)
#endif

#define NETMSG_POPU8() (_readingOutU8 = *(uint8_t*)_readingPacket, _readingPacket += sizeof(uint8_t), _readingOutU8)
#define NETMSG_POPU16() (CHECK_ALIGN(uint16_t), _readingOutU16 = *(uint16_t*)_readingPacket, _readingPacket += sizeof(uint16_t), _readingOutU16)
#define NETMSG_POPS16() (CHECK_ALIGN(int16_t), _readingOutS16 = *(int16_t*)_readingPacket, _readingPacket += sizeof(int16_t), _readingOutS16)
#define NETMSG_POPU32() (CHECK_ALIGN(uint32_t), _readingOutU32 = *(uint32_t*)_readingPacket, _readingPacket += sizeof(uint32_t), _readingOutU32)
#define NETMSG_POPS32() (CHECK_ALIGN(int32_t), _readingOutS32 = *(int32_t*)_readingPacket, _readingPacket += sizeof(int32_t), _readingOutS32)
#define NETMSG_POPF32() (CHECK_ALIGN(flex32_t), _readingOutFloat = *(flex32_t*)_readingPacket, _readingPacket += sizeof(flex32_t), _readingOutFloat)
#define NETMSG_POPVEC2() (CHECK_ALIGN(uint32_t), _readingOutV2 = *(rdVector2*)_readingPacket, _readingPacket += sizeof(rdVector2), _readingOutV2)
#define NETMSG_POPVEC3() (CHECK_ALIGN(uint32_t), _readingOutV3 = *(rdVector3*)_readingPacket, _readingPacket += sizeof(rdVector3), _readingOutV3)
#define NETMSG_POPVEC3I() (CHECK_ALIGN(uint32_t), _readingOutV3i = *(rdVector3i*)_readingPacket, _readingPacket += sizeof(rdVector3i), _readingOutV3i)
#define NETMSG_POPMAT34() (CHECK_ALIGN(uint32_t), _readingOutM34 = *(rdMatrix34*)_readingPacket, _readingPacket += sizeof(rdMatrix34), _readingOutM34)
#define NETMSG_POPSTR(x,l) { _strncpy((x), (char*)_readingPacket, (l)-1); (x)[(l)-1] = 0; _readingPacket += (l); }
#define NETMSG_POPWSTR(x,l) { CHECK_ALIGN(uint16_t), _wcsncpy((x), (char16_t*)_readingPacket, (l)-1); (x)[(l)-1] = 0; _readingPacket += (l*sizeof(char16_t)); }
#define NETMSG_IN_END {}

extern int jkGuiNetHost_bIsDedicated;

void sithMulti_SetHandleridk(sithMultiHandler_t a1);
void sithMulti_SendChat(const char *pStr, int arg0, int arg1);
void sithMulti_ProcessScore();
int sithMulti_ProcessChat(SithMessage *pMsg);

HRESULT sithMulti_CreatePlayer(const char16_t *a1, const char16_t *a2, const char *a3, const char *a4, int a5, int a6, int multiModeFlags, int rate, int a9);
int sithMulti_Startup();
void sithMulti_RemoveStaticThing(int guid);
void sithMulti_Shutdown();
int sithMulti_SendJoinRequest(int sendto_id);
MATH_FUNC int sithMulti_GetSpawnIdx(SithThing *pPlayerThing);
void sithMulti_SyncScores();
int sithMulti_map_init_related();
int sithMulti_ResetNetState();
void sithMulti_CleanupThings(SithWorld *pWorld);
void sithMulti_RemovePlayer(int playerNum);
void sithMulti_ProcessPlayerJoin(int playerNum);
void sithMulti_FinishJoining(int code, int param2, int playerId);
void sithMulti_ProcessKilledPlayer(SithPlayer *pPlayer, SithThing *pPlayerThing, SithThing *pKiller);
void sithMulti_QuitGame(uint32_t a1, int a2);
void sithMulti_SendWelcome(int idPlayer, int playerNum, int idTo);
void sithMulti_QuitPlayer(int id);
int sithMulti_LobbyMessage();
int sithMulti_ProcessWelcome(SithMessage *pMsg);
int sithMulti_ProcessPing(SithMessage *pMsg);
int sithMulti_ProcessPong(SithMessage *pMsg);
int sithMulti_ProcessQuit(SithMessage *pMsg);
int sithMulti_CheckPlayers(int32_t msecTime, SithEventParams* pParam);
void sithMulti_SyncPlayers(int idTo, int dpFlags);
int sithMulti_ProcessSyncPlayers(SithMessage *pMsg);
void sithMulti_ProcessPlayerLost(int idPlayer);
void sithMulti_InitTick(uint32_t tickrate);
int sithMulti_ProcessJoinRequest(SithMessage *pMsg);
void sithMulti_Update(int msecDeltaTime);
uint32_t sithMulti_GetPlayerIndexByID(int playerID);
int sithMulti_Ping(int idTo);

//static void (*sithMulti_Startup)() = (void*)sithMulti_Startup_ADDR;
//static void (*sithMulti_RemoveStaticThing)(int a1) = (void*)sithMulti_RemoveStaticThing_ADDR;
//static int (*sithMulti_QuitPlayer)(int a1) = (void*)sithMulti_QuitPlayer_ADDR;
//static void (*sithMulti_SyncScores)(void) = (void*)sithMulti_SyncScores_ADDR;
//static void (*sithMulti_Shutdown)() = (void*)sithMulti_Shutdown_ADDR;
//static int (*sithMulti_LobbyMessage)() = (void*)sithMulti_LobbyMessage_ADDR;
//static void (*sithMulti_Update)(int) = (void*)sithMulti_Update_ADDR;
//static int (*sithMulti_SendWelcome)(int,int,int) = (void*)sithMulti_SendWelcome_ADDR;
//static void (*sithMulti_ProcessKilledPlayer)(SithPlayer *a1, SithThing *killed, SithThing *killed_by) = (void*)sithMulti_ProcessKilledPlayer_ADDR;
//static int (*sithMulti_CreatePlayer)(char16_t *a1, char16_t *a2, char *a3, char *a4, int a5, int a6, int a7, int a8, int a9) = (void*)sithMulti_CreatePlayer_ADDR;
//static uint32_t (*sithMulti_InitTick)(uint32_t) = (void*)sithMulti_InitTick_ADDR;
//static int (*sithMulti_CheckPlayers)() = (void*)sithMulti_CheckPlayers_ADDR;
//static int (*sithMulti_SendJoinRequest)(int a1) = (void*)sithMulti_SendJoinRequest_ADDR;

#ifdef __cplusplus
}
#endif


#endif // _SITHMULTI_H
