#include <../source/TAL/objbase.h>
#include <../source/PAL/dataman.h>

/*
 *	Wrappers for OpenGL 1.1 texturing stuff
 */

typedef u8_p (V_CALL *imgload_t)(char *name,u32_t *width,u32_t *height);

V_LOCAL u8_p (V_CALL *load_image[3])(char *,u32_t *,u32_t *)=
{
    tgaLoad,
    jpgLoad,
	bmpLoad
};

/*
 *	Selects appropriate loading function based on:
 *		- file externsions;
 *		- if no match - based on first bytes of file.
 */
int V_LOCAL
imgChooseLoad(char *file_name)
{
	file_t in;
    u16_t magic;
    char ext[_MAX_EXT];
	if(!file_name || !file_name[0])
    	return -1;
    fsSplitPath(file_name,NULL,NULL,ext);
    if(!strcasecmp(ext,"tga"))
    	return 0;
    else if(!strcasecmp(ext,"jpg"))
    	return 1;
    else if(!strcasecmp(ext,"bmp"))
    	return 2;
	/* Unknown extension ! */
	in=fileOpenRead(file_name);
    if(!in)
    	return -1;
	magic=fileGetLittleShort(in);
    fileClose(in);
    switch(magic)
    {
    	case 0x4D42: return 2;
        case 0xD8FF: return 1;
        case 0x0000: return 0; /* TGA: no id, no colormap ? */
	}
    return -1;
}

/*
**	Loads image and binds it to new texture object with given parameters.
*/
GLuint	V_GLOBAL
vglLoadTexture(char *file_name,GLenum filter,GLenum clamp,GLenum env)
{
	u32_t width,height;
	GLuint id;
	u8_t *texels;
	GLenum magf;
	int level, proc;
    proc=imgChooseLoad(file_name);
	if(proc==-1)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"vglLoadTexture('%s'): unable to determine file format.\n",
			dbs(file_name));
#endif	/* CHECKED_BUILD */
		return 0;
	}
	texels=load_image[proc](file_name,&width,&height);
	if(!texels)
		return 0;
	if(!IS_POWER_TWO(width) || !IS_POWER_TWO(height))
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"vglLoadTexture(): wrong texture dimensions %dX%d.\n",
			width,height);
#endif	/* CHECKED_BUILD */
		memFree(texels);
		return 0;
	}
	switch(filter)
	{
		case GL_LINEAR_MIPMAP_NEAREST:
		case GL_LINEAR_MIPMAP_LINEAR:
		case GL_LINEAR:
			magf=GL_LINEAR;
			break;
		case GL_NEAREST_MIPMAP_LINEAR:
		case GL_NEAREST_MIPMAP_NEAREST:
		case GL_NEAREST:
			magf=GL_NEAREST;
			break;
		default:
			magf=filter;
			break;
	}
	/* Generate texture and bind it to target */
	glGenTextures(1,&id);
	glBindTexture(GL_TEXTURE_2D,id);
	/* Download image data to renderer */
	level=0;
	if(magf!=filter)	/* generate all except last mipmap levels */
		for(;width>1 && height>1;level++)
		{
			glTexImage2D(GL_TEXTURE_2D,level,GL_RGBA,width,height,0,GL_RGBA,
				GL_UNSIGNED_BYTE,texels);
			texels=(u8_p)imgDownsample(texels,width,height,false);
			width>>=1;
			height>>=1;
		}
	glTexImage2D(GL_TEXTURE_2D,level,GL_RGBA,width,height,0,GL_RGBA,
		GL_UNSIGNED_BYTE,texels);
	memFree(texels);
	/* Set texture parameters */
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,filter);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,magf);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,clamp);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,clamp);
	glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,env);
	/* Clear current texture object */
	glBindTexture(GL_TEXTURE_2D,0);
	return id;
}

/*
 *	Loads six textures for cube map (QuakeII-style sky)
 */
vboolean	V_GLOBAL
vglLoadSkyBoxTextures(GLuint quake_sky[6],char *path,char *sky_name,char *file_ext)
{
	int i;
	static char suffixes[6][3]={"up","ft","lf","bk","rt","dn"};
	char name[_MAX_FNAME];
	char file_name[_MAX_PATH];
	if(!quake_sky || !sky_name || !sky_name[0]
    	|| !file_ext || !file_ext[0])
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"vglLoadSky(%X,'%s','%s','%s'): incorrect parameter(s).\n",
			quake_sky,dbs(path),dbs(sky_name),dbs(file_ext));
#endif	/* CHECKED_BUILD */
		return false;	
	}
	/* Load images */
	/* TODO: determine presence of HW renderer by ctx->accelerated */
	for(i=0;i<6;i++)
	{
		sprintf(name,"%s%s",sky_name,suffixes[i]);
		fsMakePath(file_name,path,name,file_ext);
		quake_sky[i]=vglLoadTexture(file_name,
        	GL_LINEAR,GL_CLAMP,GL_MODULATE);
	}
	return true;
}

void	V_GLOBAL
vglDrawSkyBox(GLuint quake_sky[6],vec_t sky_intensity,vec_t sky_opacity)
{
	int i;
	GLfloat	mat[16];
	GLfloat	cube_vertices[8][3]={
		{	1,	1,	1},
		{	-1,	1,	1},
		{	-1,	-1,	1},
		{	1,	-1,	1},
		{	1,	1,	-1},
		{	-1,	1,	-1},
		{	-1,	-1,	-1},
		{	1,	-1,	-1}};
	int		cube_faces[6][4]={
		{	0,	1,	2,	3	},		/* up */
		{	0,	3,	7,	4	},		/* forward */
		{	1,	0,	4,	5	},		/* left */
		{	2,	1,	5,	6	},		/* back */
		{	3,	2,	6,	7	},		/* right */
		{	4,	7,	6,	5	}};		/* down */
	GLfloat	cube_bottom[4][2]=
	{
		{1,0},
		{1,1},
		{0,1},
		{0,0}
	};
	GLfloat	cube_top[4][2]=
	{
		{1,1},
		{0,1},
		{0,0},
		{1,0}
	};
	GLfloat	cube_wall[4][2]=
	{
		{0,0},
		{1,0},
		{1,1},
		{0,1}
	};
	/* setup state */
	glDisable(GL_CULL_FACE);
	glEnable(GL_TEXTURE_2D);
	/*
     *	Move cube to the origin of the world
     *	(reset current translation & scale transform)
     */
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glGetFloatv(GL_MODELVIEW_MATRIX,mat);
	mat[3]=mat[7]=mat[11]=mat[12]=mat[13]=mat[14]=0;
	mat[15]=1;
	glLoadMatrixf(mat);
    /* Make cube large enought */
	glScalef(10,10,10);
	/* Draw textured cube */
	glColor4f(sky_intensity,sky_intensity,sky_intensity,sky_opacity);
	if(IsZero(sky_opacity-1))
	{
		glDisable(GL_BLEND);
	}
	else
	{
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	}
	/* top */
	glBindTexture(GL_TEXTURE_2D,quake_sky[0]);
	glBegin(GL_QUADS);
	for(i=0;i<4;i++)
	{
		glTexCoord2fv(cube_top[i]);
		glVertex3fv(cube_vertices[cube_faces[0][i]]);
	}
	glEnd();
	/* forward */
	glBindTexture(GL_TEXTURE_2D,quake_sky[1]);
	glBegin(GL_QUADS);
	for(i=0;i<4;i++)
	{
		glTexCoord2fv(cube_wall[i]);
		glVertex3fv(cube_vertices[cube_faces[1][i]]);
	}
	glEnd();
	/* left */
	glBindTexture(GL_TEXTURE_2D,quake_sky[4]);
	glBegin(GL_QUADS);
	for(i=0;i<4;i++)
	{
		glTexCoord2fv(cube_wall[i]);
		glVertex3fv(cube_vertices[cube_faces[2][i]]);
	}
	glEnd();
	/* back */
	glBindTexture(GL_TEXTURE_2D,quake_sky[3]);
	glBegin(GL_QUADS);
	for(i=0;i<4;i++)
	{
		glTexCoord2fv(cube_wall[i]);;
		glVertex3fv(cube_vertices[cube_faces[3][i]]);
	}
	glEnd();
	/* right */
	glBindTexture(GL_TEXTURE_2D,quake_sky[2]);
	glBegin(GL_QUADS);
	for(i=0;i<4;i++)
	{
		glTexCoord2fv(cube_wall[i]);;
		glVertex3fv(cube_vertices[cube_faces[4][i]]);
	}
	glEnd();
	/* bottom */
	glBindTexture(GL_TEXTURE_2D,quake_sky[5]);
	glBegin(GL_QUADS);
	for(i=0;i<4;i++)
	{
		glTexCoord2fv(cube_bottom[i]);
		glVertex3fv(cube_vertices[cube_faces[5][i]]);
	}
	glEnd();
	glPopMatrix();
	glDisable(GL_TEXTURE_2D);
}
