/*
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.

*/

// vid_dos.c -- DOS video driver frontend

#include "client.h"
#include "sinmdl.h"
#include "console3d.h"
#include "vid_dos.h"

extern debugline_t	*DebugLines;
extern int				numDebugLines;

int vidref_val;
enum
{
	VIDREF_SOFT,
	VIDREF_GL,
	VIDREF_OTHER
};

#define NUM_VID_DRIVERS 2
#define REF_SOFT	0
#define REF_OPENGL	1

cvar_t *vid_ref;
cvar_t *vid_fullscreen;
cvar_t *vid_gamma;
cvar_t *scr_shotname;
cvar_t *vid_checkamd;
extern cvar_t *scr_viewsize;	/* client/cl_scrn.c */

static void VID_ListModes_f (void);
static void VID_Restart_f (void);

static vmodeinfo_t		*vid_modes;
static int			vid_nummodes;

static const char *		resolution_names[MAX_VIDEOMODES + 1];
				/* initialized by VID_LoadRefresh() */

viddef_t	viddef; /* global video state */

refexport_t	re;
#ifdef REF_HARD_LINKED
refexport_t GetRefAPI (refimport_t rimp);
#else
static void *reflib_library; /* Handle to refresh DLL. */
#endif
static qboolean reflib_active = false;


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

DIRECT LINK GLUE

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

#define	MAXPRINTMSG	4096
static void __attribute__((__format__(__printf__,2,3)))
VID_Printf (int print_level, const char *fmt, ...)
{
	va_list		argptr;
	char		msg[MAXPRINTMSG];

	va_start (argptr,fmt);
	vsnprintf (msg,sizeof(msg),fmt,argptr);
	va_end (argptr);

	if (print_level == PRINT_ALL)
		Com_Printf ("%s", msg);
	else
		Com_DPrintf(DEVELOPER_MSG_GFX, "%s", msg);
}

static void __attribute__((__format__(__printf__,2,3)))
VID_Error (int err_level, const char *fmt, ...)
{
	va_list		argptr;
	char		msg[MAXPRINTMSG];

	va_start (argptr,fmt);
	vsnprintf (msg,sizeof(msg),fmt,argptr);
	va_end (argptr);

	Com_Error (err_level, "%s", msg);
}

static void VID_NewWindow (int width, int height)
{
	viddef.width = width;
	viddef.height = height;

	CONSOLE_CheckResize();
#if 0 /* win32 code needed this for what?? possibly videos? */
	if (sound_started)
		CL_Snd_Restart_f();
#endif

	cl.force_refdef = true; /* can't use a paused refdef */
}

int VID_GetModeCount (void) /* FS */
{
	return vid_nummodes;
}

const char *VID_GetModeDescription (int mode) /* FS */
{
	if ((mode < 0) || (mode >= vid_nummodes))
	{
		return " UNKNOWN";
	}

	return vid_modes[mode].menuname;
}

/*
** VID_GetModeInfo
*/
static qboolean VID_GetModeInfo(int *width, int *height, int mode)
{
	if (mode < 0 || mode >= vid_nummodes)
		return false;

	*width  = vid_modes[mode].width;
	*height = vid_modes[mode].height;

	return true;
}

static void VID_FreeReflib (void)
{
#ifndef REF_HARD_LINKED
	if (reflib_library)
		Sys_dlclose(reflib_library);
	reflib_library = NULL;
#endif
	memset (&re, 0, sizeof(re));
	reflib_active  = false;
	vid_nummodes = 0;
	free(vid_modes);
	vid_modes = NULL;
}

static qboolean VID_LoadRefresh (const char *name)
{
	int		i;
	refimport_t	ri;
#ifndef REF_HARD_LINKED
	GetRefAPI_t	GetRefAPI;
#endif

	if (reflib_active)
	{
		re.Shutdown();
		VID_FreeReflib ();
	}

#ifndef REF_HARD_LINKED
	Com_Printf("------- Loading %s -------\n", name);

	if ((reflib_library = Sys_dlopen(name, false)) == NULL)
	{
		Com_Printf("dlopen(\"%s\") failed\n", name);
		return false;
	}
	if ((GetRefAPI = (void *) Sys_dlsym(reflib_library, "_GetRefAPI")) == NULL)
	{
		Com_Error(ERR_FATAL, "dlsym() failed on %s\n", name);
		return false;
	}
#endif

	ri.Cmd_AddCommand = Cmd_AddCommand;
	ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
	ri.Cmd_Argc = Cmd_Argc;
	ri.Cmd_Argv = Cmd_Argv;
	ri.Cmd_ExecuteText = Cbuf_ExecuteText;
	ri.Con_Printf = VID_Printf;
	ri.Sys_Error = VID_Error;
	ri.FS_LoadFile = FS_LoadFile;
	ri.FS_FreeFile = FS_FreeFile;
	ri.FS_Gamedir = FS_Gamedir;
	ri.SCR_CenterPrintf = SCR_CenterPrintf;
	ri.FS_Playerdir = FS_Playerdir;
	ri.Cvar_Get = Cvar_Get;
	ri.Cvar_Set = Cvar_Set;
	ri.Cvar_SetValue = Cvar_SetValue;
	ri.Vid_GetModeInfo = VID_GetModeInfo;
	ri.Vid_MenuInit = VID_MenuInit;
	ri.Vid_NewWindow = VID_NewWindow;
	ri.SINMDL_GetAnim = SINMDL_GetAnim;
	ri.SINMDL_GetBaseModel = SINMDL_GetBaseModel;
	ri.SINMDL_GetDef = SINMDL_GetDef;
	ri.SINMDL_FreeDef = SINMDL_FreeDef;
	ri.SINMDL_RegisterDef = SINMDL_RegisterDef;
	ri.CONSOLE_Process = CONSOLE_Process;

	ri.DebugLines		= &DebugLines;
	ri.numDebugLines	= &numDebugLines;

	ri.Cvar_SetDescription = Cvar_SetDescription; /* FS */

	re = GetRefAPI(ri);

	if (re.api_version != API_VERSION)
	{
		VID_FreeReflib ();
		Com_Error (ERR_FATAL, "%s has incompatible api_version", name);
	}

	/* HACK HACK HACK: retrieving the video modes list into here
	 * using the hInstance and wndProc parameters of re.Init(). */
	if (re.Init (&vid_nummodes, &vid_modes) == -1)
	{
		re.Shutdown();
		VID_FreeReflib ();
		return false;
	}

	reflib_active = true;

	vidref_val = VIDREF_OTHER;
	if(vid_ref)
	{
		if(!strcmp (vid_ref->string, "gl"))
			vidref_val = VIDREF_GL;
		else if(!strcmp(vid_ref->string, "soft"))
			vidref_val = VIDREF_SOFT;
	}

	for (i = 0; i < MAX_VIDEOMODES; ++i)
	{
		resolution_names[i] = (i < vid_nummodes)?
					vid_modes[i].menuname : NULL;
	}
	resolution_names[MAX_VIDEOMODES] = NULL;

	return true;
}

static char fxmesa_env_multitex[32] = "FX_DONT_FAKE_MULTITEX=1";
static char fxglide_env_nosplash[32] = "FX_GLIDE_NO_SPLASH=1";

void	VID_Init (void)
{
	/* putenv() from DJGPP libc will copy, so
	 * it is OK to use an automatic var here. */
	int	i, bpp;

	viddef.width = 320;
	viddef.height = 240;

	vid_ref = Cvar_Get ("vid_ref", "soft", CVAR_ARCHIVE);
	vid_ref->description = "Video renderer to use.";
	vid_fullscreen = Cvar_Get ("vid_fullscreen", "1", CVAR_ARCHIVE);
	vid_fullscreen->description = "Enable fullscreen video.  Locked to fullscreen in SiN.";
	vid_gamma = Cvar_Get("vid_gamma", "1", CVAR_ARCHIVE);
	vid_gamma->description = "Video gamma.  Use lower values for a brighter screen display.";
	scr_shotname = Cvar_Get( "scr_shotname", "sin", CVAR_ARCHIVE );
	vid_checkamd = Cvar_Get( "vid_checkamd", "0", CVAR_ARCHIVE );

	/* cvars for vga/vesa code */
	Cvar_Get("vid_vgaonly", (COM_CheckParm("-vgaonly"))? "1" : "0", 0);
	Cvar_Get("vid_bankedvga", (COM_CheckParm("-bankedvga"))? "1" : "0", 0);
	/* cvars for opengl code */
	Cvar_Get("vid_glbpp", "16", 0);
	i = COM_CheckParm("-bpp");
	if (i && i < com_argc-1) {
		bpp = atoi(com_argv[i+1]);
		switch (bpp) {
		case 15: case 16: case 32:
			Cvar_SetValue("vid_glbpp", bpp);
			break;
		}
	}
	Cvar_Get ("r_ignorehwgamma", "0", CVAR_ARCHIVE);
	/* we only have 3dfx hw accel in dos: */
	if (COM_CheckParm("-no3dfxgamma"))
		Cvar_Set("r_ignorehwgamma", "1");

	/* Add some console commands that we want to handle */
	Cmd_AddCommand("vid_restart", VID_Restart_f);
	Cmd_AddCommand("vid_listmodes", VID_ListModes_f);

	/* don't let fxMesa cheat multitexturing */
	putenv(fxmesa_env_multitex);
	/* disable the 3dfx splash screen. */
	putenv(fxglide_env_nosplash);

	/* Start the graphics mode and load refresh DLL */
	VID_CheckChanges ();

	SCR_EndLoadingPlaque();
}

void	VID_Shutdown (void)
{
	if (reflib_active)
	{
		re.Shutdown ();
		VID_FreeReflib ();
	}
}

void	VID_CheckChanges (void)
{
	char name[100];

	if ( vid_ref->modified)
	{
		cl.force_refdef = true; /* can't use a paused refdef */
		S_StopAllSounds();
		CL_ClearTriangles();
		CL_ClearTempmodels();
	}
	while (vid_ref->modified)
	{
		/*
		** refresh has changed
		*/
		vid_ref->modified = false;
		vid_fullscreen->modified = true;
		cl.refresh_prepped = false;
		S_StopAllSounds();

		Com_sprintf(name, sizeof(name), "ref_%s.dxe", vid_ref->string);
		if (!VID_LoadRefresh(name))
		{
			if (strcmp (vid_ref->string, "soft") == 0)
				Com_Error (ERR_FATAL, "Couldn't fall back to software refresh!");
			Cvar_Set("vid_ref", "soft");

			/*
			** drop the console if we fail to load a refresh
			*/
			if (cls.key_dest != key_console)
			{
				Con_ToggleConsole_f();
			}
		}
	}
}

static void VID_Restart_f (void)
{
	vid_ref->modified = true;
}

static void VID_ListModes_f (void)
{
	int i;

	for(i = 0; i < vid_nummodes; i++)
	{
		if(vid_modes[i].menuname[0] != 0)
			Com_Printf("[Mode %02d] %s\n", i, vid_modes[i].menuname);
	}
	Com_Printf("Available modes: %d\n", vid_nummodes);
}
