/*
===========================================================================
Copyright (C) 2023 the OpenMoHAA team

This file is part of OpenMoHAA source code.

OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

// DESCRIPTION:
// client game public interfaces

#pragma once

#include "../renderercommon/tr_types.h"

#ifdef __cplusplus
extern "C" {
#endif

#define MAGIC_UNUSED_NUMBER 7777
#define CMD_BACKUP          128
#define CMD_MASK            (CMD_BACKUP - 1)
    // allow a lot of command backups for very fast systems
    // multiple commands may be combined into a single packet, so this
    // needs to be larger than PACKET_BACKUP

#define MAX_ENTITIES_IN_SNAPSHOT 1024

    // snapshots are a view of the server at a given time

    // Snapshots are generated at regular time intervals by the server,
    // but they may not be sent if a client's rate level is exceeded, or
    // they may be dropped by the network.
    typedef struct {
        int snapFlags; // SNAPFLAG_RATE_DELAYED, etc
        int ping;

        int serverTime; // server time the message is valid for (in msec)

        byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits

        playerState_t ps; // complete information about the current player at this time

        int           numEntities;                        // all of the entities that need to be presented
        entityState_t entities[MAX_ENTITIES_IN_SNAPSHOT]; // at the time of this snapshot

        int numServerCommands;     // text based server commands to execute when this
        int serverCommandSequence; // snapshot becomes current

        int            number_of_sounds;
        server_sound_t sounds[MAX_SERVER_SOUNDS];
    } snapshot_t;

    typedef struct stopWatch_s {
        int iStartTime;
        int iEndTime;
        int eType;
    } stopWatch_t;

    typedef struct AliasList_s     AliasList_t;
    typedef struct AliasListNode_s AliasListNode_t;
    typedef struct dtiki_s         dtiki_t;
    typedef struct tiki_cmd_s      tiki_cmd_t;

    /*
==================================================================

functions imported from the main executable

==================================================================
*/

#define CGAME_IMPORT_API_VERSION 3

    /*
==================================================================

functions exported to the main executable

==================================================================
*/

    typedef struct {
        int apiversion;

        //============== general services ==================

        // print message on the local console
        void (*Printf)(const char *fmt, ...);
        void (*DPrintf)(const char *fmt, ...);
        void (*DebugPrintf)(const char *fmt, ...);

        // managed memory allocation
        void *(*Malloc)(int size);
        void (*Free)(void *block);

        // abort the game
        void (*Error)(int errorLevel, const char *fmt, ...);

        // milliseconds should only be used for profiling, never
        // for anything game related.  Get time from CG_ReadyToBuildScene.
        int (*Milliseconds)(void);

        // localization
        const char *(*LV_ConvertString)(const char *string);

        // console variable interaction
        cvar_t *(*Cvar_Get)(const char *var_name, const char *value, int flags);
        cvar_t *(*Cvar_Find)(const char *var_name);
        void (*Cvar_Set)(const char *var_name, const char *value);

        // ClientCommand and ConsoleCommand parameter access
        int (*Argc)(void);
        char *(*Argv)(int n);
        char *(*Args)(void); // concatenation of all argv >= 1
        void (*AddCommand)(const char *cmd);
        void (*Cmd_Stuff)(const char *text);
        void (*Cmd_Execute)(int execWhen, const char *text);
        void (*Cmd_TokenizeString)(const char *textIn);

        // a -1 return means the file does not exist
        // NULL can be passed for buf to just determine existance
        long (*FS_ReadFile)(const char *name, void **buf, qboolean quiet);
        void (*FS_FreeFile)(void *buf);
        int (*FS_WriteFile)(const char *qpath, const void *buffer, int size);
        void (*FS_WriteTextFile)(const char *qpath, const void *buffer, int size);
        char** (*FS_ListFilteredFiles)(const char* path, const char* extension, const char* filter, qboolean wantSubs, int* numfiles, qboolean allowNonPureFilesOnDisk);
        void (*FS_FreeFileList)(char **list);
        // add commands to the local console as if they were typed in
        // for map changing, etc.  The command is not executed immediately,
        // but will be executed in order the next time console commands
        // are processed
        void (*SendConsoleCommand)(const char *text);

        // =========== client specific functions ===============

        // send a string to the server over the network
        int (*MSG_ReadBits)(int bits);
        int (*MSG_ReadChar)();
        int (*MSG_ReadByte)();
        int (*MSG_ReadSVC)();
        int (*MSG_ReadShort)();
        int (*MSG_ReadLong)();
        float (*MSG_ReadFloat)();
        char *(*MSG_ReadString)();
        char *(*MSG_ReadStringLine)();
        float (*MSG_ReadAngle8)();
        float (*MSG_ReadAngle16)();
        void (*MSG_ReadData)(void *data, int len);
        float (*MSG_ReadCoord)();
        void (*MSG_ReadDir)(vec3_t dir);
        void (*SendClientCommand)(const char *s);

        // CM functions
        void (*CM_LoadMap)(const char *name, int* checksum);
        clipHandle_t (*CM_InlineModel)(int index); // 0 = world, 1+ = bmodels
        int (*CM_NumInlineModels)(void);
        int (*CM_PointContents)(const vec3_t p, int headnode);
        int (*CM_TransformedPointContents)(
            const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles
        );
        void (*CM_BoxTrace)(
            trace_t     *results,
            const vec3_t start,
            const vec3_t end,
            const vec3_t mins,
            const vec3_t maxs,
            int          headnode,
            int          brushmask,
            qboolean     cylinder
        );
        void (*CM_TransformedBoxTrace)(
            trace_t     *results,
            const vec3_t start,
            const vec3_t end,
            const vec3_t mins,
            const vec3_t maxs,
            int          headnode,
            int          brushmask,
            const vec3_t origin,
            const vec3_t angles,
            qboolean     cylinder
        );
        clipHandle_t (*CM_TempBoxModel)(const vec3_t mins, const vec3_t maxs, int contents);
        void (*CM_PrintBSPFileSizes)();
        qboolean (*CM_LeafInPVS)(int leaf1, int leaf2);
        int (*CM_PointLeafnum)(const vec3_t p);
        int (*R_MarkFragments)(
            int             numPoints,
            const vec3_t   *points,
            const vec3_t    projection,
            int             maxPoints,
            vec3_t          pointBuffer,
            int             maxFragments,
            markFragment_t *fragmentBuffer,
            float           fRadiusSquared
        );
        int (*R_MarkFragmentsForInlineModel)(
            clipHandle_t    bmodel,
            const vec3_t    vAngles,
            const vec3_t    vOrigin,
            int             numPoints,
            const vec3_t   *points,
            const vec3_t    projection,
            int             maxPoints,
            vec3_t          pointBuffer,
            int             maxFragments,
            markFragment_t *fragmentBuffer,
            float           fRadiusSquared
        );

        void (*R_GetInlineModelBounds)(int index, vec3_t mins, vec3_t maxs);
        void (*R_GetLightingForDecal)(vec3_t light, const vec3_t facing, const vec3_t origin);
        void (*R_GetLightingForSmoke)(vec3_t light, const vec3_t origin);
        int (*R_GatherLightSources)(const vec3_t pos, vec3_t *lightPos, vec3_t *lightIntensity, int maxLights);

        // =========== sound function calls ===============

        void (*S_StartSound)(
            const vec3_t origin,
            int          entNum,
            int          entChannel,
            sfxHandle_t  sfxHandle,
            float        volume,
            float        minDist,
            float        pitch,
            float        maxDist,
            int          streamed
        );
        void (*S_StartLocalSound)(const char *soundName, qboolean forceLoad);
        void (*S_StopSound)(int entnum, int channel);
        void (*S_ClearLoopingSounds)(void);
        void (*S_AddLoopingSound)(
            const vec3_t origin,
            const vec3_t velocity,
            sfxHandle_t  sfxHandle,
            float        volume,
            float        minDist,
            float        maxDist,
            float        pitch,
            int          flags
        );
        void (*S_Respatialize)(int entityNum, const vec3_t origin, vec3_t axis[3]);
        void (*S_BeginRegistration)(void);
        sfxHandle_t (*S_RegisterSound)(const char *sample, int streamed);
        void (*S_EndRegistration)(void);
        void (*S_UpdateEntity)(int entityNum, const vec3_t origin, const vec3_t velocity, qboolean use_listener);
        void (*S_SetReverb)(int reverb_type, float reverb_level);
        void (*S_SetGlobalAmbientVolumeLevel)(float volume);
        float (*S_GetSoundTime)(sfxHandle_t handle);
        int (*S_ChannelNameToNum)(const char *name);
        const char *(*S_ChannelNumToName)(int channel);
        int (*S_IsSoundPlaying)(int channelNumber, const char *name);

        // =========== music function calls ===============

        void (*MUSIC_NewSoundtrack)(const char *name);
        void (*MUSIC_UpdateMood)(int current_mood, int fallback_mood);
        void (*MUSIC_UpdateVolume)(float volume, float fade_time);

        // =========== camera function calls ===============

        float *(*get_camera_offset)(qboolean *lookactive, qboolean *resetview);

        // =========== renderer function calls ================
        void (*R_ClearScene)(void);
        void (*R_RenderScene)(const refdef_t *fd);
        void (*R_LoadWorldMap)(const char *mapname);
        void (*R_PrintBSPFileSizes)();
        int (*MapVersion)();
        int (*R_MapVersion)();
        qhandle_t (*R_RegisterModel)(const char *name);
        qhandle_t (*R_SpawnEffectModel)(const char *name, vec3_t pos, vec3_t axis[3]);
        qhandle_t (*R_RegisterServerModel)(const char *name);
        const char * (*R_GetModelName)(qhandle_t hModel);
        void (*R_UnregisterServerModel)(qhandle_t hModel);
        qhandle_t (*R_RegisterShader)(const char *name);
        qhandle_t (*R_RegisterShaderNoMip)(const char *name);
        const char* (*R_GetShaderName)(qhandle_t hShader);
        void (*R_AddRefEntityToScene)(const refEntity_t *ent, int parentEntityNumber);
        void (*R_AddRefSpriteToScene)(const refEntity_t *ent);
        void (*R_AddLightToScene)(const vec3_t origin, float intensity, float r, float g, float b, int type);
        qboolean (*R_AddPolyToScene)(qhandle_t hShader, int numVerts, const polyVert_t *verts, int renderfx);
        void (*R_AddTerrainMarkToScene)(
            int terrainIndex, qhandle_t hShader, int numVerts, const polyVert_t *verts, int renderFx
        );
        void (*R_SetColor)(const vec4_t rgba); // NULL = 1,1,1,1
        void (*R_DrawStretchPic)(
            float     x,
            float     y,
            float     w,
            float     h,
            float     s1,
            float     t1,
            float     s2,
            float     t2,
            qhandle_t hShader
        ); // 0 = white
        fontheader_t *(*R_LoadFont)(const char *name);
        void (*R_DrawString)(
            fontheader_t *font, const char *text, float x, float y, int maxLen, const float *pvVirtualScreen
        );
        refEntity_t *(*R_GetRenderEntity)(int entityNumber);
        void (*R_ModelBounds)(clipHandle_t model, vec3_t mins, vec3_t maxs);
        float (*R_ModelRadius)(clipHandle_t model);
        float (*R_Noise)(float x, float y, float z, double t);
        void (*R_DebugLine)(const vec3_t start, const vec3_t end, float r, float g, float b, float alpha);
        baseshader_t *(*GetShader)(int shaderNum);
        // =========== Swipes =============
        void (*R_SwipeBegin)(float thistime, float life, qhandle_t shader);
        void (*R_SwipePoint)(vec3_t p1, vec3_t p2, float time);
        void (*R_SwipeEnd)(void);
        int (*R_GetShaderWidth)(qhandle_t shader);
        int (*R_GetShaderHeight)(qhandle_t shader);
        void (*R_DrawBox)(float x, float y, float w, float h);

        // =========== data shared with the client =============
        void (*GetGameState)(gameState_t *gamestate);
        int (*GetSnapshot)(int snapshotNumber, snapshot_t *snapshot);
        int (*GetServerStartTime)();
        void (*SetTime)(int time);
        void (*GetCurrentSnapshotNumber)(int *snapshotNumber, int *serverTime);
        void (*GetGlconfig)(glconfig_t *glconfig);

        // will return false if the number is so old that it doesn't exist in the buffer anymore
        qboolean (*GetParseEntityState)(int parseEntityNumber, entityState_t *state);
        int (*GetCurrentCmdNumber)(void); // returns the most recent command number
                                          // which is the local predicted command for
                                          // the following frame
        qboolean (*GetUserCmd)(int cmdNumber, usercmd_t *ucmd);
        qboolean (*GetServerCommand)(int serverCommandNumber, qboolean differentServer);

        // ALIAS STUFF
        qboolean (*Alias_Add)(const char *alias, const char *name, const char *parameters);
        qboolean (*Alias_ListAdd)(AliasList_t *list, const char *alias, const char *name, const char *parameters);
        const char *(*Alias_FindRandom)(const char *alias, AliasListNode_t **ret);
        const char *(*Alias_ListFindRandom)(AliasList_t *list, const char *alias, AliasListNode_t **ret);
        void (*Alias_Dump)(void);
        void (*Alias_Clear)(void);
        AliasList_t *(*AliasList_New)(const char *name);
        void (*Alias_ListFindRandomRange)(
            AliasList_t *list, const char *alias, int *minIndex, int *maxIndex, float *totalWeight
        );
        AliasList_t *(*Alias_GetGlobalList)();

        // ==================== UI STUFF ==========================
        void (*UI_ShowMenu)(const char *name, qboolean bForce);
        void (*UI_HideMenu)(const char *name, qboolean bForce);
        int (*UI_FontStringWidth)(fontheader_t *font, const char *string, int maxLen);
        // Added in 2.0
        float (*UI_GetObjectivesTop)(void);
        void (*UI_GetHighResolutionScale)(vec2_t scale);

        int (*Key_StringToKeynum)(const char *str);
        const char *(*Key_KeynumToBindString)(int keyNum);
        void (*Key_GetKeysForCommand)(const char *command, int *key1, int *key2);

        // ==================== TIKI STUFF ==========================
        // TIKI SPECIFIC STUFF
        dtiki_t *(*R_Model_GetHandle)(qhandle_t handle);
        int (*TIKI_NumAnims)(dtiki_t *pmdl);
        void (*TIKI_CalculateBounds)(dtiki_t *pmdl, float scale, vec3_t mins, vec3_t maxs);
        const char *(*TIKI_Name)(dtiki_t *tiki);
        void *(*TIKI_GetSkeletor)(dtiki_t *tiki, int entNum);
        void (*TIKI_SetEyeTargetPos)(dtiki_t *tiki, int entNum, vec3_t pos);

        // ANIM SPECIFIC STUFF
        const char *(*Anim_NameForNum)(dtiki_t *tiki, int animnum);
        int (*Anim_NumForName)(dtiki_t *tiki, const char *name);
        int (*Anim_Random)(dtiki_t *tiki, const char *name);
        int (*Anim_NumFrames)(dtiki_t *tiki, int animnum);
        float (*Anim_Time)(dtiki_t *tiki, int animnum);
        float (*Anim_Frametime)(dtiki_t *tiki, int animnum);
        void (*Anim_Delta)(dtiki_t *tiki, int animnum, vec3_t delta);
        int (*Anim_Flags)(dtiki_t *tiki, int animnum);
        int (*Anim_FlagsSkel)(dtiki_t *tiki, int animnum);
        float (*Anim_CrossblendTime)(dtiki_t *tiki, int animnum);
        qboolean (*Anim_HasCommands)(dtiki_t *tiki, int animnum);

        // FRAME SPECIFIC STUFF
        qboolean (*Frame_Commands)(dtiki_t *tiki, int animnum, int framenum, tiki_cmd_t *tiki_cmd);
        qboolean (*Frame_CommandsTime)(dtiki_t *tiki, int animnum, float start, float end, tiki_cmd_t *tiki_cmd);

        // SURFACE SPECIFIC STUFF
        int (*Surface_NameToNum)(dtiki_t *tiki, const char *name);
        //const char *   (*Surface_NumToName) (dtiki_t* tiki, int num );
        //int                (*Surface_Flags) (dtiki_t* tiki, int num );
        //int                (*Surface_NumSkins) (dtiki_t* tiki, int num );

        // TAG SPECIFIC STUFF
        int (*Tag_NumForName)(dtiki_t *tiki, const char *name);
        const char *(*Tag_NameForNum)(dtiki_t *tiki, int num);
        void (*ForceUpdatePose)(refEntity_t *model);
        orientation_t (*TIKI_Orientation)(refEntity_t *model, int tagNum);
        qboolean (*TIKI_IsOnGround)(refEntity_t *model, int tagNum, float threshold);

        // MISCELLANEOUS SPECIFIC STUFF
        void (*UI_ShowScoreBoard)(const char *menuName);
        void (*UI_HideScoreBoard)();
        void (*UI_SetScoreBoardItem)(
            int          itemNumber,
            const char  *data1,
            const char  *data2,
            const char  *data3,
            const char  *data4,
            const char  *data5,
            const char  *data6,
            const char  *data7,
            const char  *data8,
            const vec4_t textColor,
            const vec4_t backColor,
            qboolean     isHeader
        );
        void (*UI_DeleteScoreBoardItems)(int maxIndex);
        void (*UI_ToggleDMMessageConsole)(int consoleMode);
        void (*CL_InitRadar)(radarClient_t* radars, qhandle_t* shaders, int clientNum); // Added in 2.0
        dtiki_t *(*TIKI_FindTiki)(const char *path);
        void (*LoadResource)(const char *name);
        void (*FS_CanonicalFilename)(char *name);
        void (*CL_RestoreSavedCgameState)();
        void (*CL_ClearSavedCgameState)();

        size_t (*getConfigStringIdNormalized)(size_t num);

        cvar_t       *fsDebug;
        hdelement_t  *HudDrawElements;
        clientAnim_t *anim;
        stopWatch_t  *stopWatch;

    } clientGameImport_t;

    /*
==================================================================

functions exported to the main executable

==================================================================
*/

    typedef struct {
        void (*CG_Init)(clientGameImport_t *imported, int serverMessageNum, int serverCommandSequence, int clientNum);
        void (*CG_Shutdown)(void);
        void (*CG_DrawActiveFrame)(int serverTime, int frameTime, stereoFrame_t stereoView, qboolean demoPlayback);
        qboolean (*CG_ConsoleCommand)(void);
        void (*CG_GetRendererConfig)(void);
        void (*CG_Draw2D)(void);
        void (*CG_EyePosition)(vec3_t *eyePos);
        void (*CG_EyeOffset)(vec3_t *eyeOffset);
        void (*CG_EyeAngles)(vec3_t *eyeAngles);
        float (*CG_SensitivityScale)();
        void (*CG_ParseCGMessage)();
        void (*CG_RefreshHudDrawElements)();
        void (*CG_HudDrawShader)(int info);
        void (*CG_HudDrawFont)(int info);
        int (*CG_GetParent)(int entNum);
        float (*CG_GetObjectiveAlpha)();
        int (*CG_PermanentMark)(
            vec3_t          origin,
            vec3_t          dir,
            float           orientation,
            float           sScale,
            float           tScale,
            float           red,
            float           green,
            float           blue,
            float           alpha,
            qboolean        doLighting,
            float           sCenter,
            float           tCenter,
            markFragment_t *markFragments,
            void           *polyVerts
        );
        int (*CG_PermanentTreadMarkDecal)(
            treadMark_t    *treadMark,
            qboolean        startSegment,
            qboolean        doLighting,
            markFragment_t *markFragments,
            void           *polyVerts
        );
        int (*CG_PermanentUpdateTreadMark)(
            treadMark_t *treadMark, float alpha, float minSegment, float maxSegment, float maxOffset, float texScale
        );
        void (*CG_ProcessInitCommands)(dtiki_t *tiki, refEntity_t *ent);
        void (*CG_EndTiki)(dtiki_t *tiki);
        const char *(*CG_GetColumnName)(int columnNum, int *columnWidth);
        void (*CG_GetScoreBoardColor)(float *red, float *green, float *blue, float *alpha);
        void (*CG_GetScoreBoardFontColor)(float *red, float *green, float *blue, float *alpha);
        int (*CG_GetScoreBoardDrawHeader)();
        void (*CG_GetScoreBoardPosition)(float *x, float *y, float *width, float *height);
        int (*CG_WeaponCommandButtonBits)();
        int (*CG_CheckCaptureKey)(int key, qboolean down, unsigned int time);

        //
        // Added in 2.0
        //
		void (*CG_ReadNonPVSClient)(radarUnpacked_t* radarUnpacked);
		void (*CG_UpdateRadar)();
		size_t (*CG_SaveStateToBuffer)(void** out, int svsTime);
		qboolean (*CG_LoadStateToBuffer)(void* state, size_t size, int svsTime);
		void (*CG_CleanUpTempModels)();

        // FIXME
        //prof_cgame_t* profStruct;

        qboolean (*CG_Command_ProcessFile)(const char *name, qboolean quiet, dtiki_t *curTiki);

    } clientGameExport_t;

#ifdef CGAME_DLL
#    ifdef WIN32
    __declspec(dllexport)
#    else
    __attribute__((visibility("default")))
#    endif
#endif
        clientGameExport_t *GetCGameAPI(void);

#ifdef __cplusplus
}
#endif
