/*
Copyright (C) 1997-2001 Id Software, Inc.

This program 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.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// client.h -- primary header for client

#ifndef QUAKE2_CLIENT_CLIENT_H__
#define QUAKE2_CLIENT_CLIENT_H__

// define	PARANOID			// speed sapping error checking

#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "ref.h"

#include "cdaudio.h"
#include "console.h"
#include "input.h"
#include "keys.h"
#include "vid.h"
#include "screen.h"
#include "sound.h"

#include <alias/ecs.h>

static inline alias_ecs_Instance *CLIENT_ECS_INSTANCE(void) {
  extern alias_ecs_Instance *client_global_ecs_instance;
  if(client_global_ecs_instance == NULL) {
    alias_ecs_create_instance(NULL, &client_global_ecs_instance);
  }
  return client_global_ecs_instance;
}

static inline alias_ecs_LayerHandle CLIENT_MAIN_LAYER(void) {
  extern alias_ecs_LayerHandle client_global_ecs_main_layer;
  if(client_global_ecs_main_layer == 0) {
    alias_ecs_create_layer(CLIENT_ECS_INSTANCE(), &(alias_ecs_LayerCreateInfo){.max_entities = 0},
                           &client_global_ecs_main_layer);
  }
  return client_global_ecs_main_layer;
}

//=============================================================================

typedef struct {
  bool valid; // cleared if delta parsing was invalid
  int serverframe;
  int servertime; // server time the message is valid for (in msec)
  int deltaframe;
  byte areabits[MAX_MAP_AREAS / 8]; // portalarea visibility bits
  player_state_t playerstate;
  int num_entities;
  int parse_entities; // non-masked index into cl_parse_entities array
} frame_t;

typedef struct {
  entity_state_t baseline; // delta from this if not from a previous frame
  entity_state_t current;
  entity_state_t prev; // will always be valid, but might just be a copy of current

  int serverframe; // if not current, this ent isn't in the frame

  int trailcount;     // for diminishing grenade trails
  vec3_t lerp_origin; // for trails (variable hz)

  int fly_stoptime;
} centity_t;

#define MAX_CLIENTWEAPONMODELS 20 // PGM -- upped from 16 to fit the chainfist vwep

typedef struct {
  char name[MAX_QPATH];
  char cinfo[MAX_QPATH];
  struct BaseImage *skin;
  struct BaseImage *icon;
  char iconname[MAX_QPATH];
  struct model_s *model;
  struct model_s *weaponmodel[MAX_CLIENTWEAPONMODELS];
} clientinfo_t;

extern char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH];
extern int num_cl_weaponmodels;

#define CMD_BACKUP 64 // allow a lot of command backups for very fast systems

//
// the client_state_t structure is wiped completely at every
// server map change
//
typedef struct {
  int timeoutcount;

  int timedemo_frames;
  int timedemo_start;

  bool refresh_prepped; // false if on new level or new ref dll
  bool sound_prepped;   // ambient sounds can start
  bool force_refdef;    // vid has changed, so we can't use a paused refdef

  int parse_entities; // index (not anded off) into cl_parse_entities[]

  usercmd_t cmd;
  usercmd_t cmds[CMD_BACKUP];             // each mesage will send several old cmds
  int cmd_time[CMD_BACKUP];               // time sent, for calculating pings
  short predicted_origins[CMD_BACKUP][3]; // for debug comparing against server

  float predicted_step; // for stair up smoothing
  unsigned predicted_step_time;

  vec3_t predicted_origin; // generated by CL_PredictMovement
  vec3_t predicted_angles;
  vec3_t prediction_error;

  frame_t frame;     // received from server
  int surpressCount; // number of messages rate supressed
  frame_t frames[UPDATE_BACKUP];

  // the client maintains its own idea of view angles, which are
  // sent to the server each frame.  It is cleared to 0 upon entering each level.
  // the server sends a delta each frame which is added to the locally
  // tracked view angles to account for standing on rotating objects,
  // and teleport direction changes
  vec3_t viewangles;

  int time;       // this is the time value that the client
                  // is rendering at.  always <= cls.realtime
  float lerpfrac; // between oldframe and frame

  refdef_t refdef;

  vec3_t v_forward, v_right, v_up; // set when refdef.angles is set

  //
  // transient data from server
  //
  char layout[1024]; // general 2D overlay
  int inventory[MAX_ITEMS];

  //
  // non-gameserver infornamtion
  // FIXME: move this cinematic stuff into the cin_t structure
  FILE *cinematic_file;
  int cinematictime; // cls.realtime for first cinematic frame
  int cinematicframe;
  char cinematicpalette[768];
  bool cinematicpalette_active;

  //
  // server state information
  //
  bool attractloop; // running the attract loop, any key will menu
  int servercount;  // server identification for prespawns
  char gamedir[MAX_QPATH];
  int playernum;

  char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH];

  //
  // locally derived information from server state
  //
  struct model_s *model_draw[MAX_MODELS];
  struct model_s *cmodel_draw[CMODEL_COUNT][MAX_MODELS];
  struct cmodel_s *cmodel_clip[CMODEL_COUNT][MAX_MODELS];

  struct sfx_s *sound_precache[MAX_SOUNDS];
  struct BaseImage *image_precache[MAX_IMAGES];

  clientinfo_t clientinfo[MAX_CLIENTS];
  clientinfo_t baseclientinfo;
} client_state_t;

extern client_state_t cl;

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

the client_static_t structure is persistant through an arbitrary number
of server connections

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

typedef enum {
  ca_uninitialized,
  ca_disconnected, // not talking to a server
  ca_connecting,   // sending request packets to the server
  ca_connected,    // netchan_t established, waiting for svc_serverdata
  ca_active        // game views should be displayed
} connstate_t;

typedef enum { dl_none, dl_model, dl_sound, dl_skin, dl_single } dltype_t; // download type

typedef enum { key_game, key_console, key_message, key_menu } keydest_t;

typedef struct {
  connstate_t state;
  keydest_t key_dest;

  int framecount;
  int realtime;    // always increasing, no clamping, etc
  float frametime; // seconds since last frame

  // screen rendering information
  float disable_screen;    // showing loading plaque between levels
                           // or changing rendering dlls
                           // if time gets > 30 seconds ahead, break it
  int disable_servercount; // when we receive a frame and cl.servercount
                           // > cls.disable_servercount, clear disable_screen

  // connection information
  char servername[MAX_OSPATH]; // name of server from original connect
  float connect_time;          // for connection retransmits

  int quakePort; // a 16 bit value that allows quake servers
                 // to work around address translating routers
  netchan_t netchan;
  int serverProtocol; // in case we are doing some kind of version hack

  int challenge; // from the server to use for connecting

  FILE *download; // file transfer from server
  char downloadtempname[MAX_OSPATH];
  char downloadname[MAX_OSPATH];
  int downloadnumber;
  dltype_t downloadtype;
  int downloadpercent;

  // demo recording info must be here, so it isn't cleared on level change
  bool demorecording;
  bool demowaiting; // don't record until a non-delta message is received
  FILE *demofile;
} client_static_t;

extern client_static_t cls;

//=============================================================================

//
// cvars
//
extern cvar_t *cl_stereo_separation;
extern cvar_t *cl_stereo;

extern cvar_t *cl_gun;
extern cvar_t *cl_add_blend;
extern cvar_t *cl_add_lights;
extern cvar_t *cl_add_entities;
extern cvar_t *cl_predict;
extern cvar_t *cl_footsteps;
extern cvar_t *cl_noskins;
extern cvar_t *cl_autoskins;

extern cvar_t *cl_upspeed;
extern cvar_t *cl_forwardspeed;
extern cvar_t *cl_sidespeed;

extern cvar_t *cl_yawspeed;
extern cvar_t *cl_pitchspeed;

extern cvar_t *cl_run;

extern cvar_t *cl_anglespeedkey;

extern cvar_t *cl_shownet;
extern cvar_t *cl_showmiss;
extern cvar_t *cl_showclamp;

extern cvar_t *lookspring;
extern cvar_t *lookstrafe;
extern cvar_t *sensitivity;

extern cvar_t *m_pitch;
extern cvar_t *m_yaw;
extern cvar_t *m_forward;
extern cvar_t *m_side;

extern cvar_t *freelook;

extern cvar_t *cl_lightlevel; // FIXME HACK

extern cvar_t *cl_paused;
extern cvar_t *cl_timedemo;

extern cvar_t *cl_vwep;

typedef struct {
  int key; // so entities can reuse same entry
  vec3_t color;
  vec3_t origin;
  float radius;
  float die;      // stop lighting after this time
  float decay;    // drop this each second
  float minlight; // don't add when contributing less
} cdlight_t;

extern centity_t cl_entities[MAX_EDICTS];
extern cdlight_t cl_dlights[MAX_DLIGHTS];

// the cl_parse_entities must be large enough to hold UPDATE_BACKUP frames of
// entities, so that when a delta compressed message arives from the server
// it can be un-deltad from the original
#define MAX_PARSE_ENTITIES 1024
extern entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES];

//=============================================================================

extern netadr_t net_from;
extern sizebuf_t net_message;

void DrawString(int x, int y, char *s);
void DrawAltString(int x, int y, char *s); // toggle high bit
bool CL_CheckOrDownloadFile(char *filename);

void CL_AddNetgraph(void);

// ROGUE
typedef struct cl_sustain {
  int id;
  int type;
  int endtime;
  int nextthink;
  int thinkinterval;
  vec3_t org;
  vec3_t dir;
  int color;
  int count;
  int magnitude;
  void (*think)(struct cl_sustain *self);
} cl_sustain_t;

#define MAX_SUSTAINS 32
void CL_ParticleSteamEffect2(cl_sustain_t *self);

void CL_TeleporterParticles(entity_state_t *ent);
void CL_ParticleEffect(vec3_t org, vec3_t dir, int color, int count);
void CL_ParticleEffect2(vec3_t org, vec3_t dir, int color, int count);

// RAFAEL
void CL_ParticleEffect3(vec3_t org, vec3_t dir, int color, int count);

//=================================================

// PGM
typedef struct particle_s {
  struct particle_s *next;

  float time;

  vec3_t org;
  vec3_t vel;
  vec3_t accel;
  int albedo;
  int emit;
  float alpha;
  float alphavel;
  float incandescence;
  float incandescencevel;
} cparticle_t;

#define PARTICLE_GRAVITY 40
#define BLASTER_PARTICLE_COLOR 0xe0
// PMM
#define INSTANT_PARTICLE -10000.0
// PGM

void CL_ClearEffects(void);
void CL_ClearTEnts(void);
void CL_BlasterTrail(vec3_t start, vec3_t end);
void CL_QuadTrail(vec3_t start, vec3_t end);
void CL_RailTrail(vec3_t start, vec3_t end);
void CL_BubbleTrail(vec3_t start, vec3_t end);
void CL_FlagTrail(vec3_t start, vec3_t end, float color);

// RAFAEL
void CL_IonripperTrail(vec3_t start, vec3_t end);

// PGM
void CL_BlasterParticles2(vec3_t org, vec3_t dir, unsigned int color);
void CL_BlasterTrail2(vec3_t start, vec3_t end);
void CL_DebugTrail(vec3_t start, vec3_t end);
void CL_SmokeTrail(vec3_t start, vec3_t end, int colorStart, int colorRun, int spacing);
void CL_Flashlight(int ent, vec3_t pos);
void CL_ForceWall(vec3_t start, vec3_t end, int color);
void CL_FlameEffects(centity_t *ent, vec3_t origin);
void CL_GenericParticleEffect(vec3_t org, vec3_t dir, int color, int count, int numcolors, int dirspread,
                              float alphavel);
void CL_BubbleTrail2(vec3_t start, vec3_t end, int dist);
void CL_Heatbeam(vec3_t start, vec3_t end);
void CL_ParticleSteamEffect(vec3_t org, vec3_t dir, int color, int count, int magnitude);
void CL_TrackerTrail(vec3_t start, vec3_t end, int particleColor);
void CL_Tracker_Explode(vec3_t origin);
void CL_TagTrail(vec3_t start, vec3_t end, float color);
void CL_ColorFlash(vec3_t pos, int ent, int intensity, float r, float g, float b);
void CL_Tracker_Shell(vec3_t origin);
void CL_MonsterPlasma_Shell(vec3_t origin);
void CL_ColorExplosionParticles(vec3_t org, int color, int run);
void CL_ParticleSmokeEffect(vec3_t org, vec3_t dir, int color, int count, int magnitude);
void CL_Widowbeamout(cl_sustain_t *self);
void CL_Nukeblast(cl_sustain_t *self);
void CL_WidowSplash(vec3_t org);
// PGM

int CL_ParseEntityBits(uint32_t *bits);
void CL_ParseDelta(entity_state_t *from, entity_state_t *to, int number, int bits);
void CL_ParseFrame(void);

void CL_ParseTEnt(void);
void CL_ParseConfigString(void);
void CL_ParseMuzzleFlash(void);
void CL_ParseMuzzleFlash2(void);
void SmokeAndFlash(vec3_t origin);

void CL_SetLightstyle(int i);

void CL_RunParticles(void);
void CL_RunDLights(void);
void CL_RunLightStyles(void);

void CL_AddEntities(void);
void CL_AddDLights(void);
void CL_AddTEnts(void);
void CL_AddLightStyles(void);

//=================================================

void CL_PrepRefresh(void);
void CL_RegisterSounds(void);

void CL_Quit_f(void);

void IN_Accumulate(void);

void CL_ParseLayout(void);

//
// cl_main
//
extern refexport_t re; // interface to refresh .dll

void CL_Init(void);

void CL_FixUpGender(void);
void CL_Disconnect(void);
void CL_Disconnect_f(void);
void CL_GetChallengePacket(void);
void CL_PingServers_f(void);
void CL_Snd_Restart_f(void);
void CL_RequestNextDownload(void);

//
// cl_input
//
typedef struct {
  int down[2];       // key nums holding it down
  unsigned downtime; // msec timestamp
  unsigned msec;     // msec down this frame
  int state;
} kbutton_t;

extern kbutton_t in_mlook, in_klook;
extern kbutton_t in_strafe;
extern kbutton_t in_speed;

void CL_InitInput(void);
void CL_SendCmd(void);
void CL_SendMove(usercmd_t *cmd);

void CL_ClearState(void);

void CL_ReadPackets(void);

int CL_ReadFromServer(void);
void CL_WriteToServer(usercmd_t *cmd);
void CL_BaseMove(usercmd_t *cmd);

void IN_CenterView(void);

float CL_KeyState(kbutton_t *key);
char *Key_KeynumToString(int keynum);

//
// cl_demo.c
//
void CL_WriteDemoMessage(void);
void CL_Stop_f(void);
void CL_Record_f(void);

//
// cl_parse.c
//
extern char *svc_strings[256];

void CL_ParseServerMessage(void);
void CL_LoadClientinfo(clientinfo_t *ci, char *s);
void SHOWNET(char *s);
void CL_ParseClientinfo(int player);
void CL_Download_f(void);

//
// cl_view.c
//
extern int gun_frame;
extern struct model_s *gun_model;

void V_Init(void);
void V_RenderView(float stereo_separation);
void V_AddEntity(entity_t *ent);
void V_AddParticle(vec3_t org, int albedo, int emit, float alpha, float incandescence);
void V_AddLight(vec3_t org, float intensity, float r, float g, float b);
void V_AddLightStyle(int style, float r, float g, float b);

//
// cl_tent.c
//
void CL_RegisterTEntSounds(void);
void CL_RegisterTEntModels(void);
void CL_SmokeAndFlash(vec3_t origin);

//
// cl_pred.c
//
void CL_InitPrediction(void);
void CL_PredictMove(void);
void CL_CheckPredictionError(void);

//
// cl_fx.c
//
cdlight_t *CL_AllocDlight(int key);
void CL_BigTeleportParticles(vec3_t org);
void CL_RocketTrail(vec3_t start, vec3_t end, centity_t *old);
void CL_DiminishingTrail(vec3_t start, vec3_t end, centity_t *old, int flags);
void CL_FlyEffect(centity_t *ent, vec3_t origin);
void CL_BfgParticles(entity_t *ent);
void CL_AddParticles(void);
void CL_EntityEvent(entity_state_t *ent);
// RAFAEL
void CL_TrapParticles(entity_t *ent);

//
// menus
//
void M_Init(void);
void M_Keydown(int key);
void M_Draw(void);
void M_Menu_Main_f(void);
void M_ForceMenuOff(void);
void M_AddToServerList(netadr_t adr, char *info);

//
// cl_inv.c
//
void CL_ParseInventory(void);
void CL_KeyInventory(int key);
void CL_DrawInventory(void);

//
// cl_pred.c
//
void CL_PredictMovement(void);

// UI
void UI_SetTexture(const char *name);
void UI_Align(float align_x, float align_y);
static inline void UI_Center(void) { UI_Align(0.5f, 0.5f); }
void UI_Picture(const char *name, ...);
void UI_Fill(float r, float g, float b, float a);
void UI_Horizontal(void);
void UI_Vertical(void);
void UI_End(void);
void UI_Text(const char *format, ...);
void UI_ForceSize(float w, float h);
void UI_ForceWidth(float w);
void UI_ForceHeight(float h);

#if id386
void x86_TimerStart(void);
void x86_TimerStop(void);
void x86_TimerInit(unsigned long smallest, unsigned longest);
unsigned long *x86_TimerGetHistogram(void);
#endif

#endif // QUAKE2_CLIENT_CLIENT_H__