// cobjfile.cpp
//
// Copyright: (c) 2000 Justin S. Houk, jhouk@mailcity.com.  Permission to use 
// and modify freely given so long as this original copyright notice remains 
// unchanged.

#include <stdio.h>
#include "cobjfile.h"

COBJFile::COBJFile(const char* filename) 
{
	int lPos, rPos;

	//	groups = _curGroups = NULL;
	_fileName = filename;
	lPos = _fileName.rfind('\\') + 1;
	rPos = _fileName.rfind('.');
	_name = _fileName.substr(lPos, rPos - lPos);
};

int COBJFile::GetNumFaces() 
{
	int result = 0;
	MapString2Group::iterator gItr, gEnd;

	// for all the groups...
	gEnd = _groups.end();
	for(gItr = _groups.begin(); gItr != gEnd; gItr++)
	{
		// count the number of faces each contains
		result += gItr->second.numFaces;
	}
	return result;
}

SmoothingGroup* COBJFile::_AlignGroups(int* face)
{
	MapString2Group::iterator gItr;
	MapInt2SmoothingGroup::iterator sgItr;

	// first get to the correct group
	gItr = _groups.begin();
	while ((*face+1) > gItr->second.numFaces)
	{
		*face -= gItr->second.numFaces;
		gItr++;
	}

	// next get the correct smoothing group
	sgItr = gItr->second.smoothingGroups.begin();
	while ((*face+1) > sgItr->second.faces.size())
	{
		*face -= sgItr->second.faces.size();
		sgItr++;
	}
	return &sgItr->second;
}

int COBJFile::GetFaceVertexIndex(int face, int vertex)
{
	SmoothingGroup* sgPtr;

	sgPtr = _AlignGroups(&face);
	return sgPtr->faces[face].arrayIndices[vertex].vertexIndex;
}
int COBJFile::GetFaceTextureVertexIndex(int face, int vertex)
{
	SmoothingGroup* sgPtr;

	sgPtr = _AlignGroups(&face);
	return sgPtr->faces[face].arrayIndices[vertex].vertexTextureIndex;
}
int COBJFile::GetFaceNormalVertexIndex(int face, int vertex)
{
	SmoothingGroup* sgPtr;

	sgPtr = _AlignGroups(&face);
	return sgPtr->faces[face].arrayIndices[vertex].vertexNormalIndex;
}

const Vertex* COBJFile::GetFaceVertex(int face, int vertex)
{
	int index = GetFaceVertexIndex(face, vertex);
	return &_vertices[index];
}
const VertexTexture* COBJFile::GetFaceTextureVertex(int face, int vertex)
{
	int index = GetFaceTextureVertexIndex(face, vertex);
	return &_vertexTextures[index];
}
const VertexNormal* COBJFile::GetFaceNormalVertex(int face, int vertex)
{
	int index = GetFaceNormalVertexIndex(face, vertex);
	return &_vertexNormals[index];
}


bool COBJFile::Load()
{
	// only deals with v, vn, vt, g, s, and f commands right now

	char line[256];
	int id;
	string strLine;
	MapString2GroupPtr::iterator itr, end;
	FILE* inputFile = fopen(_fileName.c_str(), "r");

	// start array indexing at 0
	_curVertex = _curVertexTexture = _curVertexNormal = 0; 

	while(fgets(line, 256, inputFile) != NULL)
	{
		// the first letter of every line tells us what to do with the rest 
		// of the line
		switch (line[0])
		{
			char dummy[10];

		case 'v': // new vertex of some type

			switch (line[1]) // switch on second char to determine which kind
			{
			case ' ': // plain vertex
				Vertex* vertex;
				// get and increment the current vertex
				vertex = &_vertices[_curVertex++]; 
				// initialize the vertex values (important if not all values are in file)
				vertex->x = vertex->y = vertex->z = 0.0f;
				// fill the values with the data in the line
				sscanf(line, "%s %f %f %f", dummy, &(vertex->x), &(vertex->y), &(vertex->z));
				break;
			case 't': // texture vertex
				VertexTexture* vt;
				// get and increment the current texture vertex
				vt = &_vertexTextures[_curVertexTexture++];
				// initialize the texture vertex values
				vt->u = vt->v = vt->w = 0.0f;
				// fill the values with the data in the line
				sscanf(line, "%s %f %f %f", dummy, &(vt->u), &(vt->v), &(vt->w));
				break;
			case 'n': // vertex normal
				VertexNormal* vn;
				// get and increment the current vertex normal
				vn = &_vertexNormals[_curVertexNormal++];
				// initialize the vertex normal values
				vn->i = vn->j = vn->k = 0.0f;
				// fill the values with the data in the line
				sscanf(line, "%s %f %f %f", dummy, &(vn->i), &(vn->j), &(vn->k));
				break;
			}
			break;

		case 'g': // new groups

			int lPos, rPos;

			strLine = line;
			strLine = strLine.substr(0, strLine.length() - 1);

			// reset the curGroups list
			_curGroups.clear();

			// for each word that follows until the end of the line, those are
			// our new current groups
			lPos = strLine.find(" ");
			while (lPos != string::npos)
			{
				string curWord;

				lPos++; // get the lPos past the space

				rPos = strLine.find(" ", lPos);
				if (rPos != string::npos)
					curWord = strLine.substr(lPos, rPos - (lPos));
				else
					curWord = strLine.substr(lPos, strLine.length());

				// ignore any extra spaces
				if (curWord != " ")
				{
					MapString2Group::iterator itr;
					// determine if we have already created this group
					itr = _groups.find(curWord);
					// if we haven't created the group yet, create it
					if (itr == _groups.end())
					{
						Group newGroup;
						newGroup.name = curWord;
						newGroup.curSmoothingGroupKey = -1;
						newGroup.numFaces = 0;

						_groups[curWord] = newGroup;

						itr = _groups.find(curWord);
					}
					_curGroups[curWord] = &(itr->second);
				}
				lPos = strLine.find(" ", lPos);
			}
			break;

		case 's': // a new smoothing group

			// pull in the new smoothing group id
			sscanf(line, "%s %d", dummy, &id);

			// create a default Group if we don't already have one
			if (_curGroups.empty())
				_SetupDefaultGroup();

			// set the curSmoothingGroup for all curGroups to this id, creating
			// the smoothing group if not already created
			end = _curGroups.end();
			for(itr = _curGroups.begin(); itr != end; itr++)
			{
				_SetupSmoothingGroup(itr, id);
			}
			break;

		case 'f': // a new face

			Face newFace;

			// there are two steps for faces: creating the face and adding it
			// to the groups

			//first, create the face
			_SetupFace(line, &newFace);

			// second, add the face to the groups

			// create a default Group if we don't already have one
			if (_curGroups.empty())
				_SetupDefaultGroup();

			// for every group in our current groups...
			end = _curGroups.end();
			for(itr = _curGroups.begin(); itr != end; itr++)
			{
				// make sure they have a smoothing group, or add the 
				// default if not
				if (itr->second->smoothingGroups.empty())
					_SetupSmoothingGroup(itr, 0);

				// Add the face to the smoothing group
				itr->second->smoothingGroups[itr->second->curSmoothingGroupKey].faces.push_back(newFace);

				// increment the group's face count
				itr->second->numFaces++;
			}


			break;
		}
	}

	return true;
}

void COBJFile::_SetupDefaultGroup()
{
	Group newGroup;
	MapString2Group::iterator itr;

	newGroup.name = "default";
	newGroup.curSmoothingGroupKey = -1;
	_groups["default"] = newGroup;

	itr = _groups.find("default");
	_curGroups["default"] = &(itr->second);
}

void COBJFile::_SetupSmoothingGroup(const MapString2GroupPtr::iterator& groupItr,
									int id)
{
	MapInt2SmoothingGroup::iterator sgItr;

	groupItr->second->curSmoothingGroupKey = id;
	sgItr = groupItr->second->smoothingGroups.find(id);
	if (sgItr == groupItr->second->smoothingGroups.end())
	{
		SmoothingGroup sg;
		sg.id = id;

		groupItr->second->smoothingGroups[id] = sg;
	}
}

void COBJFile::_SetupFace(string strLine, Face* newFace)
{
	int lPos, rPos;

	strLine = strLine.substr(0, strLine.length() - 1); // chomp the newline

	// for each word that follows until the end of the line, those are
	// our new array indices for the face
	lPos = strLine.find(" ");
	while (lPos != string::npos)
	{
		string curWord;
		Group newGroup;

		lPos++; // get the lPos past the space

		rPos = strLine.find(" ", lPos);
		if (rPos != string::npos)
			curWord = strLine.substr(lPos, rPos - (lPos));
		else
			curWord = strLine.substr(lPos, strLine.length());

		// ignore any extra spaces
		if (curWord != " ")
		{
			ArrayIndex arrayIndex;
			int vertexIndex, vtIndex, vnIndex;
			int posSlash1, posSlash2;
			posSlash1 = curWord.find("/");
			posSlash2 = curWord.find("/", posSlash1 + 1);
			sscanf(curWord.substr(0, posSlash1).c_str(), "%d", &vertexIndex);
			if (posSlash2 > posSlash1 + 1)
				sscanf(curWord.substr(posSlash1 + 1, posSlash2 - (posSlash1 + 1)).c_str(), "%d", &vtIndex);
			else
				vtIndex = 0; // will get turned to -1 (see below)
			if (curWord.length() > posSlash2 + 1)
				sscanf(curWord.substr(posSlash2 + 1, curWord.length()).c_str(), "%d", &vnIndex);
			else
				vnIndex = 0; // will get turned to -1 (see below)

			// since .obj starts indexes at 1, not 0, take 1 away to compensate
			arrayIndex.vertexIndex = vertexIndex - 1; 
			arrayIndex.vertexNormalIndex = vnIndex - 1; 
			arrayIndex.vertexTextureIndex = vtIndex - 1;

			// Okay, we have our array index.  Now add it to the face.
			newFace->arrayIndices.push_back(arrayIndex);
		}
		lPos = strLine.find(" ", lPos);
	}
}