//
// Class to load Wavefront's obj files
//
// Author: Alex V. Boreskoff <alexboreskoff@mtu-net.ru>, <steps3d@narod.ru>
//

#include	<assert.h>
#include	<stdio.h>

#include	"ObjLoader.h"
#include	"Mesh.h"
#include	"MeshNode.h"
#include	"MeshUtils.h"
#include	"Data.h"
#include	"Matrix3D.h"
#include	"libTexture.h"
#include	"StringUtils.h"

#include	<map>

#define	DELTA	100

template <typename T>
void	reallocArray ( T *& ptr, int& size, int delta )
{
	T * newPtr = new T [size + delta];

	if ( ptr != NULL )
		memcpy ( newPtr, ptr, sizeof ( T ) * size );

	delete ptr;

	ptr   = newPtr;
	size += delta;
}

MeshNode * ObjLoader :: load ( Data * data )
{
	MeshNode  * out = new MeshNode ( NULL );
	Vector3D	v;
	Vector2D	t;
	int			vi [3];
	int			ti [3];
	string		str, cmd, args;
	bool		res           = true;
	bool		hasTexCoords  = false;
	char		lastCommand   = '\0';
	Vertex    * vertices      = NULL;
	int			numVertices   = 0;
	int			maxVertices   = 0;
	Vector2D  * texCoords     = NULL;
	int			numTexCoords  = 0;
	int			maxTexCoords  = 0;
	Face      * faces         = NULL;
	Face      * texFaces      = NULL;
	int			numFaces      = 0;
	int			maxFaces      = 0;
	int			vertexIndex   = 0;
	int			texIndex      = 0;
	int			faceIndex     = 0;
	int			startVertex   = 1;
	int			startTexCoord = 1;
	int			startFace     = 1;

	map <int, Material>	materials;

	while ( data -> getString ( str, '\r' ) )
	{
		str = trim ( str );

		if ( str.empty () || str [0] == '#' )			// skip empty lines and comments
			continue;

		parseString ( str, cmd, args );

		if ( cmd [0] == 'v' && lastCommand == 'f' )		// check for start of next mesh
		{
			int		 n           = numVertices;
			Vertex * outVertices = fixFaces ( vertices, n, faces, texFaces, numFaces, texCoords, numTexCoords );

			Mesh * mesh = new Mesh ( "", outVertices, n, faces, numFaces, true );

			out -> attach ( "", mesh, Vector3D ( 0, 0, 0 ), Matrix3D ( 1 ) );

			delete outVertices;

			startVertex   += numVertices;
			startTexCoord += numTexCoords;
			startFace     += numFaces;
			numVertices    = 0;
			numTexCoords   = 0;
			numFaces       = 0;
			vertexIndex    = 0;
			texIndex       = 0;
			faceIndex      = 0;
		}

		if ( cmd == "v" )								// get vertex
		{
			sscanf ( args.c_str (), "%f %f %f", &v.x, &v.y, &v.z );

			lastCommand = 'v';

			if ( vertexIndex >= numVertices )
				numVertices ++;

			if ( numVertices >= maxVertices )
				reallocArray ( vertices, maxVertices, DELTA );

			vertices [vertexIndex++].pos = Vector4D ( v, 1 );
		}
		else
		if ( cmd == "vt" )								// get texcoord
		{
			hasTexCoords = true;

			sscanf ( args.c_str (), "%f %f", &t.x, &t.y );

			lastCommand = 'v';

			if ( texIndex >= numTexCoords )
				numTexCoords ++;

			if ( numTexCoords >= maxTexCoords )
				reallocArray ( texCoords, maxTexCoords, DELTA );

			texCoords [texIndex++] = t;
		}
		else
/*		if ( cmd == "vn" )								// get normal
		{
			sscanf ( args.c_str (), "%f %f %f", &v.x, &v.y, &v.z );

			lastCommand = 'v';

			if ( index >= numVertices )
				numVertices ++;

			if ( numVertices >= maxVertices )
				reallocArray ( vertices, maxVertices, DELTA );

			vertices [index++].n = v;
		}
		else
*/		if ( cmd == "f" )
		{
			if ( hasTexCoords )
				sscanf ( args.c_str (), "%d/%d %d/%d %d/%d", &vi [0], &ti [0], &vi [1], &ti [1], &vi [2], &ti [2] );
			else
				sscanf ( args.c_str (), "%d %d %d", &vi [0], &vi [1], &vi [2] );

			lastCommand = 'f';

			if ( faceIndex >= numFaces )
				numFaces ++;

			if ( numFaces >= maxFaces )
			{
				reallocArray ( faces, maxFaces, DELTA );

				if ( hasTexCoords )
				{
					reallocArray ( texFaces, maxFaces, DELTA );

					maxFaces -= DELTA;
				}
			}

			faces    [faceIndex].index [0] = vi [0] - startVertex;
			faces    [faceIndex].index [1] = vi [1] - startVertex;
			faces    [faceIndex].index [2] = vi [2] - startVertex;

			if ( hasTexCoords )
			{
				texFaces [faceIndex].index [0] = ti [0] - startTexCoord;
				texFaces [faceIndex].index [1] = ti [1] - startTexCoord;
				texFaces [faceIndex].index [2] = ti [2] - startTexCoord;
			}

			faceIndex++;
		}
	}

	if ( numVertices > 0 && numFaces > 0  )				// check for last mesh
	{
		int		 n           = numVertices;
		Vertex * outVertices = fixFaces ( vertices, n, faces, texFaces, numFaces, texCoords, numTexCoords );

		Mesh * mesh = new Mesh ( "", outVertices, n, faces, numFaces, true );

		out -> attach ( "", mesh, Vector3D ( 0, 0, 0 ), Matrix3D ( 1 ) );

		delete outVertices;
	}

	delete vertices;
	delete faces;
	delete texFaces;

	return out;
}

