#include "quakedef.h"
#include "shader.h"

#ifdef _WIN32
#include "winquake.h"
#endif

#ifdef HLCLIENT
extern unsigned int r2d_be_flags;
struct hlcvar_s *QDECL GHL_CVarGetPointer(char *varname);



#if defined(_MSC_VER)
 #if _MSC_VER >= 1300
  #define __func__ __FUNCTION__
 #else
  #define __func__ "unknown"
 #endif
#else
 //I hope you're c99 and have a __func__
#endif

#define ignore(s) Con_Printf("Fixme: " s "\n")
#define notimpl(l) Con_Printf("halflife cl builtin not implemented on line %i\n", l)
#define notimpf(f) Con_Printf("halflife cl builtin %s not implemented\n", f)

#if HLCLIENT >= 1
#define HLCL_API_VERSION HLCLIENT
#else
#define HLCL_API_VERSION 7
#endif



void *vgui_panel;
qboolean VGui_Setup(void)
{
	void *vguidll;
	int (QDECL *init)(void);

	dllfunction_t funcs[] =
	{
		{(void*)&init, "init"},
		{NULL}
	};

	vguidll = Sys_LoadLibrary("vguiwrap", funcs);

	if (vguidll)
		vgui_panel = init();
	return !!vgui_panel;
}



#define HLPIC model_t*

typedef struct
{
	int	l;
	int r;
	int t;
	int b;
} hlsubrect_t;

typedef struct
{
	vec3_t origin;

#if HLCL_API_VERSION >= 7
	vec3_t viewangles;
	int weapons;
	float fov;
#else
	float viewheight;
	float maxspeed;
	vec3_t viewangles;
	vec3_t punchangles;
	int keys;
	int weapons;
	float fov;
	float idlescale;
	float mousesens;
#endif
} hllocalclientdata_t;

typedef struct
{
	short lerpmsecs;
	qbyte msec;
	vec3_t viewangles;

	float forwardmove;
	float sidemove;
	float upmove;

	qbyte lightlevel;
	unsigned short buttons;
	qbyte impulse;
	qbyte weaponselect;

	int impact_index;
	vec3_t impact_position;
} hlusercmd_t;

typedef struct
{
	char name[64];
	char sprite[64];
	int unk;
	int forscrwidth;
	hlsubrect_t rect;
} hlspriteinf_t;

typedef struct 
{
	char *name;
	short ping;
	qbyte isus;
	qbyte isspec;
	qbyte pl;
	char *model;
	short tcolour;
	short bcolour;
} hlplayerinfo_t;

typedef struct
{
	int size;
	int width;
	int height;
	int flags;
	int charheight;
	short charwidths[256];
} hlscreeninfo_t;

typedef struct
{
	int effect;
	byte_vec4_t c1;
	byte_vec4_t c2;
	float x;
	float y;
	float fadein;
	float fadeout;
	float holdtime;
	float fxtime;
	char *name;
	char *message;
} hlmsginfo_t;

typedef struct
{
	HLPIC (QDECL *pic_load) (char *picname);
	int (QDECL *pic_getnumframes) (HLPIC pic);
	int (QDECL *pic_getheight) (HLPIC pic, int frame);
	int (QDECL *pic_getwidth) (HLPIC pic, int frame);
	void (QDECL *pic_select) (HLPIC pic, int r, int g, int b);
	void (QDECL *pic_drawcuropaque) (int frame, int x, int y, void *loc);
	void (QDECL *pic_drawcuralphatest) (int frame, int x, int y, void *loc);
	void (QDECL *pic_drawcuradditive) (int frame, int x, int y, void *loc);
	void (QDECL *pic_enablescissor) (int x, int y, int width, int height);
	void (QDECL *pic_disablescissor) (void);
	hlspriteinf_t *(QDECL *pic_parsepiclist) (char *filename, int *numparsed);

	void (QDECL *fillrgba) (int x, int y, int width, int height, int r, int g, int b, int a);
	int (QDECL *getscreeninfo) (hlscreeninfo_t *info);
	void (QDECL *setcrosshair) (HLPIC pic, hlsubrect_t rect, int r, int g, int b);	//I worry about stuff like this
	
	struct hlcvar_s *(QDECL *cvar_register) (char *name, char *defvalue, int flags);
	float (QDECL *cvar_getfloat) (char *name);
	char *(QDECL *cvar_getstring) (char *name);

	void (QDECL *cmd_register) (char *name, void (*func) (void));
	void (QDECL *hooknetmsg) (char *msgname, void *func);
	void (QDECL *forwardcmd) (char *command);
	void (QDECL *localcmd) (char *command);

	void (QDECL *getplayerinfo) (int entnum, hlplayerinfo_t *result);

	void (QDECL *startsound_name) (char *name, float vol);
	void (QDECL *startsound_idx) (int idx, float vol);

	void (QDECL *anglevectors) (float *ina, float *outf, float *outr, float *outu);

	hlmsginfo_t *(QDECL *get_message_info) (char *name);	//translated+scaled+etc intro stuff
	int (QDECL *drawchar) (int x, int y, int charnum, int r, int g, int b);
	int (QDECL *drawstring) (int x, int y, char *string);
#if HLCL_API_VERSION >= 7
	void (QDECL *settextcolour) (float r, float b, float g);
#endif
	void (QDECL *drawstring_getlen) (char *string, int *outlen, int *outheight);
	void (QDECL *consoleprint) (char *str);
	void (QDECL *centerprint) (char *str);

#if HLCL_API_VERSION >= 7
	int (QDECL *getwindowcenterx)(void);	//yes, really, window center. for use with Get/SetCursorPos, the windows function.
	int (QDECL *getwindowcentery)(void);	//yes, really, window center. for use with Get/SetCursorPos, the windows function.
	void (QDECL *getviewnangles)(float*ang);
	void (QDECL *setviewnangles)(float*ang);
	void (QDECL *getmaxclients)(float*ang);
	void (QDECL *cvar_setvalue)(char *cvarname, char *value);

	int (QDECL *cmd_argc)(void);
	char *(QDECL *cmd_argv)(int i);
	void (QDECL *con_printf)(char *fmt, ...);
	void (QDECL *con_dprintf)(char *fmt, ...);
	void (QDECL *con_notificationprintf)(int pos, char *fmt, ...);
	void (QDECL *con_notificationprintfex)(void *info, char *fmt, ...);	//arg1 is of specific type
	char *(QDECL *physkey)(char *key);
	char *(QDECL *serverkey)(char *key);
	float (QDECL *getclientmaxspeed)(void);
	int (QDECL *checkparm)(char *str, char **next);
	int (QDECL *keyevent)(int key, int down);
	void (QDECL *getmousepos)(int *outx, int *outy);
	int (QDECL *movetypeisnoclip)(void);
	struct hlclent_s *(QDECL *getlocalplayer)(void);
	struct hlclent_s *(QDECL *getviewent)(void);
	struct hlclent_s *(QDECL *getentidx)(int idx);
	float (QDECL *getlocaltime)(void);
	void (QDECL *calcshake)(void);
	void (QDECL *applyshake)(float *,float *,float);
	int (QDECL *pointcontents)(float *point, float *truecon);
	int (QDECL *waterentity)(float *point);
	void (QDECL *traceline) (float *start, float *end, int flags, int hull, int forprediction);

	model_t *(QDECL *loadmodel)(char *modelname, int *mdlindex);
	int (QDECL *addrentity)(int type, void *ent);

	model_t *(QDECL *modelfrompic) (HLPIC pic);
	void (QDECL *soundatloc)(char*sound, float volume, float *org);

	unsigned short (QDECL *precacheevent)(int evtype, char *name);
	void (QDECL *playevent)(int flags, struct hledict_s *ent, unsigned short evindex, float delay, float *origin, float *angles, float f1, float f2, int i1, int i2, int b1, int b2);
	void (QDECL *weaponanimate)(int anim, int body);
	float (QDECL *randfloat) (float minv, float maxv);
	long (QDECL *randlong) (long minv, long maxv);
	void (QDECL *hookevent) (char *name, void (*func)(struct hlevent_s *event));
	int (QDECL *con_isshown) (void);
	char *(QDECL *getgamedir) (void);
	struct hlcvar_s *(QDECL *cvar_find) (char *name);
	char *(QDECL *lookupbinding) (char *command);
	char *(QDECL *getlevelname) (void);
	void (QDECL *getscreenfade) (struct hlsfade_s *fade);
	void (QDECL *setscreenfade) (struct hlsfade_s *fade);
	void *(QDECL *vgui_getpanel) (void);
	void (QDECL *vgui_paintback) (int extents[4]);

	void *(QDECL *loadfile) (char *path, int onhunk, int *length);
	char *(QDECL *parsefile) (char *data, char *token);
	void (QDECL *freefile) (void *file);

	struct hl_tri_api_s
	{
		int vers;
		int sentinal;
	} *triapi;
	struct hl_sfx_api_s
	{
		int vers;
		int sentinal;
	} *efxapi;
	struct hl_event_api_s 
	{
		int vers;
		int sentinal;
	} *eventapi;
	struct hl_demo_api_s 
	{
		int (QDECL *isrecording)(void);
		int (QDECL *isplaying)(void);
		int (QDECL *istimedemo)(void);
		void (QDECL *writedata)(int size, void *data);

		int sentinal;
	} *demoapi;
	struct hl_net_api_s 
	{
		int vers;
		int sentinal;
	} *netapi;

	struct hl_voicetweek_s
	{
		int sentinal;
	} *voiceapi;

	int (QDECL *forcedspectator) (void);
	model_t *(QDECL *loadmapsprite) (char *name);

	void (QDECL *fs_addgamedir) (char *basedir, char *appname);
	int (QDECL *expandfilename) (char *filename, char *outbuff, int outsize);

	char *(QDECL *player_key) (int pnum, char *key);
	void (QDECL *player_setkey) (char *key, char *value);	//wait, no pnum?

	qboolean (QDECL *getcdkey) (int playernum, char key[16]);
	int trackerfromplayer;
	int playerfromtracker;
	int (QDECL *sendcmd_unreliable) (char *cmd);
	void (QDECL *getsysmousepos) (long *xandy);
	void (QDECL *setsysmousepos) (int x, int y);
	void (QDECL *setmouseenable) (qboolean enable);
#endif

	int sentinal;
} CLHL_enginecgamefuncs_t;


typedef struct
{
	int (QDECL *HUD_VidInit) (void);
	int (QDECL *HUD_Init) (void);
	int (QDECL *HUD_Shutdown) (void);
	int (QDECL *HUD_Redraw) (float maptime, int inintermission);
	int (QDECL *HUD_UpdateClientData) (hllocalclientdata_t *localclientdata, float maptime);
	int (QDECL *HUD_Reset) (void);
#if HLCL_API_VERSION >= 7
	void (QDECL *CL_CreateMove) (float frametime, hlusercmd_t *cmd, int isplaying);
	void (QDECL *IN_ActivateMouse) (void);
	void (QDECL *IN_DeactivateMouse) (void);
	void (QDECL *IN_MouseEvent) (int buttonmask);
#endif
} CLHL_cgamefuncs_t;



//FIXME
typedef struct
{
	vec3_t	origin;
	vec3_t	oldorigin;

	int firstframe;
	int numframes;

	int		type;
	vec3_t	angles;
	int		flags;
	float	alpha;
	float	start;
	float	framerate;
	model_t	*model;
	int skinnum;
} explosion_t;





typedef struct
{
	char name[64];
	int (QDECL *hook) (char *name, int bufsize, void *bufdata);
} CLHL_UserMessages_t;
CLHL_UserMessages_t usermsgs[256];

int numnewhooks;
CLHL_UserMessages_t pendingusermsgs[256];


static HLPIC selectedpic;

float hl_viewmodelsequencetime;
int hl_viewmodelsequencecur;
int hl_viewmodelsequencebody;





HLPIC QDECL CLGHL_pic_load (char *picname)
{
	return Mod_ForName(picname, false);
//	return R2D_SafeCachePic(picname);
}
int QDECL CLGHL_pic_getnumframes (HLPIC pic)
{
	if (pic)
		return pic->numframes;
	else
		return 0;
}

static mspriteframe_t *getspriteframe(HLPIC pic, int frame)
{
	msprite_t		*psprite;
	mspritegroup_t *pgroup;
	if (!pic)
		return NULL;
	psprite = pic->cache.data;
	if (!psprite)
		return NULL;

	if (psprite->frames[frame].type == SPR_SINGLE)
		return psprite->frames[frame].frameptr;
	else
	{
		pgroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
		return pgroup->frames[0];
	}
}
static mpic_t *getspritepic(HLPIC pic, int frame)
{
	mspriteframe_t *f;
	f = getspriteframe(pic, frame);
	if (f)
		return f->shader;
	return NULL;
}

int QDECL CLGHL_pic_getheight (HLPIC pic, int frame)
{
	mspriteframe_t *pframe;

	pframe = getspriteframe(pic, frame);
	if (!pframe)
		return 0;

	return pframe->shader->width;
}
int QDECL CLGHL_pic_getwidth (HLPIC pic, int frame)
{
	mspriteframe_t *pframe;

	pframe = getspriteframe(pic, frame);
	if (!pframe)
		return 0;

	return pframe->shader->height;
}
void QDECL CLGHL_pic_select (HLPIC pic, int r, int g, int b)
{
	selectedpic = pic;
	R2D_ImageColours(r/255.0f, g/255.0f, b/255.0f, 1);
}
void QDECL CLGHL_pic_drawcuropaque (int frame, int x, int y, hlsubrect_t *loc)
{
	mpic_t *pic = getspritepic(selectedpic, frame);
	if (!pic)
		return;

	//faster SW render: no blends/holes
	pic->flags &= ~1;

	R2D_Image(x, y,
		loc->r-loc->l, loc->b-loc->t,
		(float)loc->l/pic->width, (float)loc->t/pic->height,
		(float)loc->r/pic->width, (float)loc->b/pic->height,
		pic);
}
void QDECL CLGHL_pic_drawcuralphtest (int frame, int x, int y, hlsubrect_t *loc)
{
	mpic_t *pic = getspritepic(selectedpic, frame);
	if (!pic)
		return;
	//use some kind of alpha
	pic->flags |= 1;

	R2D_Image(x, y,
		loc->r-loc->l, loc->b-loc->t,
		(float)loc->l/pic->width, (float)loc->t/pic->height,
		(float)loc->r/pic->width, (float)loc->b/pic->height,
		pic);
}
void QDECL CLGHL_pic_drawcuradditive (int frame, int x, int y, hlsubrect_t *loc)
{
	mpic_t *pic = getspritepic(selectedpic, frame);
	if (!pic)
		return;

	r2d_be_flags = BEF_FORCEADDITIVE;

	//use some kind of alpha
	pic->flags |= 1;
	if (loc)
	{
		R2D_Image(x, y,
			loc->r-loc->l, loc->b-loc->t,
			(float)loc->l/pic->width, (float)loc->t/pic->height,
			(float)loc->r/pic->width, (float)loc->b/pic->height,
			pic);
	}
	else
	{
		R2D_Image(x, y,
			pic->width, pic->height,
			0, 0,
			1, 1,
			pic);
	}
	r2d_be_flags = 0;
}
void QDECL CLGHL_pic_enablescissor (int x, int y, int width, int height)
{
}
void QDECL CLGHL_pic_disablescissor (void)
{
}
hlspriteinf_t *QDECL CLGHL_pic_parsepiclist (char *filename, int *numparsed)
{
	hlspriteinf_t *result;
	int entry;
	int entries;
	void *file;
	char *pos;

	*numparsed = 0;

	FS_LoadFile(filename, &file);
	if (!file)
		return NULL;
	pos = file;

	pos = COM_Parse(pos);
	entries = atoi(com_token);

	//name, res, pic, x, y, w, h

	result = Z_Malloc(sizeof(*result)*entries);
	for (entry = 0; entry < entries; entry++)
	{
		pos = COM_Parse(pos);
		Q_strncpyz(result[entry].name, com_token, sizeof(result[entry].name));

		pos = COM_Parse(pos);
		result[entry].forscrwidth = atoi(com_token);

		pos = COM_Parse(pos);
		Q_strncpyz(result[entry].sprite, com_token, sizeof(result[entry].name));

		pos = COM_Parse(pos);
		result[entry].rect.l = atoi(com_token);

		pos = COM_Parse(pos);
		result[entry].rect.t = atoi(com_token);

		pos = COM_Parse(pos);
		result[entry].rect.r = result[entry].rect.l+atoi(com_token);

		pos = COM_Parse(pos);
		result[entry].rect.b = result[entry].rect.t+atoi(com_token);

		if (pos)
			*numparsed = entry;
	}

	if (!pos || COM_Parse(pos))
		Con_Printf("unexpected end of file\n");

	FS_FreeFile(file);

	return result;
}

void QDECL CLGHL_fillrgba (int x, int y, int width, int height, int r, int g, int b, int a)
{
}
int QDECL CLGHL_getscreeninfo (hlscreeninfo_t *info)
{
	int i;
	if (info->size != sizeof(*info))
		return false;

	info->width = vid.width;
	info->height = vid.height;
	info->flags = 0;
	info->charheight = 8;
	for (i = 0; i < 256; i++)
		info->charwidths[i] = 8;

	return true;
}
void QDECL CLGHL_setcrosshair (HLPIC pic, hlsubrect_t rect, int r, int g, int b)
{
}

struct hlcvar_s *QDECL CLGHL_cvar_register (char *name, char *defvalue, int flags)
{
	if (Cvar_Get(name, defvalue, 0, "Halflife cvars"))
		return GHL_CVarGetPointer(name);
	else
		return NULL;
}
float QDECL CLGHL_cvar_getfloat (char *name)
{
	cvar_t *var = Cvar_FindVar(name);
	if (var)
		return var->value;
	return 0;
}
char *QDECL CLGHL_cvar_getstring (char *name)
{
	cvar_t *var = Cvar_FindVar(name);
	if (var)
		return var->string;
	return "";
}

void QDECL CLGHL_cmd_register (char *name, xcommand_t func)
{
	Cmd_AddCommand(name, func);
}
void QDECL CLGHL_hooknetmsg (char *msgname, void *func)
{
	int i;
	//update the current list now.
	for (i = 0; i < sizeof(usermsgs)/sizeof(usermsgs[0]); i++)
	{
		if (!strcmp(usermsgs[i].name, msgname))
		{
			usermsgs[i].hook = func;
			break;	//one per name
		}
	}

	//we already asked for it perhaps?
	for (i = 0; i < numnewhooks; i++)
	{
		if (!strcmp(pendingusermsgs[i].name, msgname))
		{
			pendingusermsgs[i].hook = func;
			return;	//nothing to do
		}
	}

	Q_strncpyz(pendingusermsgs[numnewhooks].name, msgname, sizeof(pendingusermsgs[i].name));
	pendingusermsgs[numnewhooks].hook = func;
	numnewhooks++;
}
void QDECL CLGHL_forwardcmd (char *command)
{
	CL_SendClientCommand(true, "%s", command);
}
void QDECL CLGHL_localcmd (char *command)
{
	Cbuf_AddText(command, RESTRICT_SERVER);
}

void QDECL CLGHL_getplayerinfo (int entnum, hlplayerinfo_t *result)
{
	player_info_t *player;
	entnum--;
	if (entnum < 0 || entnum >= MAX_CLIENTS)
		return;

	player = &cl.players[entnum];
	result->name = player->name;
	result->ping = player->ping;
	result->tcolour = player->rtopcolor;
	result->bcolour = player->rbottomcolor;
	result->isus = true;
	result->isspec = player->spectator;
	result->pl = player->pl;
	if (player->skin)
		result->model = player->skin->name;
	else
		result->model = "";
}

void QDECL CLGHL_startsound_name (char *name, float vol)
{
	sfx_t *sfx = S_PrecacheSound (name);
	if (!sfx)
	{
		Con_Printf ("CLGHL_startsound_name: can't cache %s\n", name);
		return;
	}
	S_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0);
}
void QDECL CLGHL_startsound_idx (int idx, float vol)
{
	sfx_t *sfx = cl.sound_precache[idx];
	if (!sfx)
	{
		Con_Printf ("CLGHL_startsound_name: index not precached %s\n", name);
		return;
	}
	S_StartSound (-1, -1, sfx, vec3_origin, vol, 1, 0, 0);
}

void QDECL CLGHL_anglevectors (float *ina, float *outf, float *outr, float *outu)
{
	AngleVectors(ina, outf, outr, outu);
}

hlmsginfo_t *QDECL CLGHL_get_message_info (char *name)
{
	//fixme: add parser
	hlmsginfo_t *ret;
	ret = Z_Malloc(sizeof(*ret));
	memset(ret, 0, sizeof(*ret));
	ret->name = name;
	ret->message = name;
	ret->x = 0;
	ret->y = 0;
	*(int*)&ret->c1 = 0xffffffff;
	*(int*)&ret->c2 = 0xffffffff;
	ret->effect = 0;
	ret->fadein = 0;
	ret->fadeout = 0;
	ret->fxtime = 0;
	ret->holdtime = 1000;
	return ret;
}
int QDECL CLGHL_drawchar (int x, int y, int charnum, int r, int g, int b)
{
	return 0;
}
int QDECL CLGHL_drawstring (int x, int y, char *string)
{
	return 0;
}
void QDECL CLGHL_settextcolour(float r, float g, float b)
{
}
void QDECL CLGHL_drawstring_getlen (char *string, int *outlen, int *outheight)
{
	*outlen = strlen(string)*8;
	*outheight = 8;
}
void QDECL CLGHL_consoleprint (char *str)
{
	Con_Printf("%s", str);
}
void QDECL CLGHL_centerprint (char *str)
{
	SCR_CenterPrint(0, str, true);
}


int QDECL CLGHL_getwindowcenterx(void)
{
	return window_center_x;
}
int QDECL CLGHL_getwindowcentery(void)
{
	return window_center_y;
}
void QDECL CLGHL_getviewnangles(float*ang)
{
	VectorCopy(cl.playerview[0].viewangles, ang);
}
void QDECL CLGHL_setviewnangles(float*ang)
{
	VectorCopy(ang, cl.playerview[0].viewangles);
}
void QDECL CLGHL_getmaxclients(float*ang){notimpf(__func__);}
void QDECL CLGHL_cvar_setvalue(char *cvarname, char *value){notimpf(__func__);}

int QDECL CLGHL_cmd_argc(void)
{
	return Cmd_Argc();
}
char *QDECL CLGHL_cmd_argv(int i)
{
	return Cmd_Argv(i);
}
#define CLGHL_con_printf Con_Printf//void CLGHL_con_printf(char *fmt, ...){notimp(__LINE__);}
#define CLGHL_con_dprintf Con_DPrintf//void CLGHL_con_dprintf(char *fmt, ...){notimp(__LINE__);}
void QDECL CLGHL_con_notificationprintf(int pos, char *fmt, ...){notimpf(__func__);}
void QDECL CLGHL_con_notificationprintfex(void *info, char *fmt, ...){notimpf(__func__);}
char *QDECL CLGHL_physkey(char *key){notimpf(__func__);return NULL;}
char *QDECL CLGHL_serverkey(char *key){notimpf(__func__);return NULL;}
float QDECL CLGHL_getclientmaxspeed(void)
{
	return 320;
}
int QDECL CLGHL_checkparm(char *str, const char **next)
{
	int i;
	i = COM_CheckParm(str);
	if (next)
	{
		if (i && i+1<com_argc)
			*next = com_argv[i+1];
		else
			*next = NULL;
	}
	return i;
}
int QDECL CLGHL_keyevent(int key, int down)
{
	if (key >= 241 && key <= 241+5)
		Key_Event(0, K_MOUSE1+key-241, 0, down);
	else
		Con_Printf("CLGHL_keyevent: Unrecognised HL key code\n");
	return true;	//fixme: check the return type
}
void QDECL CLGHL_getmousepos(int *outx, int *outy){notimpf(__func__);}
int QDECL CLGHL_movetypeisnoclip(void){notimpf(__func__);return 0;}
struct hlclent_s *QDECL CLGHL_getlocalplayer(void){notimpf(__func__);return NULL;}
struct hlclent_s *QDECL CLGHL_getviewent(void){notimpf(__func__);return NULL;}
struct hlclent_s *QDECL CLGHL_getentidx(int idx)
{
	notimpf(__func__);return NULL;
}
float QDECL CLGHL_getlocaltime(void){return cl.time;}
void QDECL CLGHL_calcshake(void){notimpf(__func__);}
void QDECL CLGHL_applyshake(float *origin, float *angles, float factor){notimpf(__func__);}
int QDECL CLGHL_pointcontents(float *point, float *truecon){notimpf(__func__);return 0;}
int QDECL CLGHL_entcontents(float *point){notimpf(__func__);return 0;}
void QDECL CLGHL_traceline(float *start, float *end, int flags, int hull, int forprediction){notimpf(__func__);}

model_t *QDECL CLGHL_loadmodel(char *modelname, int *mdlindex){notimpf(__func__);return Mod_ForName(modelname, false);}
int QDECL CLGHL_addrentity(int type, void *ent){notimpf(__func__);return 0;}

model_t *QDECL CLGHL_modelfrompic(HLPIC pic){notimpf(__func__);return NULL;}
void QDECL CLGHL_soundatloc(char*sound, float volume, float *org){notimpf(__func__);}

unsigned short QDECL CLGHL_precacheevent(int evtype, char *name){notimpf(__func__);return 0;}
void QDECL CLGHL_playevent(int flags, struct hledict_s *ent, unsigned short evindex, float delay, float *origin, float *angles, float f1, float f2, int i1, int i2, int b1, int b2){notimpf(__func__);}
void QDECL CLGHL_weaponanimate(int newsequence, int body)
{
	hl_viewmodelsequencetime = cl.time;
	hl_viewmodelsequencecur = newsequence;
	hl_viewmodelsequencebody = body;
}
float QDECL CLGHL_randfloat(float minv, float maxv){notimpf(__func__);return minv;}
long QDECL CLGHL_randlong(long minv, long maxv){notimpf(__func__);return minv;}
void QDECL CLGHL_hookevent(char *name, void (*func)(struct hlevent_s *event))
{
	Con_Printf("CLGHL_hookevent: not implemented. %s\n", name);
//	notimpf(__func__);
}
int QDECL CLGHL_con_isshown(void)
{
	return scr_con_current > 0;
}
char *QDECL CLGHL_getgamedir(void)
{
	extern char	gamedirfile[];
	return gamedirfile;
}
struct hlcvar_s *QDECL CLGHL_cvar_find(char *name)
{
	return GHL_CVarGetPointer(name);
}
char *QDECL CLGHL_lookupbinding(char *command)
{
	return NULL;
}
char *QDECL CLGHL_getlevelname(void)
{
	if (!cl.worldmodel)
		return "";
	return cl.worldmodel->name;
}
void QDECL CLGHL_getscreenfade(struct hlsfade_s *fade){notimpf(__func__);}
void QDECL CLGHL_setscreenfade(struct hlsfade_s *fade){notimpf(__func__);}
void *QDECL CLGHL_vgui_getpanel(void)
{
	return vgui_panel;
}
void QDECL CLGHL_vgui_paintback(int extents[4])
{
	notimpf(__func__);
}

void *QDECL CLGHL_loadfile(char *path, int alloctype, int *length)
{
	void *ptr = NULL;
	int flen = -1;
	if (alloctype == 5)
	{
		flen = FS_LoadFile(path, &ptr);
	}
	else
		notimpf(__func__);	//don't leak, just fail

	if (length)
		*length = flen;

	return ptr;
}
char *QDECL CLGHL_parsefile(char *data, char *token)
{
	return COM_ParseOut(data, token, 1024);
}
void QDECL CLGHL_freefile(void *file)
{
	//only valid for alloc type 5
	FS_FreeFile(file);
}


int QDECL CLGHL_forcedspectator(void)
{
	return cls.demoplayback;
}
model_t *QDECL CLGHL_loadmapsprite(char *name)
{
	notimpf(__func__);return NULL;
}

void QDECL CLGHL_fs_addgamedir(char *basedir, char *appname){notimpf(__func__);}
int QDECL CLGHL_expandfilename(char *filename, char *outbuff, int outsize){notimpf(__func__);return false;}

char *QDECL CLGHL_player_key(int pnum, char *key){notimpf(__func__);return NULL;}
void QDECL CLGHL_player_setkey(char *key, char *value){notimpf(__func__);return;}

qboolean QDECL CLGHL_getcdkey(int playernum, char key[16]){notimpf(__func__);return false;}
int QDECL CLGHL_trackerfromplayer(int pslot){notimpf(__func__);return 0;}
int QDECL CLGHL_playerfromtracker(int tracker){notimpf(__func__);return 0;}
int QDECL CLGHL_sendcmd_unreliable(char *cmd){notimpf(__func__);return 0;}
void QDECL CLGHL_getsysmousepos(long *xandy)
{
#ifdef _WIN32
	GetCursorPos((LPPOINT)xandy);
#endif
}
void QDECL CLGHL_setsysmousepos(int x, int y)
{
#ifdef _WIN32
	SetCursorPos(x, y);
#endif
}
void QDECL CLGHL_setmouseenable(qboolean enable)
{
	extern cvar_t _windowed_mouse;
	Cvar_Set(&_windowed_mouse, enable?"1":"0");
}



#if HLCL_API_VERSION >= 7
int QDECL CLGHL_demo_isrecording(void)
{
	return cls.demorecording;
}
int QDECL CLGHL_demo_isplaying(void)
{
	return cls.demoplayback;
}
int QDECL CLGHL_demo_istimedemo(void)
{
	return cls.timedemo;
}
void QDECL CLGHL_demo_writedata(int size, void *data)
{
	notimpf(__func__);
}

struct hl_demo_api_s hl_demo_api = 
{
		CLGHL_demo_isrecording,
		CLGHL_demo_isplaying,
		CLGHL_demo_istimedemo,
		CLGHL_demo_writedata,

		0xdeadbeef
};
#endif

CLHL_cgamefuncs_t CLHL_cgamefuncs;
CLHL_enginecgamefuncs_t CLHL_enginecgamefuncs =
{
	CLGHL_pic_load,
	CLGHL_pic_getnumframes,
	CLGHL_pic_getheight,
	CLGHL_pic_getwidth,
	CLGHL_pic_select,
	CLGHL_pic_drawcuropaque,
	CLGHL_pic_drawcuralphtest,
	CLGHL_pic_drawcuradditive,
	CLGHL_pic_enablescissor,
	CLGHL_pic_disablescissor,
	CLGHL_pic_parsepiclist,

	CLGHL_fillrgba,
	CLGHL_getscreeninfo,
	CLGHL_setcrosshair,

	CLGHL_cvar_register,
	CLGHL_cvar_getfloat,
	CLGHL_cvar_getstring,

	CLGHL_cmd_register,
	CLGHL_hooknetmsg,
	CLGHL_forwardcmd,
	CLGHL_localcmd,

	CLGHL_getplayerinfo,

	CLGHL_startsound_name,
	CLGHL_startsound_idx,

	CLGHL_anglevectors,

	CLGHL_get_message_info,
	CLGHL_drawchar,
	CLGHL_drawstring,
#if HLCL_API_VERSION >= 7
	CLGHL_settextcolour,
#endif
	CLGHL_drawstring_getlen,
	CLGHL_consoleprint,
	CLGHL_centerprint,

#if HLCL_API_VERSION >= 7
	CLGHL_getwindowcenterx,
	CLGHL_getwindowcentery,
	CLGHL_getviewnangles,
	CLGHL_setviewnangles,
	CLGHL_getmaxclients,
	CLGHL_cvar_setvalue,

	CLGHL_cmd_argc,
	CLGHL_cmd_argv,
	CLGHL_con_printf,
	CLGHL_con_dprintf,
	CLGHL_con_notificationprintf,
	CLGHL_con_notificationprintfex,
	CLGHL_physkey,
	CLGHL_serverkey,
	CLGHL_getclientmaxspeed,
	CLGHL_checkparm,
	CLGHL_keyevent,
	CLGHL_getmousepos,
	CLGHL_movetypeisnoclip,
	CLGHL_getlocalplayer,
	CLGHL_getviewent,
	CLGHL_getentidx,
	CLGHL_getlocaltime,
	CLGHL_calcshake,
	CLGHL_applyshake,
	CLGHL_pointcontents,
	CLGHL_entcontents,
	CLGHL_traceline,

	CLGHL_loadmodel,
	CLGHL_addrentity,

	CLGHL_modelfrompic,
	CLGHL_soundatloc,

	CLGHL_precacheevent,
	CLGHL_playevent,
	CLGHL_weaponanimate,
	CLGHL_randfloat,
	CLGHL_randlong,
	CLGHL_hookevent,
	CLGHL_con_isshown,
	CLGHL_getgamedir,
	CLGHL_cvar_find,
	CLGHL_lookupbinding,
	CLGHL_getlevelname,
	CLGHL_getscreenfade,
	CLGHL_setscreenfade,
	CLGHL_vgui_getpanel,
	CLGHL_vgui_paintback,

	CLGHL_loadfile,
	CLGHL_parsefile,
	CLGHL_freefile,

	NULL, //triapi;
	NULL, //efxapi;
	NULL, //eventapi;
	&hl_demo_api,
	NULL, //netapi;

//sdk 2.3+
	NULL, //voiceapi;

	CLGHL_forcedspectator,
	CLGHL_loadmapsprite,

	CLGHL_fs_addgamedir,
	CLGHL_expandfilename,

	CLGHL_player_key,
	CLGHL_player_setkey,

	CLGHL_getcdkey,
	(void*)0xdeaddead,//CLGHL_trackerfromplayer;
	(void*)0xdeaddead,//CLGHL_playerfromtracker;
	CLGHL_sendcmd_unreliable,
	CLGHL_getsysmousepos,
	CLGHL_setsysmousepos,
	CLGHL_setmouseenable,
#endif

	0xdeadbeef
};

dllhandle_t clg;

int CLHL_GamecodeDoesMouse(void)
{
#if HLCL_API_VERSION >= 7
	if (clg && CLHL_cgamefuncs.CL_CreateMove)
		return true;
#endif
	return false;
}

int CLHL_MouseEvent(unsigned int buttonmask)
{
#if HLCL_API_VERSION >= 7
	if (!CLHL_GamecodeDoesMouse())
		return false;

	CLHL_cgamefuncs.IN_MouseEvent(buttonmask);
	return true;
#else
	return false;
#endif
}

void CLHL_SetMouseActive(int activate)
{
#if HLCL_API_VERSION >= 7
	static int oldactive;
	if (!clg)
	{
		oldactive = false;
		return;
	}
	if (activate == oldactive)
		return;
	oldactive = activate;

	if (activate)
	{
		if (CLHL_cgamefuncs.IN_ActivateMouse)
			CLHL_cgamefuncs.IN_ActivateMouse();
	}
	else
	{
		if (CLHL_cgamefuncs.IN_DeactivateMouse)
			CLHL_cgamefuncs.IN_DeactivateMouse();
	}
#endif
}

void CLHL_UnloadClientGame(void)
{
	if (!clg)
		return;

	CLHL_SetMouseActive(false);
	if (CLHL_cgamefuncs.HUD_Shutdown)
		CLHL_cgamefuncs.HUD_Shutdown();
	Sys_CloseLibrary(clg);
	memset(&CLHL_cgamefuncs, 0, sizeof(CLHL_cgamefuncs));
	clg = NULL;

	hl_viewmodelsequencetime = 0;
}

void CLHL_LoadClientGame(void)
{
	char fullname[MAX_OSPATH];
	char *path;

	int (QDECL *initfunc)(CLHL_enginecgamefuncs_t *funcs, int version);
	dllfunction_t funcs[] =
	{
		{(void*)&initfunc, "Initialize"},
		{NULL}
	};

	CLHL_UnloadClientGame();

	memset(&CLHL_cgamefuncs, 0, sizeof(CLHL_cgamefuncs));

	clg = NULL;//Sys_LoadLibrary("C:/Incoming/d/Half-Life/sdks/hlsdk-2.3-p3/hlsdk-2.3-p3/multiplayer/cl_dll/Debug/client", funcs);
	if (!clg)
	{
		path = NULL;
		while((path = COM_NextPath (path)))
		{
			if (!path)
				return;		// couldn't find one anywhere
			snprintf (fullname, sizeof(fullname), "%s/%s", path, "cl_dlls/client");
			clg = Sys_LoadLibrary(fullname, funcs);
			if (clg)
				break;
		}
	}

	if (!clg)
		return;

	if (!initfunc(&CLHL_enginecgamefuncs, HLCL_API_VERSION))
	{
		Con_Printf("HalfLife cldll is not version %i\n", HLCL_API_VERSION);
		Sys_CloseLibrary(clg);
		clg = NULL;
		return;
	}

	CLHL_cgamefuncs.HUD_VidInit = (void*)Sys_GetAddressForName(clg, "HUD_VidInit");
	CLHL_cgamefuncs.HUD_Init = (void*)Sys_GetAddressForName(clg, "HUD_Init");
	CLHL_cgamefuncs.HUD_Shutdown = (void*)Sys_GetAddressForName(clg, "HUD_Shutdown");
	CLHL_cgamefuncs.HUD_Redraw = (void*)Sys_GetAddressForName(clg, "HUD_Redraw");
	CLHL_cgamefuncs.HUD_UpdateClientData = (void*)Sys_GetAddressForName(clg, "HUD_UpdateClientData");
	CLHL_cgamefuncs.HUD_Reset = (void*)Sys_GetAddressForName(clg, "HUD_Reset");
#if HLCL_API_VERSION >= 7
	CLHL_cgamefuncs.CL_CreateMove = (void*)Sys_GetAddressForName(clg, "CL_CreateMove");
	CLHL_cgamefuncs.IN_ActivateMouse = (void*)Sys_GetAddressForName(clg, "IN_ActivateMouse");
	CLHL_cgamefuncs.IN_DeactivateMouse = (void*)Sys_GetAddressForName(clg, "IN_DeactivateMouse");
	CLHL_cgamefuncs.IN_MouseEvent = (void*)Sys_GetAddressForName(clg, "IN_MouseEvent");
#endif

	VGui_Setup();

	if (CLHL_cgamefuncs.HUD_Init)
		CLHL_cgamefuncs.HUD_Init();
	if (CLHL_cgamefuncs.HUD_VidInit)
		CLHL_cgamefuncs.HUD_VidInit();
}

int CLHL_BuildUserInput(int msecs, usercmd_t *cmd)
{
#if HLCL_API_VERSION >= 7
	hlusercmd_t hlcmd;
	if (!clg || !CLHL_cgamefuncs.CL_CreateMove)
		return false;

	CLHL_cgamefuncs.CL_CreateMove(msecs/255.0f, &hlcmd, cls.state>=ca_active && !cls.demoplayback);

#define ANGLE2SHORT(x) (x) * (65536/360.0)
	cmd->msec = msecs;
	cmd->angles[0] = ANGLE2SHORT(hlcmd.viewangles[0]);
	cmd->angles[1] = ANGLE2SHORT(hlcmd.viewangles[1]);
	cmd->angles[2] = ANGLE2SHORT(hlcmd.viewangles[2]);
	cmd->forwardmove = hlcmd.forwardmove;
	cmd->sidemove = hlcmd.sidemove;
	cmd->upmove = hlcmd.upmove;
	cmd->weapon = hlcmd.weaponselect;
	cmd->impulse = hlcmd.impulse;
	cmd->buttons = hlcmd.buttons;
	cmd->lightlevel = hlcmd.lightlevel;
	return true;
#else
	return false;
#endif
}

int CLHL_DrawHud(void)
{
	extern kbutton_t in_attack;
	hllocalclientdata_t state;

	if (!clg || !CLHL_cgamefuncs.HUD_Redraw)
		return false;

	memset(&state, 0, sizeof(state));

//	state.origin;
//	state.viewangles;
#if HLCL_API_VERSION < 7
//	state.viewheight;
//	state.maxspeed;
//	state.punchangles;
	state.idlescale = 0;
	state.mousesens = 0;
	state.keys = (in_attack.state[0]&3)?1:0;
#endif
	state.weapons = cl.playerview[0].stats[STAT_ITEMS];
	state.fov = 90;

	V_StopPitchDrift(0);

	CLHL_cgamefuncs.HUD_UpdateClientData(&state, cl.time);

	return CLHL_cgamefuncs.HUD_Redraw(cl.time, cl.intermission);	
}

int CLHL_AnimateViewEntity(entity_t *ent)
{
	float time;
	if (!hl_viewmodelsequencetime)
		return false;

	time = cl.time - hl_viewmodelsequencetime;
	ent->framestate.g[FS_REG].frame[0] = hl_viewmodelsequencecur;
	ent->framestate.g[FS_REG].frame[1] = hl_viewmodelsequencecur;
	ent->framestate.g[FS_REG].frametime[0] = time;
	ent->framestate.g[FS_REG].frametime[1] = time;
	return true;
}

explosion_t *CL_AllocExplosion (void);

int CLHL_ParseGamePacket(void)
{
	int subcode;
	char *end;
	char *str;
	int tempi;

	end = net_message.data+msg_readcount+2+MSG_ReadShort();

	if (end > net_message.data+net_message.cursize)
		return false;

	subcode = MSG_ReadByte();

	if (usermsgs[subcode].hook)
		if (usermsgs[subcode].hook(usermsgs[subcode].name, end - (net_message.data+msg_readcount), net_message.data+msg_readcount))
		{
			msg_readcount = end - net_message.data;
			return true;
		}

	switch(subcode)
	{
	case 1:
		//register the server-sent code.
		tempi = MSG_ReadByte();
		str = MSG_ReadString();
		Q_strncpyz(usermsgs[tempi].name, str, sizeof(usermsgs[tempi].name));

		//get the builtin to reregister its hooks.
		for (tempi = 0; tempi < numnewhooks; tempi++)
			CLGHL_hooknetmsg(pendingusermsgs[tempi].name, pendingusermsgs[tempi].hook);
		break;
	case svc_temp_entity:
		{
			vec3_t startp, endp, vel;
			float ang;
			int midx;
			int mscale;
			int mrate;
			int flags;
			int lifetime;
			explosion_t *ef;
			extern cvar_t temp1;

			subcode = MSG_ReadByte();

			if (temp1.value)
				Con_Printf("Temp ent %i\n", subcode);
			switch(subcode)
			{
			case 3:
				MSG_ReadPos(startp);
				midx = MSG_ReadShort();
				mscale = MSG_ReadByte();
				mrate = MSG_ReadByte();
				flags = MSG_ReadByte();
				if (!(flags & 8))
					P_RunParticleEffectType(startp, NULL, 1, pt_explosion);
				if (!(flags & 4))
					S_StartSound(0, 0, S_PrecacheSound("explosion"), startp, 1, 1, 0, 0);
				if (!(flags & 2))
					CL_NewDlight(0, startp, 200, 1, 2.0,2.0,2.0);

				ef = CL_AllocExplosion();
				VectorCopy(startp, ef->origin);
				ef->start = cl.time;
				ef->model = cl.model_precache[midx];
				ef->framerate = mrate;
				ef->firstframe = 0;
				ef->numframes = ef->model->numframes;
				if (!(flags & 1))
					ef->flags = Q2RF_ADDITIVE;
				else
					ef->flags = 0;
				break;
			case 4:
				MSG_ReadPos(startp);
				P_RunParticleEffectType(startp, NULL, 1, pt_tarexplosion);
				break;
			case 5:
				MSG_ReadPos(startp);
				MSG_ReadShort();
				MSG_ReadByte();
				MSG_ReadByte();
				break;
			case 6:
				MSG_ReadPos(startp);
				MSG_ReadPos(endp);
				break;
			case 9:
				MSG_ReadPos(startp);
				P_RunParticleEffectType(startp, NULL, 1, ptdp_spark);
				break;
			case 22:
				MSG_ReadShort();
				MSG_ReadShort();
				MSG_ReadByte();
				MSG_ReadByte();
				MSG_ReadByte();
				MSG_ReadByte();
				MSG_ReadByte();
				break;
			case 23:
				MSG_ReadPos(startp);
				MSG_ReadShort();
				MSG_ReadByte();
				MSG_ReadByte();
				MSG_ReadByte();
				break;
			case 106:
				MSG_ReadPos(startp);
				MSG_ReadPos(vel);
				ang = MSG_ReadAngle();
				midx = MSG_ReadShort();
				MSG_ReadByte();
				lifetime = MSG_ReadByte();

				ef = CL_AllocExplosion();
				VectorCopy(startp, ef->origin);
				ef->start = cl.time;
				ef->angles[1] = ang;
				ef->model = cl.model_precache[midx];
				ef->firstframe = 0;
				ef->numframes = lifetime;
				ef->flags = 0;
				ef->framerate = 10;
				break;
			case 108:
				MSG_ReadPos(startp);
				MSG_ReadPos(endp);
				MSG_ReadPos(vel);
				MSG_ReadByte();
				MSG_ReadShort();
				MSG_ReadByte();
				MSG_ReadByte();
				MSG_ReadByte();
				break;
			case 109:
				MSG_ReadPos(startp);
				MSG_ReadShort();
				MSG_ReadByte();
				break;
			case 116:
				MSG_ReadPos(startp);
				MSG_ReadByte();
				break;
			case 117:
				MSG_ReadPos(startp);
				MSG_ReadByte()+256;
				break;
			case 118:
				MSG_ReadPos(startp);
				MSG_ReadByte()+256;
				break;
			default:
				Con_Printf("CLHL_ParseGamePacket: Unable to parse gamecode tempent %i\n", subcode);
				msg_readcount = end - net_message.data;
				break;
			}
		}
		if (msg_readcount != end - net_message.data)
		{
			Con_Printf("CLHL_ParseGamePacket: Gamecode temp entity %i not parsed correctly read %i bytes too many\n", subcode, msg_readcount - (end - net_message.data));
			msg_readcount = end - net_message.data;
		}
		break;
	case svc_intermission:
		//nothing.
		cl.intermission = true;
		break;
	case svc_cdtrack:
		cl.cdtrack = MSG_ReadByte();
		MSG_ReadByte();
		CDAudio_Play ((qbyte)cl.cdtrack, true);
		break;

	case 35: //svc_weaponanimation:
		tempi = MSG_ReadByte();
		CLGHL_weaponanimate(tempi, MSG_ReadByte());
		break;
	case 37: //svc_roomtype
		tempi = MSG_ReadShort();
		S_SetUnderWater(tempi==14||tempi==15||tempi==16);
		break;
	default:
		Con_Printf("Unrecognised gamecode packet %i (%s)\n", subcode, usermsgs[subcode].name);
		msg_readcount = end - net_message.data;
		break;
	}

	if (msg_readcount != end - net_message.data)
	{
		Con_Printf("CLHL_ParseGamePacket: Gamecode packet %i not parsed correctly read %i bytestoo many\n", subcode, msg_readcount - (end - net_message.data));
		msg_readcount = end - net_message.data;
	}
	return true;
}

#endif
