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

*/
#include "cg_local.h"

#define MAX_CGPOLYS				256
#define MAX_CGPOLY_VERTS			128
#define MAX_CGPOLY_FRAGMENTS		64


// poly effects flags
#define	CGPOLY_EF_NOORIENT					0x00000001
#define	CGPOLY_EF_AUTOSPRITE_PITCH			0x00000002
#define	CGPOLY_EF_AUTOSPRITE_YAW			0x00000004 
#define CGPOLY_EF_AUTOSPRITE_ROLL			0x00000008
#define	CGPOLY_EF_DRAWONCE					0x00000010


typedef struct cpoly_s
{
	struct cpoly_s		*prev, *next;

	unsigned int		die;				// remove after this time
	unsigned int		fadetime;
	float				fadefreq;
	qboolean			fadealpha;

	float				color[4];
	struct shader_s		*shader;

	poly_t				*poly;

	//jal
	int					effects;
	vec3_t				verts[MAX_CGPOLY_VERTS];
	vec3_t				origin;
	vec3_t				angles;

} cpoly_t;

static	cpoly_t			cg_polys[MAX_CGPOLYS];
static	cpoly_t			cg_polys_headnode, *cg_free_polys;

static	poly_t			cg_poly_polys[MAX_CGPOLYS];
static	vec3_t			cg_poly_verts[MAX_CGPOLYS][MAX_CGPOLY_VERTS];
static	vec2_t			cg_poly_stcoords[MAX_CGPOLYS][MAX_CGPOLY_VERTS];
static	byte_vec4_t		cg_poly_colors[MAX_CGPOLYS][MAX_CGPOLY_VERTS];

//
//=================
//CG_Clearpolys
//=================
//
void CG_ClearPolys( void )
{
	int i;

	memset( cg_polys, 0, sizeof( cg_polys ) );

	// link polys
	cg_free_polys = cg_polys;
	cg_polys_headnode.prev = &cg_polys_headnode;
	cg_polys_headnode.next = &cg_polys_headnode;
	for( i = 0; i < MAX_CGPOLYS; i++ ) {
		if( i < MAX_CGPOLYS - 1 )
			cg_polys[i].next = &cg_polys[i+1];

		cg_polys[i].poly = &cg_poly_polys[i];
		cg_polys[i].poly->verts = cg_poly_verts[i];
		cg_polys[i].poly->stcoords = cg_poly_stcoords[i];
		cg_polys[i].poly->colors = cg_poly_colors[i];
	}
}

//
//=================
//CG_Allocpoly
//
//Returns either a free poly or the oldest one
//=================
//
cpoly_t *CG_AllocPoly( void )
{
	cpoly_t *pl;

	if ( cg_free_polys ) {	// take a free poly if possible
		pl = cg_free_polys;
		cg_free_polys = pl->next;
	} else {				// grab the oldest one otherwise
		pl = cg_polys_headnode.prev;
		pl->prev->next = pl->next;
		pl->next->prev = pl->prev;
	}

	// put the poly at the start of the list
	pl->prev = &cg_polys_headnode;
	pl->next = cg_polys_headnode.next;
	pl->next->prev = pl;
	pl->prev->next = pl;

	return pl;
}

//
//=================
//CG_FreePoly
//=================
//
void CG_FreePoly( cpoly_t *dl )
{
	// remove from linked active list
	dl->prev->next = dl->next;
	dl->next->prev = dl->prev;

	// insert into linked free list
	dl->next = cg_free_polys;
	cg_free_polys = dl;
}


//
//=================
//CG_SpawnPolygon
//=================
//
cpoly_t *CG_SpawnPolygon( float r, float g, float b, float a,
				 float die, float fadetime, qboolean fadealpha,
				 struct shader_s *shader )
{
	cpoly_t *pl;
	float dietime, fadefreq;

	// clamp and scale colors
	if( r < 0 ) r = 0; else if( r > 1 ) r = 255; else r *= 255;
	if( g < 0 ) g = 0; else if( g > 1 ) g = 255; else g *= 255;
	if( b < 0 ) b = 0; else if( b > 1 ) b = 255; else b *= 255;
	if( a < 0 ) a = 0; else if( a > 1 ) a = 255; else a *= 255;

	dietime = cg.time + die * 1000;
	fadefreq = 0.001f / min( fadetime, die );
	fadetime = cg.time + (die - min( fadetime, die )) * 1000;

	// allocate poly
	pl = CG_AllocPoly ();
	pl->die = dietime;
	pl->fadetime = fadetime;
	pl->fadefreq = fadefreq;
	pl->fadealpha = fadealpha;
	pl->shader = shader;
	pl->color[0] = r; 
	pl->color[1] = g;
	pl->color[2] = b;
	pl->color[3] = a;
	pl->effects = 0;

	if( !die ) {
		pl->die = cg.time + 100;
		pl->effects |= CGPOLY_EF_DRAWONCE;
	}
	
	return pl;
}

/*
  c = (float)cos (DEG2RAD (rotation)) * scale;
   s = (float)sin (DEG2RAD (rotation)) * scale;
 
 
	// Top left
	VectorSet (p->pVerts[0],	org[0] - cg.v_right[0]*c + cg.v_up[0]*s,
									org[1] - cg.v_right[1]*c + cg.v_up[1]*s,
									org[2] - cg.v_right[2]*c + cg.v_up[2]*s);
 
	// Bottom left
	VectorSet (p->pVerts[1],	org[0] + cg.v_right[0]*s + cg.v_up[0]*c,
									org[1] + cg.v_right[1]*s + cg.v_up[1]*c,
									org[2] + cg.v_right[2]*s + cg.v_up[2]*c);
 
	// Bottom right
	VectorSet (p->pVerts[2],	org[0] + cg.v_right[0]*c - cg.v_up[0]*s,
									org[1] + cg.v_right[1]*c - cg.v_up[1]*s,
									org[2] + cg.v_right[2]*c - cg.v_up[2]*s);
 
	// Top right
	VectorSet (p->pVerts[3],	org[0] - cg.v_right[0]*s - cg.v_up[0]*c,
									org[1] - cg.v_right[1]*s - cg.v_up[1]*c,
									org[2] - cg.v_right[2]*s - cg.v_up[2]*c);
*/

//
//=================
//CG_OrientPolygon
//=================
//
void CG_OrientPolygon( vec3_t origin, vec3_t angles, poly_t *poly )
{
	int i, j;
	vec3_t	center;
	vec3_t perp;

	//rotate the polygon around it's default origin (0,0,0)
	VectorClear(center);

	//axis_identity[0] = ROLL
	//axis_identity[1] = PITCH
	//axis_identity[2] = YAW

	//FIXME: needs optimization
	for( i = 0; i < poly->numverts ; i++ )
	{
		VectorSubtract( poly->verts[i], center, perp );
		RotatePointAroundVector( poly->verts[i], axis_identity[0], perp, angles[ROLL] );
		VectorSubtract( poly->verts[i], center, perp );
		RotatePointAroundVector( poly->verts[i], axis_identity[1], perp, angles[PITCH] );
		VectorSubtract( poly->verts[i], center, perp );
		RotatePointAroundVector( poly->verts[i], axis_identity[2], perp, angles[YAW] );
	}

	//move to the desired origin
	for( i = 0; i < poly->numverts ; i++ ) {
		for( j = 0; j < 3; j++ )
			poly->verts[i][j] += origin[j];
	}
}

//=================
//CG_SpawnPolyBeam
// spawns a polygon from start to end points length and given width.
// shaderlenght makes reference to size of the texture it will draw, so it can be repeated.
//=================
cpoly_t *CG_SpawnPolyBeam( vec3_t start, vec3_t end, int width, float dietime, float fadetime, qboolean fadealpha, struct shader_s *shader, int shaderlength )
{
	cpoly_t *cgpoly;
	poly_t	*poly;
	vec3_t	angles, dir;
	int		vcolor, i;
	float	xmin, ymin, xmax, ymax;
	float	stx = 1.0f, sty = 1.0f;

	//find out beam polygon sizes
	VectorSubtract( end, start, dir );
	VecToAngles( dir, angles );
	xmin = 0;
	xmax = VectorNormalize( dir );
	ymin = -(width*0.5);
	ymax = width*0.5;

	if( xmax > shaderlength ) {
		stx = xmax/shaderlength;
	}

	cgpoly = CG_SpawnPolygon( 1.0, 1.0, 1.0, 1.0,
		dietime, fadetime, fadealpha,
		shader );

	VectorCopy( angles, cgpoly->angles );
	VectorCopy( start, cgpoly->origin );
	cgpoly->effects = CGPOLY_EF_AUTOSPRITE_ROLL;
	if( !dietime )
		cgpoly->effects |= CGPOLY_EF_DRAWONCE;

	//create the polygon inside the cgpolygon
	poly = cgpoly->poly;
	poly->shader = cgpoly->shader;
	vcolor = COLOR_RGBA( (int)(cgpoly->color[0] * 255), (int)(cgpoly->color[1] * 255), (int)(cgpoly->color[2] * 255), (int)(cgpoly->color[3] * 255) );
	poly->numverts = 0;

	//A
	VectorSet( poly->verts[poly->numverts], xmin, 0, ymin );
	poly->stcoords[poly->numverts][0] = 0;
	poly->stcoords[poly->numverts][1] = 0;
	*(int *)poly->colors[poly->numverts] = vcolor;
	poly->numverts++;

	//B
	VectorSet( poly->verts[poly->numverts], xmin, 0, ymax );
	poly->stcoords[poly->numverts][0] = 0;
	poly->stcoords[poly->numverts][1] = sty;
	*(int *)poly->colors[poly->numverts] = vcolor;
	poly->numverts++;

	//C
	VectorSet( poly->verts[poly->numverts], xmax, 0, ymax );
	poly->stcoords[poly->numverts][0] = stx;
	poly->stcoords[poly->numverts][1] = sty;
	*(int *)poly->colors[poly->numverts] = vcolor;
	poly->numverts++;

	//D
	VectorSet( poly->verts[poly->numverts], xmax, 0, ymin );
	poly->stcoords[poly->numverts][0] = stx;
	poly->stcoords[poly->numverts][1] = 0;
	*(int *)poly->colors[poly->numverts] = vcolor;
	poly->numverts++;

	//the verts data is stored inside cgpoly, cause it can be moved later
	for( i = 0; i < poly->numverts; i++ )
		VectorCopy( poly->verts[i], cgpoly->verts[i] );

	return cgpoly;
}

//=================
//CG_QuickPolyBeam
//=================
void CG_QuickPolyBeam( vec3_t start, vec3_t end, int width, struct shader_s *shader )
{
	cpoly_t *cgpoly, *cgpoly2;

	cgpoly = CG_SpawnPolyBeam( start, end, width, 0, 0, qfalse, CG_MediaShader( cgs.media.shaderLaserGunBeam ), 64 );
	cgpoly->effects |= CGPOLY_EF_DRAWONCE;

	// since autosprite doesn't work, spawn a second and rotate it 90º
	cgpoly2 = CG_SpawnPolyBeam( start, end, width, 0, 0, qfalse, CG_MediaShader( cgs.media.shaderLaserGunBeam ), 64 );
	cgpoly2->angles[ROLL] += 90;
	cgpoly2->effects |= CGPOLY_EF_DRAWONCE;
}

//=================
//CG_LaserGunPolyBeam
//=================
void CG_LaserGunPolyBeam( vec3_t start, vec3_t end )
{
	cpoly_t *cgpoly, *cgpoly2;

	cgpoly = CG_SpawnPolyBeam( start, end, 16, 0, 0, qfalse, CG_MediaShader( cgs.media.shaderLaserGunBeam ), 64 );
	cgpoly->effects |= CGPOLY_EF_DRAWONCE;

	// since autosprite doesn't work, spawn a second and rotate it 90º
	cgpoly2 = CG_SpawnPolyBeam( start, end, 16, 0, 0, qfalse, CG_MediaShader( cgs.media.shaderLaserGunBeam ), 64 );
	cgpoly2->angles[ROLL] += 90;
	cgpoly2->effects |= CGPOLY_EF_DRAWONCE;
}
void CG_BoltExplosionMode( vec3_t pos, vec3_t dir, int fire_mode );
//=================
//CG_ElectroPolyBeam
//=================
void CG_ElectroPolyBeam( vec3_t start, vec3_t end )
{
	cpoly_t *cgpoly, *cgpoly2;
	vec3_t	dir;
	cgpoly = CG_SpawnPolyBeam( start, end, 16, 0.6f, 0.3f, qtrue, CG_MediaShader( cgs.media.shaderElectroBeam ), 64 );
	
	// since autosprite doesn't work, spawn a second and rotate it 90º
	cgpoly2 = CG_SpawnPolyBeam( start, end, 16, 0.6f, 0.3f, qtrue, CG_MediaShader( cgs.media.shaderElectroBeam ), 64 );
	cgpoly2->angles[ROLL] += 90;

	// effect on end, since trail goes through players, end will always be wall
	VectorSubtract( start, end, dir );
	VectorNormalizeFast( dir );
	CG_BoltExplosionMode( end, dir, FIRE_MODE_STRONG );

}

//=================
//CG_Addpolys
//=================
void CG_AddPolys( void )
{
	int			i;
	float		fade;
	cpoly_t		*cgpoly, *next, *hnode;
	poly_t		*poly;
	static		byte_vec4_t color;
	static		vec3_t		angles;

	// add polys in first-spawed - first-drawn order
	hnode = &cg_polys_headnode;
	for( cgpoly = hnode->prev; cgpoly != hnode; cgpoly = next ) {
		next = cgpoly->prev;

		// it's time to DIE
		if( cgpoly->die <= cg.time ) {
			CG_FreePoly( cgpoly );
			continue;
		}
		poly = cgpoly->poly;

		//jal[start] composite the poly with cgpoly's data
		if( !(cgpoly->effects & CGPOLY_EF_NOORIENT) ) {
			
			for( i=0; i<poly->numverts; i++ )
				VectorCopy( cgpoly->verts[i], poly->verts[i] );

			VectorCopy( cgpoly->angles, angles );
			if( cgpoly->effects & CGPOLY_EF_AUTOSPRITE_PITCH )
			{
			}

			if( cgpoly->effects & CGPOLY_EF_AUTOSPRITE_YAW )
			{
			}

			if( cgpoly->effects & CGPOLY_EF_AUTOSPRITE_ROLL )
			{
			}

			CG_OrientPolygon( cgpoly->origin, angles, poly );
		}
		//jal[end]

		// fade out
		if( cgpoly->fadetime < cg.time ) {
			fade = (cgpoly->die - cg.time) * cgpoly->fadefreq;

			if( cgpoly->fadealpha ) {
				color[0] = ( qbyte )( cgpoly->color[0] );
				color[1] = ( qbyte )( cgpoly->color[1] );
				color[2] = ( qbyte )( cgpoly->color[2] );
				color[3] = ( qbyte )( cgpoly->color[3]*fade );
			} else {
				color[0] = ( qbyte )( cgpoly->color[0]*fade );
				color[1] = ( qbyte )( cgpoly->color[1]*fade );
				color[2] = ( qbyte )( cgpoly->color[2]*fade );
				color[3] = ( qbyte )( cgpoly->color[3] );
			}

			for( i = 0; i < poly->numverts; i++ )
				*( int * )poly->colors[i] = *( int * )color;
		}

		if( cgpoly->effects & CGPOLY_EF_DRAWONCE ) {
			cgpoly->die = cg.time; // will be ignored and freed the next time
		}
		

		trap_R_AddPolyToScene( poly );
	}
}

