/* -------------------------------------------------------------------------------

   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

   ----------------------------------------------------------------------------------

   This code has been altered significantly from its original form, to support
   several games based on the Quake III Arena engine, in the form of "Q3Map2."

   ------------------------------------------------------------------------------- */



/* dependencies */
#include "q3map2.h"
#include "surface_extra.h"


/* -------------------------------------------------------------------------------

   ydnar: srf file module

   ------------------------------------------------------------------------------- */


static std::vector<surfaceExtra_t> surfaceExtras;
static surfaceExtra_t seDefault;



/*
   SetDefaultSampleSize()
   sets the default lightmap sample size
 */

void SetDefaultSampleSize( int sampleSize ){
	seDefault.sampleSize = sampleSize;
}

void SetDefaultAmbientColor( const Vector3& color ){
	seDefault.ambientColor = color;
}



/*
   SetSurfaceExtra()
   stores extra (q3map2) data for the specific numbered drawsurface
 */

void SetSurfaceExtra( const mapDrawSurface_t& ds ){
	/* get a new extra */
	surfaceExtra_t& se = surfaceExtras.emplace_back( seDefault );

	/* copy out the relevant bits */
	se.mds = &ds;
	se.si           = ds.shaderInfo;
	se.parentSurfaceNum = ds.parent != nullptr ? ds.parent->outputNum : -1;
	se.entityNum    = ds.entityNum;
	se.castShadows  = ds.castShadows;
	se.recvShadows  = ds.recvShadows;
	se.sampleSize   = ds.sampleSize;
	se.ambientColor = ds.ambientColor;
	se.longestCurve = ds.longestCurve;
	se.lightmapAxis = ds.lightmapAxis;

	/* debug code */
	//%	Sys_FPrintf( SYS_VRB, "SetSurfaceExtra(): entityNum = %d\n", ds.entityNum );
}



/*
   GetSurfaceExtra*()
   getter functions for extra surface data
 */

const surfaceExtra_t& GetSurfaceExtra( int num ){
	if ( num < 0 || size_t( num ) >= surfaceExtras.size() ) {
		return seDefault;
	}
	return surfaceExtras[ num ];
}



/*
   WriteSurfaceExtraFile()
   writes out a surface info file (<map>.srf)
 */

void WriteSurfaceExtraFile( const char *path ){
	/* dummy check */
	if ( strEmptyOrNull( path ) ) {
		return;
	}

	/* note it */
	Sys_Printf( "--- WriteSurfaceExtraFile ---\n" );

	/* open the file */
	const auto srfPath = StringStream( path, ".srf" );
	Sys_Printf( "Writing %s\n", srfPath.c_str() );
	FILE *sf = SafeOpenWrite( srfPath, "wt" );

	/* lap through the extras list */
	for ( int i = -1, size = surfaceExtras.size(); i < size; ++i )
	{
		/* get extra */
		const surfaceExtra_t& se = GetSurfaceExtra( i );

		/* default or surface num? */
		if ( i < 0 ) {
			fprintf( sf, "default" );
		}
		else{
			fprintf( sf, "%d", i );
		}

		/* valid map drawsurf? */
		if ( se.mds == nullptr ) {
			fprintf( sf, "\n" );
		}
		else
		{
			fprintf( sf, " // %s V: %zu I: %zu %s\n",
			         surfaceTypeName( se.mds->type ),
			         se.mds->verts.size(),
			         se.mds->indexes.size(),
			         ( se.mds->planar ? "planar" : "" ) );
		}

		/* open braces */
		fprintf( sf, "{\n" );

		/* shader */
		if ( se.si != nullptr ) {
			fprintf( sf, "\tshader %s\n", se.si->shader.c_str() );
		}

		/* parent surface number */
		if ( se.parentSurfaceNum != seDefault.parentSurfaceNum ) {
			fprintf( sf, "\tparent %d\n", se.parentSurfaceNum );
		}

		/* entity number */
		if ( se.entityNum != seDefault.entityNum ) {
			fprintf( sf, "\tentity %d\n", se.entityNum );
		}

		/* cast shadows */
		if ( se.castShadows != seDefault.castShadows || &se == &seDefault ) {
			fprintf( sf, "\tcastShadows %d\n", se.castShadows );
		}

		/* recv shadows */
		if ( se.recvShadows != seDefault.recvShadows || &se == &seDefault ) {
			fprintf( sf, "\treceiveShadows %d\n", se.recvShadows );
		}

		/* lightmap sample size */
		if ( se.sampleSize != seDefault.sampleSize || &se == &seDefault ) {
			fprintf( sf, "\tsampleSize %d\n", se.sampleSize );
		}

		if ( ( se.ambientColor != g_vector3_identity && se.ambientColor != seDefault.ambientColor ) || &se == &seDefault ) { // 0 == use global
			fprintf( sf, "\tambientColor ( %f %f %f )\n", se.ambientColor[0], se.ambientColor[1], se.ambientColor[2] );
		}

		/* longest curve */
		if ( se.longestCurve != seDefault.longestCurve || &se == &seDefault ) {
			fprintf( sf, "\tlongestCurve %f\n", se.longestCurve );
		}

		/* lightmap axis vector */
		if ( !VectorCompare( se.lightmapAxis, seDefault.lightmapAxis ) ) {
			fprintf( sf, "\tlightmapAxis ( %f %f %f )\n", se.lightmapAxis[ 0 ], se.lightmapAxis[ 1 ], se.lightmapAxis[ 2 ] );
		}

		/* close braces */
		fprintf( sf, "}\n\n" );
	}

	/* close the file */
	fclose( sf );
}



/*
   LoadSurfaceExtraFile()
   reads a surface info file (<map>.srf)
 */

void LoadSurfaceExtraFile( const char *path ){
	/* dummy check */
	if ( strEmptyOrNull( path ) ) {
		return;
	}

	/* load the file */
	const auto srfPath = StringStream( PathExtensionless( path ), ".srf" );

	/* parse the file */
	if( !LoadScriptFile( srfPath, -1 ) )
		Error( "" );

	/* tokenize it */
	while ( GetToken( true ) ) /* test for end of file */
	{
		surfaceExtra_t  *se;
		/* default? */
		if ( striEqual( token, "default" ) ) {
			se = &seDefault;
		}

		/* surface number */
		else
		{
			const int surfaceNum = atoi( token );
			if ( surfaceNum < 0 ) {
				Error( "ReadSurfaceExtraFile(): %s, line %d: bogus surface num %d", srfPath.c_str(), scriptline, surfaceNum );
			}
			if( size_t( surfaceNum ) >= surfaceExtras.size() ){
				if( size_t( surfaceNum ) >= surfaceExtras.capacity() ) // ensure that capacity grows efficiently, as it's not guaranteed for vector::resize()
					surfaceExtras.reserve( surfaceExtras.capacity() << 1 );
				surfaceExtras.resize( surfaceNum + 1, seDefault );
			}
			se = &surfaceExtras[ surfaceNum ];
		}

		/* handle { } section */
		if ( !( GetToken( true ) && strEqual( token, "{" ) ) ) {
			Error( "ReadSurfaceExtraFile(): %s, line %d: { not found", srfPath.c_str(), scriptline );
		}
		while ( GetToken( true ) && !strEqual( token, "}" ) )
		{
			/* shader */
			if ( striEqual( token, "shader" ) ) {
				GetToken( false );
				se->si = &ShaderInfoForShader( token );
			}

			/* parent surface number */
			else if ( striEqual( token, "parent" ) ) {
				GetToken( false );
				se->parentSurfaceNum = atoi( token );
			}

			/* entity number */
			else if ( striEqual( token, "entity" ) ) {
				GetToken( false );
				se->entityNum = atoi( token );
			}

			/* cast shadows */
			else if ( striEqual( token, "castShadows" ) ) {
				GetToken( false );
				se->castShadows = atoi( token );
			}

			/* recv shadows */
			else if ( striEqual( token, "receiveShadows" ) ) {
				GetToken( false );
				se->recvShadows = atoi( token );
			}

			/* lightmap sample size */
			else if ( striEqual( token, "sampleSize" ) ) {
				GetToken( false );
				se->sampleSize = atoi( token );
			}

			else if ( striEqual( token, "ambientColor" ) ) {
				Parse1DMatrix( 3, se->ambientColor.data() );
			}

			/* longest curve */
			else if ( striEqual( token, "longestCurve" ) ) {
				GetToken( false );
				se->longestCurve = atof( token );
			}

			/* lightmap axis vector */
			else if ( striEqual( token, "lightmapAxis" ) ) {
				Parse1DMatrix( 3, se->lightmapAxis.data() );
			}

			/* ignore all other tokens on the line */
			while ( TokenAvailable() )
				GetToken( false );
		}
	}
}
