/*
===========================================================================
Copyright (C) 1997-2006 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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
===========================================================================
*/

#include "qbsp.h"

int		nummiptex;
textureref_t	textureref[MAX_MAP_TEXTURES];

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


int	FindMiptex (char *name)
{
	int		i, mod_fail;
	char	path[1080];
	char	pakpath[56];
	miptex_t	*mt;

	for (i=0 ; i<nummiptex ; i++)
		if (!strcmp (name, textureref[i].name))
		{
			return i;
		}
	if (nummiptex == MAX_MAP_TEXTURES)
		Error ("MAX_MAP_TEXTURES");
	strcpy (textureref[i].name, name);

    mod_fail = true;

    sprintf (pakpath, "textures/%s.wal", name);

    if(moddir[0] != 0)
        {
	    sprintf (path, "%s%s", moddir, pakpath);
	// load the miptex to get the flags and values
	    if (TryLoadFile (path, (void **)&mt, false) != -1 ||
                TryLoadFileFromPak (pakpath, (void **)&mt, moddir) != -1)
	{
		textureref[i].value = LittleLong (mt->value);
		textureref[i].flags = LittleLong (mt->flags);
		textureref[i].contents = LittleLong (mt->contents);
		strcpy (textureref[i].animname, mt->animname);
		free (mt);
                mod_fail = false;
	}
        }

    if(mod_fail)
        {
	    // load the miptex to get the flags and values
        sprintf (path, "%s%s", gamedir, pakpath);

	    if (TryLoadFile (path, (void **)&mt, false) != -1 ||
                TryLoadFileFromPak (pakpath, (void **)&mt, gamedir) != -1)
	        {
		        textureref[i].value = LittleLong (mt->value);
		        textureref[i].flags = LittleLong (mt->flags);
		        textureref[i].contents = LittleLong (mt->contents);
		        strcpy (textureref[i].animname, mt->animname);
		        free (mt);
	        }
	}
	nummiptex++;

	if (textureref[i].animname[0])
		FindMiptex (textureref[i].animname);

	return i;
}


/*
==================
textureAxisFromPlane
==================
*/
vec3_t	baseaxis[18] =
{
{0,0,1}, {1,0,0}, {0,-1,0},			// floor
{0,0,-1}, {1,0,0}, {0,-1,0},		// ceiling
{1,0,0}, {0,1,0}, {0,0,-1},			// west wall
{-1,0,0}, {0,1,0}, {0,0,-1},		// east wall
{0,1,0}, {1,0,0}, {0,0,-1},			// south wall
{0,-1,0}, {1,0,0}, {0,0,-1}			// north wall
};

void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
{
	int		bestaxis;
	vec_t	dot,best;
	int		i;

	best = 0;
	bestaxis = 0;

	for (i=0 ; i<6 ; i++)
	{
		dot = DotProduct (pln->normal, baseaxis[i*3]);
		if (dot > best)
		{
			best = dot;
			bestaxis = i;
		}
	}

	VectorCopy (baseaxis[bestaxis*3+1], xv);
	VectorCopy (baseaxis[bestaxis*3+2], yv);
}

inline void CheckTexinfoCount() //mxd
{
	if (numtexinfo == DEFAULT_MAP_TEXINFO)
		printf("WARNING: texinfo count exceeds vanilla limit (%i).\n", DEFAULT_MAP_TEXINFO);
	else if (numtexinfo >= MAX_MAP_TEXINFO)
		Error("ERROR: texinfo count exceeds program limit (%i).\n", MAX_MAP_TEXINFO);
}

qboolean TexinfosMatch (texinfo_t t1, texinfo_t t2) //mxd
{
	if (t1.flags != t2.flags || t1.value != t2.value || strcmp (t1.texture, t2.texture))
		return false;

	for (int j = 0; j < 2; j++)
		for (int k = 0; k < 4; k++)
			if (t1.vecs[j][k] != t2.vecs[j][k])
				return false;

	return true;
}

//mxd. Applies origin brush offset to existing v220 texinfo...
int ApplyTexinfoOffset_UV(int texinfoindex, const brush_texture_t *bt, const vec3_t origin)
{
	if ((!origin[0] && !origin[1] && !origin[2]) || texinfoindex < 0)
		return texinfoindex;

	const texinfo_t otx = texinfo[texinfoindex];

	// Copy texinfo
	texinfo_t tx;
	memcpy (&tx, &otx, sizeof(tx));

	// Transform origin to UV space and add it to ST offsets
	tx.vecs[0][3] += origin[0] * tx.vecs[0][0] + origin[1] * tx.vecs[0][1] + origin[2] * tx.vecs[0][2];
	tx.vecs[1][3] += origin[0] * tx.vecs[1][0] + origin[1] * tx.vecs[1][1] + origin[2] * tx.vecs[1][2];

	// Find or replace texinfo
	texinfo_t *tc = texinfo;
	for (texinfoindex = 0; texinfoindex < numtexinfo; texinfoindex++, tc++)
		if (TexinfosMatch (*tc, tx))
			return texinfoindex;

	*tc = tx;
	numtexinfo++;
	CheckTexinfoCount();

	// Repeat for the next animation frame
	const int mt = FindMiptex (tx.texture);
	if (textureref[mt].animname[0])
	{
		brush_texture_t anim = *bt;
		strcpy(anim.name, textureref[mt].animname);
		tc->nexttexinfo = ApplyTexinfoOffset_UV (tc->nexttexinfo, &anim, origin);
	}
	else
	{
		tc->nexttexinfo = -1;
	}

	// Return new texinfo index.
	return texinfoindex;
}

// DarkEssence: function for new #mapversion with UVaxis
int TexinfoForBrushTexture_UV (brush_texture_t *bt, vec_t *UVaxis)
{
	if (!bt->name[0])
		return 0;

	texinfo_t tx;
	memset (&tx, 0, sizeof(tx));
	strcpy (tx.texture, bt->name);

	if (!bt->scale[0])
		bt->scale[0] = 1;
	if (!bt->scale[1])
		bt->scale[1] = 1;

	for (int i = 0; i < 2 ; i++)
		for (int j = 0; j < 3; j++)
			tx.vecs[i][j] = UVaxis[i*3+j] / bt->scale[i];

	tx.vecs[0][3] = bt->shift[0];
	tx.vecs[1][3] = bt->shift[1];
	tx.flags = bt->flags;
	tx.value = bt->value;

	// Find or replace texinfo
	int texinfoindex;
	texinfo_t *tc = texinfo;
	for (texinfoindex = 0; texinfoindex < numtexinfo; texinfoindex++, tc++)
		if (TexinfosMatch (*tc, tx))
			return texinfoindex;

	*tc = tx;
	numtexinfo++;
	CheckTexinfoCount(); //mxd

	// Load the next animation
	const int mt = FindMiptex (bt->name);
	if (textureref[mt].animname[0])
	{
		brush_texture_t anim = *bt;
		strcpy (anim.name, textureref[mt].animname);
		tc->nexttexinfo = TexinfoForBrushTexture_UV (&anim, UVaxis);
	}
	else
		tc->nexttexinfo = -1;

	return texinfoindex;
}

extern qboolean origfix;

int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, vec3_t origin)
{
	if (!bt->name[0])
		return 0;

	texinfo_t tx;
	memset (&tx, 0, sizeof(tx));
	strcpy (tx.texture, bt->name);

	vec3_t vecs[2];
	TextureAxisFromPlane (plane, vecs[0], vecs[1]);

	/* Originally:
	shift[0] = DotProduct (origin, vecs[0]);
	shift[1] = DotProduct (origin, vecs[1]);
	*/

	if (!bt->scale[0])
		bt->scale[0] = 1;
	if (!bt->scale[1])
		bt->scale[1] = 1;

	// DWH: Fix for scaled textures using an origin brush
	float shift[2];
	vec3_t scaled_origin;
	if(origfix)
	{
		VectorScale(origin, 1.0 / bt->scale[0], scaled_origin);
		shift[0] = DotProduct (scaled_origin, vecs[0]);
		VectorScale(origin, 1.0 / bt->scale[1], scaled_origin);
		shift[1] = DotProduct (scaled_origin, vecs[1]);
	}
	else
	{
		shift[0] = DotProduct (origin, vecs[0]);
		shift[1] = DotProduct (origin, vecs[1]);
	}

	// Rotate axis
	vec_t sinv, cosv;
	if (bt->rotate == 0)
		{ sinv = 0 ; cosv = 1; }
	else if (bt->rotate == 90)
		{ sinv = 1 ; cosv = 0; }
	else if (bt->rotate == 180)
		{ sinv = 0 ; cosv = -1; }
	else if (bt->rotate == 270)
		{ sinv = -1 ; cosv = 0; }
	else
	{
		const vec_t ang = bt->rotate / 180 * Q_PI;
		sinv = sin(ang);
		cosv = cos(ang);
	}

	 // DWH: and again...
	if (origfix)
	{
		const vec_t ns = cosv * shift[0] - sinv * shift[1];
		const vec_t nt = sinv * shift[0] + cosv * shift[1];
		shift[0] = ns;
		shift[1] = nt;
	}

	int sv, tv;
	if (vecs[0][0])
		sv = 0;
	else if (vecs[0][1])
		sv = 1;
	else
		sv = 2;

	if (vecs[1][0])
		tv = 0;
	else if (vecs[1][1])
		tv = 1;
	else
		tv = 2;

	for (int i = 0; i < 2; i++)
	{
		const vec_t ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
		const vec_t nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
		vecs[i][sv] = ns;
		vecs[i][tv] = nt;
	}

	for (int i = 0; i < 2; i++)
		for (int j = 0; j < 3; j++)
			tx.vecs[i][j] = vecs[i][j] / bt->scale[i];

	tx.vecs[0][3] = bt->shift[0] + shift[0];
	tx.vecs[1][3] = bt->shift[1] + shift[1];
	tx.flags = bt->flags;
	tx.value = bt->value;

	// Find or replace texinfo
	int texinfoindex;
	texinfo_t *tc = texinfo;
	for (texinfoindex = 0; texinfoindex < numtexinfo; texinfoindex++, tc++)
		if (TexinfosMatch (*tc, tx))
			return texinfoindex;

	*tc = tx;
	numtexinfo++;
	CheckTexinfoCount(); //mxd

	// load the next animation
	const int mt = FindMiptex (bt->name);
	if (textureref[mt].animname[0])
	{
		brush_texture_t anim = *bt;
		strcpy (anim.name, textureref[mt].animname);
		tc->nexttexinfo = TexinfoForBrushTexture (plane, &anim, origin);
	}
	else
		tc->nexttexinfo = -1;

	return texinfoindex;
}
