/*
   Copyright (C) 1999-2007 id Software, Inc. and contributors.
   For a list of contributors, see the accompanying CONTRIBUTORS file.

   This file is part of GtkRadiant.

   GtkRadiant 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.

   GtkRadiant 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 GtkRadiant; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "qrad.h"

vec3_t texture_reflectivity[MAX_MAP_TEXINFO];

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

   TEXTURE LIGHT VALUES

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

/*
   ======================
   CalcTextureReflectivity_Quake2
   ======================
 */
void CalcTextureReflectivity_Quake2( void ){
	int i;
	int j, k, texels;
	int color[3];
	int texel;
	byte            *palette;
	char path[1024];
	float r, scale;
	miptex_t        *mt;

	sprintf( path, "%spics/colormap.pcx", gamedir );

	// get the game palette
	Load256Image( path, NULL, &palette, NULL, NULL );

	// allways set index 0 even if no textures
	texture_reflectivity[0][0] = 0.5;
	texture_reflectivity[0][1] = 0.5;
	texture_reflectivity[0][2] = 0.5;

	for ( i = 0 ; i < numtexinfo ; i++ )
	{
		// see if an earlier texinfo allready got the value
		for ( j = 0 ; j < i ; j++ )
		{
			if ( !strcmp( texinfo[i].texture, texinfo[j].texture ) ) {
				VectorCopy( texture_reflectivity[j], texture_reflectivity[i] );
				break;
			}
		}
		if ( j != i ) {
			continue;
		}

		// load the wal file
		sprintf( path, "%stextures/%s.wal", gamedir, texinfo[i].texture );
		if ( TryLoadFile( path, (void **)&mt ) == -1 ) {
			Sys_Printf( "Couldn't load %s\n", path );
			texture_reflectivity[i][0] = 0.5;
			texture_reflectivity[i][1] = 0.5;
			texture_reflectivity[i][2] = 0.5;
			continue;
		}
		texels = LittleLong( mt->width ) * LittleLong( mt->height );
		color[0] = color[1] = color[2] = 0;

		for ( j = 0 ; j < texels ; j++ )
		{
			texel = ( (byte *)mt )[LittleLong( mt->offsets[0] ) + j];
			for ( k = 0 ; k < 3 ; k++ )
				color[k] += palette[texel * 3 + k];
		}

		for ( j = 0 ; j < 3 ; j++ )
		{
			r = color[j] / texels / 255.0;
			texture_reflectivity[i][j] = r;
		}
		// scale the reflectivity up, because the textures are
		// so dim
		scale = ColorNormalize( texture_reflectivity[i],
								texture_reflectivity[i] );
		if ( scale < 0.5 ) {
			scale *= 2;
			VectorScale( texture_reflectivity[i], scale, texture_reflectivity[i] );
		}
#if 0
		texture_reflectivity[i][0] = 0.5;
		texture_reflectivity[i][1] = 0.5;
		texture_reflectivity[i][2] = 0.5;
#endif
	}
}

/*
   ======================
   CalcTextureReflectivity_Heretic2
   ======================
 */
void CalcTextureReflectivity_Heretic2( void ){
	int i;
	int j, texels;
	int color[3];
	int texel;
	char path[1024];
	float r;
	miptex_m8_t     *mt;
	miptex_m32_t        *mt32;
	byte            *pos;


	// allways set index 0 even if no textures
	texture_reflectivity[0][0] = 0.5;
	texture_reflectivity[0][1] = 0.5;
	texture_reflectivity[0][2] = 0.5;

	for ( i = 0 ; i < numtexinfo ; i++ )
	{
		// see if an earlier texinfo allready got the value
		for ( j = 0 ; j < i ; j++ )
		{
			if ( !strcmp( texinfo[i].texture, texinfo[j].texture ) ) {
				VectorCopy( texture_reflectivity[j], texture_reflectivity[i] );
				break;
			}
		}
		if ( j != i ) {
			continue;
		}

		// load the wal file

		sprintf( path, "%stextures/%s.m32", gamedir, texinfo[i].texture );
		if ( TryLoadFile( path, (void **)&mt32 ) == -1 ) {
			sprintf( path, "%stextures/%s.m8", gamedir, texinfo[i].texture );
			if ( TryLoadFile( path, (void **)&mt ) == -1 ) {
				Sys_Printf( "Couldn't load %s\n", path );
				texture_reflectivity[i][0] = 0.5;
				texture_reflectivity[i][1] = 0.5;
				texture_reflectivity[i][2] = 0.5;
				continue;
			}
			texels = LittleLong( mt->width[0] ) * LittleLong( mt->height[0] );
			color[0] = color[1] = color[2] = 0;

			for ( j = 0 ; j < texels ; j++ )
			{
				texel = ( (byte *)mt )[LittleLong( mt->offsets[0] ) + j];
				color[0] += mt->palette[texel].r;
				color[1] += mt->palette[texel].g;
				color[2] += mt->palette[texel].b;
			}

			free( mt );
		}
		else
		{
			texels = LittleLong( mt32->width[0] ) * LittleLong( mt32->height[0] );
			color[0] = color[1] = color[2] = 0;

			for ( j = 0 ; j < texels ; j++ )
			{
				pos = (byte *)mt32 + mt32->offsets[0] + ( j << 2 );
				color[0] += *pos++; // r
				color[1] += *pos++; // g
				color[2] += *pos++; // b
			}

			free( mt32 );
		}


		for ( j = 0 ; j < 3 ; j++ )
		{
			r = color[j] / ( (float) texels * 255.0 );
			texture_reflectivity[i][j] = r;
		}
	}
}

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

   MAKE FACES

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

/*
   =============
   WindingFromFace
   =============
 */
winding_t   *WindingFromFace( dface_t *f ){
	int i;
	int se;
	dvertex_t   *dv;
	int v;
	winding_t   *w;

	w = AllocWinding( f->numedges );
	w->numpoints = f->numedges;

	for ( i = 0 ; i < f->numedges ; i++ )
	{
		se = dsurfedges[f->firstedge + i];
		if ( se < 0 ) {
			v = dedges[-se].v[1];
		}
		else{
			v = dedges[se].v[0];
		}

		dv = &dvertexes[v];
		VectorCopy( dv->point, w->p[i] );
	}

	RemoveColinearPoints( w );

	return w;
}

/*
   =============
   BaseLightForFace
   =============
 */
void BaseLightForFace( dface_t *f, vec3_t color ){
	texinfo_t   *tx;

	//
	// check for light emited by texture
	//
	tx = &texinfo[f->texinfo];
	if ( !( tx->flags & SURF_LIGHT ) || tx->value == 0 ) {
		VectorClear( color );
		return;
	}

	VectorScale( texture_reflectivity[f->texinfo], tx->value, color );
}

qboolean IsSky( dface_t *f ){
	texinfo_t   *tx;

	tx = &texinfo[f->texinfo];
	if ( tx->flags & SURF_SKY ) {
		return true;
	}
	return false;
}

/*
   =============
   MakePatchForFace
   =============
 */
float totalarea;
void MakePatchForFace( int fn, winding_t *w ){
	dface_t *f;
	float area;
	patch_t     *patch;
	dplane_t    *pl;
	int i;
	vec3_t color;
	dleaf_t     *leaf;

	f = &dfaces[fn];

	area = WindingArea( w );
	totalarea += area;

	patch = &patches[num_patches];
	if ( num_patches == MAX_PATCHES ) {
		Error( "num_patches == MAX_PATCHES" );
	}
	patch->next = face_patches[fn];
	face_patches[fn] = patch;

	patch->winding = w;

	if ( f->side ) {
		patch->plane = &backplanes[f->planenum];
	}
	else{
		patch->plane = &dplanes[f->planenum];
	}
	if ( face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] ) { // origin offset faces must create new planes
		if ( numplanes + fakeplanes >= MAX_MAP_PLANES ) {
			Error( "numplanes + fakeplanes >= MAX_MAP_PLANES" );
		}
		pl = &dplanes[numplanes + fakeplanes];
		fakeplanes++;

		*pl = *( patch->plane );
		pl->dist += DotProduct( face_offset[fn], pl->normal );
		patch->plane = pl;
	}

	WindingCenter( w, patch->origin );
	VectorAdd( patch->origin, patch->plane->normal, patch->origin );
	leaf = Rad_PointInLeaf( patch->origin );
	patch->cluster = leaf->cluster;
	if ( patch->cluster == -1 ) {
		Sys_FPrintf( SYS_VRB, "patch->cluster == -1\n" );
	}

	patch->area = area;
	if ( patch->area <= 1 ) {
		patch->area = 1;
	}
	patch->sky = IsSky( f );

	VectorCopy( texture_reflectivity[f->texinfo], patch->reflectivity );

	// non-bmodel patches can emit light
	if ( fn < dmodels[0].numfaces ) {
		BaseLightForFace( f, patch->baselight );

		ColorNormalize( patch->reflectivity, color );

		for ( i = 0 ; i < 3 ; i++ )
			patch->baselight[i] *= color[i];

		VectorCopy( patch->baselight, patch->totallight );
	}
	num_patches++;
}


entity_t *EntityForModel( int modnum ){
	int i;
	char    *s;
	char name[16];

	sprintf( name, "*%i", modnum );
	// search the entities for one using modnum
	for ( i = 0 ; i < num_entities ; i++ )
	{
		s = ValueForKey( &entities[i], "model" );
		if ( !strcmp( s, name ) ) {
			return &entities[i];
		}
	}

	return &entities[0];
}

/*
   =============
   MakePatches
   =============
 */
void MakePatches( void ){
	int i, j, k;
	dface_t *f;
	int fn;
	winding_t   *w;
	dmodel_t    *mod;
	vec3_t origin;
	entity_t    *ent;

	Sys_FPrintf( SYS_VRB, "%i faces\n", numfaces );

	for ( i = 0 ; i < nummodels ; i++ )
	{
		mod = &dmodels[i];
		ent = EntityForModel( i );
		// bmodels with origin brushes need to be offset into their
		// in-use position
		GetVectorForKey( ent, "origin", origin );
//VectorCopy (vec3_origin, origin);

		for ( j = 0 ; j < mod->numfaces ; j++ )
		{
			fn = mod->firstface + j;
			face_entity[fn] = ent;
			VectorCopy( origin, face_offset[fn] );
			f = &dfaces[fn];
			w = WindingFromFace( f );
			for ( k = 0 ; k < w->numpoints ; k++ )
			{
				VectorAdd( w->p[k], origin, w->p[k] );
			}
			MakePatchForFace( fn, w );
		}
	}

	Sys_FPrintf( SYS_VRB, "%i sqaure feet\n", (int)( totalarea / 64 ) );
}

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

   SUBDIVIDE

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

void FinishSplit( patch_t *patch, patch_t *newp ){
	dleaf_t     *leaf;

	VectorCopy( patch->baselight, newp->baselight );
	VectorCopy( patch->totallight, newp->totallight );
	VectorCopy( patch->reflectivity, newp->reflectivity );
	newp->plane = patch->plane;
	newp->sky = patch->sky;

	patch->area = WindingArea( patch->winding );
	newp->area = WindingArea( newp->winding );

	if ( patch->area <= 1 ) {
		patch->area = 1;
	}
	if ( newp->area <= 1 ) {
		newp->area = 1;
	}

	WindingCenter( patch->winding, patch->origin );
	VectorAdd( patch->origin, patch->plane->normal, patch->origin );
	leaf = Rad_PointInLeaf( patch->origin );
	patch->cluster = leaf->cluster;
	if ( patch->cluster == -1 ) {
		Sys_FPrintf( SYS_VRB, "patch->cluster == -1\n" );
	}

	WindingCenter( newp->winding, newp->origin );
	VectorAdd( newp->origin, newp->plane->normal, newp->origin );
	leaf = Rad_PointInLeaf( newp->origin );
	newp->cluster = leaf->cluster;
	if ( newp->cluster == -1 ) {
		Sys_FPrintf( SYS_VRB, "patch->cluster == -1\n" );
	}
}

/*
   =============
   SubdividePatch

   Chops the patch only if its local bounds exceed the max size
   =============
 */
void    SubdividePatch( patch_t *patch ){
	winding_t *w, *o1, *o2;
	vec3_t mins, maxs, total;
	vec3_t split;
	vec_t dist;
	int i, j;
	vec_t v;
	patch_t *newp;

	w = patch->winding;
	mins[0] = mins[1] = mins[2] = 99999;
	maxs[0] = maxs[1] = maxs[2] = -99999;
	for ( i = 0 ; i < w->numpoints ; i++ )
	{
		for ( j = 0 ; j < 3 ; j++ )
		{
			v = w->p[i][j];
			if ( v < mins[j] ) {
				mins[j] = v;
			}
			if ( v > maxs[j] ) {
				maxs[j] = v;
			}
		}
	}
	VectorSubtract( maxs, mins, total );
	for ( i = 0 ; i < 3 ; i++ )
		if ( total[i] > ( subdiv + 1 ) ) {
			break;
		}
	if ( i == 3 ) {
		// no splitting needed
		return;
	}

	//
	// split the winding
	//
	VectorCopy( vec3_origin, split );
	split[i] = 1;
	dist = ( mins[i] + maxs[i] ) * 0.5;
	ClipWindingEpsilon( w, split, dist, ON_EPSILON, &o1, &o2 );

	//
	// create a new patch
	//
	if ( num_patches == MAX_PATCHES ) {
		Error( "MAX_PATCHES" );
	}
	newp = &patches[num_patches];
	num_patches++;

	newp->next = patch->next;
	patch->next = newp;

	patch->winding = o1;
	newp->winding = o2;

	FinishSplit( patch, newp );

	SubdividePatch( patch );
	SubdividePatch( newp );
}


/*
   =============
   DicePatch

   Chops the patch by a global grid
   =============
 */
void    DicePatch( patch_t *patch ){
	winding_t *w, *o1, *o2;
	vec3_t mins, maxs;
	vec3_t split;
	vec_t dist;
	int i;
	patch_t *newp;

	w = patch->winding;
	WindingBounds( w, mins, maxs );
	for ( i = 0 ; i < 3 ; i++ )
		if ( floor( ( mins[i] + 1 ) / subdiv ) < floor( ( maxs[i] - 1 ) / subdiv ) ) {
			break;
		}
	if ( i == 3 ) {
		// no splitting needed
		return;
	}

	//
	// split the winding
	//
	VectorCopy( vec3_origin, split );
	split[i] = 1;
	dist = subdiv * ( 1 + floor( ( mins[i] + 1 ) / subdiv ) );
	ClipWindingEpsilon( w, split, dist, ON_EPSILON, &o1, &o2 );

	//
	// create a new patch
	//
	if ( num_patches == MAX_PATCHES ) {
		Error( "MAX_PATCHES" );
	}
	newp = &patches[num_patches];
	num_patches++;

	newp->next = patch->next;
	patch->next = newp;

	patch->winding = o1;
	newp->winding = o2;

	FinishSplit( patch, newp );

	DicePatch( patch );
	DicePatch( newp );
}


/*
   =============
   SubdividePatches
   =============
 */
void SubdividePatches( void ){
	int i, num;

	if ( subdiv < 1 ) {
		return;
	}

	num = num_patches;  // because the list will grow
	for ( i = 0 ; i < num ; i++ )
	{
//		SubdividePatch (&patches[i]);
		DicePatch( &patches[i] );
	}
	Sys_FPrintf( SYS_VRB, "%i patches after subdivision\n", num_patches );
}

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