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

//
// l3dslib.c: library for loading triangles from an Alias triangle file
//

#include <stdio.h>
#include "cmdlib.h"
#include "mathlib.h"
#include "trilib.h"
#include "l3dslib.h"

#define MAIN3DS       0x4D4D
#define EDIT3DS       0x3D3D  // this is the start of the editor config
#define EDIT_OBJECT   0x4000
#define OBJ_TRIMESH   0x4100
#define TRI_VERTEXL   0x4110
#define TRI_FACEL1    0x4120

#define MAXVERTS    2000
#define MAXTRIANGLES    750

typedef struct {
	int v[4];
} tri;

float fverts[MAXVERTS][3];
tri tris[MAXTRIANGLES];

int bytesread, level, numtris, totaltris;
int vertsfound, trisfound;

triangle_t  *ptri;


// Alias stores triangles as 3 explicit vertices in .tri files, so even though we
// start out with a vertex pool and vertex indices for triangles, we have to convert
// to raw, explicit triangles
void StoreAliasTriangles( void ){
	int i, j, k;

	if ( ( totaltris + numtris ) > MAXTRIANGLES ) {
		Error( "Error: Too many triangles" );
	}

	for ( i = 0; i < numtris ; i++ )
	{
		for ( j = 0 ; j < 3 ; j++ )
		{
			for ( k = 0 ; k < 3 ; k++ )
			{
				ptri[i + totaltris].verts[j][k] = fverts[tris[i].v[j]][k];
			}
		}
	}

	totaltris += numtris;
	numtris = 0;
	vertsfound = 0;
	trisfound = 0;
}


int ParseVertexL( FILE *input ){
	int i, j, startbytesread, numverts;
	unsigned short tshort;

	if ( vertsfound ) {
		Error( "Error: Multiple vertex chunks" );
	}

	vertsfound = 1;
	startbytesread = bytesread;

	if ( feof( input ) ) {
		Error( "Error: unexpected end of file" );
	}

	fread( &tshort, sizeof( tshort ), 1, input );
	bytesread += sizeof( tshort );
	numverts = (int)tshort;

	if ( numverts > MAXVERTS ) {
		Error( "Error: Too many vertices" );
	}

	for ( i = 0 ; i < numverts ; i++ )
	{
		for ( j = 0 ; j < 3 ; j++ )
		{
			if ( feof( input ) ) {
				Error( "Error: unexpected end of file" );
			}

			fread( &fverts[i][j], sizeof( float ), 1, input );
			bytesread += sizeof( float );
		}
	}

	if ( vertsfound && trisfound ) {
		StoreAliasTriangles();
	}

	return bytesread - startbytesread;
}


int ParseFaceL1( FILE *input ){

	int i, j, startbytesread;
	unsigned short tshort;

	if ( trisfound ) {
		Error( "Error: Multiple face chunks" );
	}

	trisfound = 1;
	startbytesread = bytesread;

	if ( feof( input ) ) {
		Error( "Error: unexpected end of file" );
	}

	fread( &tshort, sizeof( tshort ), 1, input );
	bytesread += sizeof( tshort );
	numtris = (int)tshort;

	if ( numtris > MAXTRIANGLES ) {
		Error( "Error: Too many triangles" );
	}

	for ( i = 0 ; i < numtris ; i++ )
	{
		for ( j = 0 ; j < 4 ; j++ )
		{
			if ( feof( input ) ) {
				Error( "Error: unexpected end of file" );
			}

			fread( &tshort, sizeof( tshort ), 1, input );
			bytesread += sizeof( tshort );
			tris[i].v[j] = (int)tshort;
		}
	}

	if ( vertsfound && trisfound ) {
		StoreAliasTriangles();
	}

	return bytesread - startbytesread;
}


int ParseChunk( FILE *input ){
#define BLOCK_SIZE  4096
	char temp[BLOCK_SIZE];
	unsigned short type;
	int i, length, w, t, retval;

	level++;
	retval = 0;

// chunk type
	if ( feof( input ) ) {
		Error( "Error: unexpected end of file" );
	}

	fread( &type, sizeof( type ), 1, input );
	bytesread += sizeof( type );

// chunk length
	if ( feof( input ) ) {
		Error( "Error: unexpected end of file" );
	}

	fread( &length, sizeof( length ), 1, input );
	bytesread += sizeof( length );
	w = length - 6;

// process chunk if we care about it, otherwise skip it
	switch ( type )
	{
	case TRI_VERTEXL:
		w -= ParseVertexL( input );
		goto ParseSubchunk;

	case TRI_FACEL1:
		w -= ParseFaceL1( input );
		goto ParseSubchunk;

	case EDIT_OBJECT:
		// read the name
		i = 0;

		do
		{
			if ( feof( input ) ) {
				Error( "Error: unexpected end of file" );
			}

			fread( &temp[i], 1, 1, input );
			i++;
			w--;
			bytesread++;
		} while ( temp[i - 1] );

	case MAIN3DS:
	case OBJ_TRIMESH:
	case EDIT3DS:
		// parse through subchunks
ParseSubchunk:
		while ( w > 0 )
		{
			w -= ParseChunk( input );
		}

		retval = length;
		goto Done;

	default:
		// skip other chunks
		while ( w > 0 )
		{
			t = w;

			if ( t > BLOCK_SIZE ) {
				t = BLOCK_SIZE;
			}

			if ( feof( input ) ) {
				Error( "Error: unexpected end of file" );
			}

			fread( &temp, t, 1, input );
			bytesread += t;

			w -= t;
		}

		retval = length;
		goto Done;
	}

Done:
	level--;
	return retval;
}


void Load3DSTriangleList( char *filename, triangle_t **pptri, int *numtriangles ){
	FILE        *input;
	short int tshort;

	bytesread = 0;
	level = 0;
	numtris = 0;
	totaltris = 0;
	vertsfound = 0;
	trisfound = 0;

	if ( ( input = fopen( filename, "rb" ) ) == 0 ) {
		fprintf( stderr,"reader: could not open file '%s'\n", filename );
		exit( 0 );
	}

	fread( &tshort, sizeof( tshort ), 1, input );

// should only be MAIN3DS, but some files seem to start with EDIT3DS, with
// no MAIN3DS
	if ( ( tshort != MAIN3DS ) && ( tshort != EDIT3DS ) ) {
		fprintf( stderr,"File is not a 3DS file.\n" );
		exit( 0 );
	}

// back to top of file so we can parse the first chunk descriptor
	fseek( input, 0, SEEK_SET );

	ptri = safe_malloc( MAXTRIANGLES * sizeof( triangle_t ) );

	*pptri = ptri;

// parse through looking for the relevant chunk tree (MAIN3DS | EDIT3DS | EDIT_OBJECT |
// OBJ_TRIMESH | {TRI_VERTEXL, TRI_FACEL1}) and skipping other chunks
	ParseChunk( input );

	if ( vertsfound || trisfound ) {
		Error( "Incomplete triangle set" );
	}

	*numtriangles = totaltris;

	fclose( input );
}