/*
 * $Id: objwriter.cpp,v 1.56 2008-06-25 22:50:29 kybav1 Exp $
 *
 * Description  : One line description of file.
 * Author       : Antonin Misek <antonin.misek@skoda-auto.cz>
 * 
 * Purpose      :
 *     Long description of what the file is for.
 */


#include "module.h"
#include "objwriter.h"
#include "geometrytriangles.h"

using namespace VRUT;


OBJWriter::OBJWriter(wxBufferedOutputStream * _outputStream, Scene * _scene) : outputStream(_outputStream), scene(_scene)
{
}


OBJWriter::~OBJWriter()
{
	///Is called by wxWidgets itself on wxApp end but it doesn't work in dynamic library
}

void OBJWriter::WriteFaceIndice(TextWriter &textWriter, size_t indiceV, size_t indiceT, size_t indiceN, bool writeTex, bool writeNorm)
{
	textWriter.Write(" ")->Write(long(indiceV));
	if (writeTex||writeNorm)
		textWriter.Write("/");
	if (writeTex)
		textWriter.Write(long(indiceT));
	if (writeTex||writeNorm)
		textWriter.Write("/");
	if (writeNorm)
		textWriter.Write(long(indiceN));
}

void OBJWriter::WriteLineIndice(TextWriter &textWriter, size_t indiceV, size_t indiceT, bool writeTex)
{
	textWriter.Write(" ")->Write(long(indiceV));
	if (writeTex)
		textWriter.Write("/")->Write(long(indiceT));
}

void OBJWriter::Write(const NODE_ID rootID)
{
	TextWriter textWriter(outputStream);
	std::vector<smart_pointer<SceneNode> > nodes;
	scene->GetSubNodes(nodes, rootID, true);
	size_t geometryOffsetV=1;
	size_t geometryOffsetT=1;
	size_t geometryOffsetN=1;
	
	textWriter.Write("o ")->Write(scene->GetName().char_str(wxConvISO8859_1))->Write("\r\n");

	for (auto & node : nodes)
	{
		if (!node || !node->IsOfType(SceneNode::GEOMETRY))
			continue;

		smart_pointer<GeometryTriangles> geo;
		scene->GetGeometry(geo, ((GeometryNode*)node.getPtr())->GetGeometryID());
		if (geo && geo->type == Geometry::GEOMETRY_TRIANGLE)
		{
			//workaround for bad output
			GeometryTriangles *geometry=(GeometryTriangles *)geo->Clone();
			std::vector<GeometryTriangles::Index> triInds;
			geometry->Triangulate(triInds);
			geometry->indices.assign(triInds.begin(), triInds.end());
			geometry->triDescList.clear();
			std::pair<GeometryTriangles::Index, GeometryTriangles::PRIMITIVE_TYPE> indType(0, GeometryTriangles::TRI_LIST);
			geometry->triDescList.push_back(indType);
			//end of workaround
			size_t indiceIndex = 0;
			size_t maxInd = geometry->indices.size();
			const std::vector<GeometryTriangles::Index> * indices = &geometry->indices;
			Matrix transf(scene->GetWorldTransMatrix(node->GetID()));
			bool writeTex=(geometry->texCoords.size()==geometry->vertices.size());
			bool writeNorm=(geometry->normals.size()==geometry->vertices.size());

			//write group, perhaps one possible solution for scene structure...
			textWriter.Write("g ")->Write(node->GetName().char_str(wxConvISO8859_1))->Write("\r\n");

			//write vertices
			for (auto &vert : geometry->vertices)
				textWriter.Write("v ")->Write(transf.TransformCoord(vert)._v, 3, " ")->Write("\r\n");

			//write texture coordinates
			if (writeTex)
				for (auto &texCoord : geometry->texCoords)
					textWriter.Write("vt ")->Write(texCoord._v, 2, " ")->Write("\r\n");

			//write normals
			if (writeNorm)
				for (auto &normal : geometry->normals)
					textWriter.Write("vn ")->Write(transf.TransformNormal(normal)._v, 3, " ")->Write("\r\n");

			for (GeometryTriangles::TriDescList::const_iterator indPrimType = geometry->triDescList.begin();
				indPrimType != geometry->triDescList.end(); ++indPrimType)
			{
				GeometryTriangles::TriDescList::const_iterator endIndPrimType = indPrimType + 1;
				size_t endInd = ( endIndPrimType == geometry->triDescList.end() ? maxInd : endIndPrimType->first );
				endInd = __min( endInd, maxInd );

				switch (indPrimType->second)
				{
				case GeometryTriangles::TRI_LIST:
					for ( ;	indiceIndex+2 < endInd; indiceIndex+=3)
					{
						textWriter.Write("f");
						WriteFaceIndice(textWriter, geometryOffsetV+indices->at(indiceIndex), geometryOffsetT+indices->at(indiceIndex), geometryOffsetN+indices->at(indiceIndex), writeTex, writeNorm);
						WriteFaceIndice(textWriter, geometryOffsetV+indices->at(indiceIndex+1), geometryOffsetT+indices->at(indiceIndex+1), geometryOffsetN+indices->at(indiceIndex+1), writeTex, writeNorm);
						WriteFaceIndice(textWriter, geometryOffsetV+indices->at(indiceIndex+2), geometryOffsetT+indices->at(indiceIndex+2), geometryOffsetN+indices->at(indiceIndex+2), writeTex, writeNorm);
						textWriter.Write("\r\n");
					}
					break;
				case GeometryTriangles::POLYGON:
				case GeometryTriangles::TRI_FAN:
					{
						GeometryTriangles::Index last1, last2;
						if (indiceIndex + 1 < endInd)
						{
							last1 = indices->at(indiceIndex++);
							last2 = indices->at(indiceIndex++);
							for ( ;	indiceIndex < endInd; indiceIndex++)
							{
								GeometryTriangles::Index next = indices->at(indiceIndex);
								textWriter.Write("f");
								WriteFaceIndice(textWriter, geometryOffsetV+last1, geometryOffsetT+last1, geometryOffsetN+last1, writeTex, writeNorm);
								WriteFaceIndice(textWriter, geometryOffsetV+last2, geometryOffsetT+last2, geometryOffsetN+last2, writeTex, writeNorm);
								WriteFaceIndice(textWriter, geometryOffsetV+next, geometryOffsetT+next, geometryOffsetN+next, writeTex, writeNorm);
								textWriter.Write("\r\n");
								last2 = next;
							}
						}
						else
							indiceIndex=endInd;
					}
					break;
				case GeometryTriangles::TRI_STRIP:
					{
						GeometryTriangles::Index last1, last2;
						if (indiceIndex + 1 < endInd)
						{
							last1 = indices->at(indiceIndex++);
							last2 = indices->at(indiceIndex++);
							bool sw = true;
							for ( ;	indiceIndex < endInd; sw = !sw)
							{
								textWriter.Write("f");
								GeometryTriangles::Index next = indices->at(indiceIndex++);
								if (sw)
								{
									WriteFaceIndice(textWriter, geometryOffsetV+last1, geometryOffsetT+last1, geometryOffsetN+last1, writeTex, writeNorm);
									WriteFaceIndice(textWriter, geometryOffsetV+next, geometryOffsetT+next, geometryOffsetN+next, writeTex, writeNorm);
									WriteFaceIndice(textWriter, geometryOffsetV+last2, geometryOffsetT+last2, geometryOffsetN+last2, writeTex, writeNorm);
								}
								else
								{
									WriteFaceIndice(textWriter, geometryOffsetV+last1, geometryOffsetT+last1, geometryOffsetN+last1, writeTex, writeNorm);
									WriteFaceIndice(textWriter, geometryOffsetV+last2, geometryOffsetT+last2, geometryOffsetN+last2, writeTex, writeNorm);
									WriteFaceIndice(textWriter, geometryOffsetV+next, geometryOffsetT+next, geometryOffsetN+next, writeTex, writeNorm);
								}
								textWriter.Write("\r\n");
								last1 = last2;
								last2 = next;
							}
						}
						else
							indiceIndex=endInd;
					}
					break;
				case GeometryTriangles::QUADS:
					for ( ;	indiceIndex+3 < endInd; indiceIndex+=4)
					{
						textWriter.Write("f");
						WriteFaceIndice(textWriter, geometryOffsetV+indices->at(indiceIndex), geometryOffsetT+indices->at(indiceIndex), geometryOffsetN+indices->at(indiceIndex), writeTex, writeNorm);
						WriteFaceIndice(textWriter, geometryOffsetV+indices->at(indiceIndex+1), geometryOffsetT+indices->at(indiceIndex+1), geometryOffsetN+indices->at(indiceIndex+1), writeTex, writeNorm);
						WriteFaceIndice(textWriter, geometryOffsetV+indices->at(indiceIndex+2), geometryOffsetT+indices->at(indiceIndex+2), geometryOffsetN+indices->at(indiceIndex+2), writeTex, writeNorm);
						WriteFaceIndice(textWriter, geometryOffsetV+indices->at(indiceIndex+3), geometryOffsetT+indices->at(indiceIndex+3), geometryOffsetN+indices->at(indiceIndex+3), writeTex, writeNorm);
						textWriter.Write("\r\n");
					}
					break;
				case GeometryTriangles::LINES:
					for ( ;	indiceIndex+1 < endInd; indiceIndex+=2)
					{
						textWriter.Write("l");
						WriteLineIndice(textWriter, geometryOffsetV+indices->at(indiceIndex), geometryOffsetT+indices->at(indiceIndex), writeTex);
						WriteLineIndice(textWriter, geometryOffsetV+indices->at(indiceIndex+1), geometryOffsetT+indices->at(indiceIndex+1), writeTex);
						textWriter.Write("\r\n");
					}
					break;
				case GeometryTriangles::LINE_STRIP:
					if (indiceIndex+1 >= endInd)
						break;
					textWriter.Write("l");
					WriteLineIndice(textWriter, geometryOffsetV+indices->at(indiceIndex), geometryOffsetT+indices->at(indiceIndex), writeTex);
					++indiceIndex;
					for ( ;	indiceIndex < endInd; indiceIndex++)
						WriteLineIndice(textWriter, geometryOffsetV+indices->at(indiceIndex), geometryOffsetT+indices->at(indiceIndex), writeTex);
					textWriter.Write("\r\n");
					break;
				case GeometryTriangles::POINTS:
					if (indiceIndex >= endInd)
						break;
					textWriter.Write("p");
					for ( ;	indiceIndex < endInd; indiceIndex++)
						textWriter.Write(" ")->Write(long(geometryOffsetV+indices->at(indiceIndex)));
					textWriter.Write("\r\n");
					break;
				default:
					LOGERROR(wxT("<OBJWriter>Unknown primitive type."));
					break;
				}
			}
			geometryOffsetV+=geometry->vertices.size();
			if (writeTex)
				geometryOffsetT+=geometry->texCoords.size();
			if (writeNorm)
				geometryOffsetN+=geometry->normals.size();
		}
	}
}
