// Copyright (C) 2008-2012 Christian Stehno
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#include "IrrCompileConfig.h"

#ifdef _IRR_COMPILE_WITH_PLY_WRITER_

#include "CPLYMeshWriter.h"
#include "os.h"
#include "IMesh.h"
#include "IMeshBuffer.h"
#include "IWriteFile.h"

namespace irr
{
namespace scene
{

CPLYMeshWriter::CPLYMeshWriter()
{
	#ifdef _DEBUG
	setDebugName("CPLYMeshWriter");
	#endif
}


//! Returns the type of the mesh writer
EMESH_WRITER_TYPE CPLYMeshWriter::getType() const
{
	return EMWT_PLY;
}

//! writes a mesh
bool CPLYMeshWriter::writeMesh(io::IWriteFile* file, scene::IMesh* mesh, s32 flags)
{
	if (!file || !mesh)
		return false;

	os::Printer::log("Writing mesh", file->getFileName());

    // write PLY header
	core::stringc header = "ply\n";

	if (flags & scene::EMWF_WRITE_BINARY)
	{
		#ifdef __BIG_ENDIAN__
		header += "format binary_big_endian 1.0\n";
		#else
		header += "format binary_little_endian 1.0\n";
		#endif
	}
	else
		header += "format ascii 1.0\n";

	header +=  "comment Irrlicht Engine ";
	header +=  IRRLICHT_SDK_VERSION;

	// get vertex and triangle counts
	u32 VertexCount   = 0;
	u32 TriangleCount = 0;

	for (u32 i=0; i < mesh->getMeshBufferCount(); ++i)
	{
		VertexCount   += mesh->getMeshBuffer(i)->getVertexCount();
		TriangleCount += mesh->getMeshBuffer(i)->getIndexCount() / 3;
	}

	// vertex definition
	header += "\nelement vertex ";
	header += VertexCount;

	header += "\n"
		"property float x\n"
		"property float y\n"
		"property float z\n"
		"property float nx\n"
		"property float ny\n"
		"property float nz\n"
		"property float s\n"
		"property float t\n"
		"property uchar red\n"
		"property uchar green\n"
		"property uchar blue\n";
	//	"property float tx\n"
	//	"property float ty\n"
	//	"property float tz\n"

	// face definition

	header += "element face ";
	header += TriangleCount;
	header += "\n"
		"property list uchar int vertex_indices\n"
		"end_header\n";

	// write header
	file->write(header.c_str(), header.size());

	// write vertices

	c8 outLine[1024];

	for (u32 i=0; i < mesh->getMeshBufferCount(); ++i)
	{
		const scene::IMeshBuffer* mb = mesh->getMeshBuffer(i);
		u32 vertexSize = 0;
		switch(mb->getVertexType())
		{
		case video::EVT_STANDARD:
			vertexSize = sizeof(video::S3DVertex);
			break;
		case video::EVT_2TCOORDS:
			vertexSize = sizeof(video::S3DVertex2TCoords);
			break;
		case video::EVT_TANGENTS:
			vertexSize = sizeof(video::S3DVertexTangents);
			break;
		}
		u8 *vertices  = (u8*)mb->getVertices() ;

		for (u32 j=0; j < mb->getVertexCount(); ++j)
		{
        	u8 *buf = vertices + j * vertexSize;
			const video::S3DVertex* vertex = ( (video::S3DVertex*)buf );
			const core::vector3df& pos    = vertex->Pos;
			const core::vector3df& n      = vertex->Normal;
			const core::vector2df& uv     = vertex->TCoords;
			const video::SColor& color    = vertex->Color;

			if (flags & scene::EMWF_WRITE_BINARY)
			{
				// Y and Z are flipped
				file->write(&pos.X, 4);
				file->write(&pos.Z, 4);
				file->write(&pos.Y, 4);

				file->write(&n.X, 4);
				file->write(&n.Z, 4);
				file->write(&n.Y, 4);

				file->write(&uv, 8);

				const u32 r = color.getRed(), g = color.getGreen(), b = color.getBlue();
				file->write(&r, 1);
				file->write(&g, 1);
				file->write(&b, 1);
			}
			else
			{
				// x y z nx ny nz u v red green blue [u1 v1 | tx ty tz]\n
				snprintf_irr(outLine, 1024,
					"%f %f %f %f %f %f %f %f %d %d %d\n",// %u %u %u %u %f %f\n",
					pos.X, pos.Z, pos.Y, // Y and Z are flipped
					n.X, n.Z, n.Y,
					uv.X, uv.Y,
					color.getRed(), color.getGreen(), color.getBlue());

					file->write(outLine, strlen(outLine));
			}
		}
	}

	// index of the first vertex in the current mesh buffer
	u32 StartOffset = 0;

	// write triangles
	const unsigned char nbIndicesParFace = 3;
	for (u32 i=0; i < mesh->getMeshBufferCount(); ++i)
	{
		scene::IMeshBuffer* mb = mesh->getMeshBuffer(i);
		for (u32 j=0; j < mb->getIndexCount(); j+=3)
		{
			// y and z are flipped so triangles are reversed
			u32 a=StartOffset;
			u32 b=StartOffset;
			u32 c=StartOffset;

			switch(mb->getIndexType())
			{
			case video::EIT_16BIT:
				a += mb->getIndices()[j+0];
				c += mb->getIndices()[j+1];
				b += mb->getIndices()[j+2];
				break;
			case video::EIT_32BIT:
				a += ((u32*)mb->getIndices()) [j+0];
				c += ((u32*)mb->getIndices()) [j+1];
				b += ((u32*)mb->getIndices()) [j+2];
				break;
			}

			if (flags & scene::EMWF_WRITE_BINARY)
			{
				file->write(&nbIndicesParFace, 1);
				file->write(&a, 4);
				file->write(&b, 4);
				file->write(&c, 4);
			}
			else
			{
				// count a b c\n
				snprintf_irr(outLine, 1024, "3 %u %u %u\n", a, b, c);
				file->write(outLine, strlen(outLine));
			}
		}

		// increment offset
		StartOffset += mb->getVertexCount();
	}

	// all done!
	return true;
}

} // end namespace
} // end namespace

#endif // _IRR_COMPILE_WITH_PLY_WRITER_