/****************************************************************************
* MeshLab                                                           o o     *
* An extendible mesh processor                                    o     o   *
*                                                                _   O  _   *
* Copyright(C) 2005, 2006                                          \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* This program 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.                                       *
*                                                                           *
* This program 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 (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/
#include "io_json.h"

#include <string>
#include <fstream>

#include <vcg/complex/algorithms/attribute_seam.h>

#include <QString>
#include <QFile>

JSONIOPlugin::JSONIOPlugin(void) : MeshIOInterface()
{
	;
}

JSONIOPlugin::~JSONIOPlugin(void)
{
	;
}

bool JSONIOPlugin::open(const QString & formatName, const QString & fileName, MeshModel & m, int & mask, const RichParameterSet & parlst, vcg::CallBackPos * cb, QWidget * parent)
{
	(void)formatName;
	(void)fileName;
	(void)m;
	(void)mask;
	(void)parlst;
	(void)cb;
	(void)parent;

	return false;
}

bool JSONIOPlugin::save(const QString & formatName,const QString & fileName, MeshModel & m, const int mask, const RichParameterSet & par, vcg::CallBackPos * cb, QWidget * parent)
{
	(void)par;
	(void)cb;
	(void)parent;
  vcg::tri::Allocator<CMeshO>::CompactVertexVector(m.cm);
  vcg::tri::Allocator<CMeshO>::CompactFaceVector(m.cm);

  const size_t maxValuesPerLine = 10; // must be > 0

	if (formatName.toUpper() != tr("JSON")) return false;

	const bool hasPerVertexPosition = true;
	const bool hasPerVertexNormal   = ((mask & vcg::tri::io::Mask::IOM_VERTNORMAL)   != 0) && m.hasDataMask(MeshModel::MM_VERTNORMAL);
	const bool hasPerVertexColor    = ((mask & vcg::tri::io::Mask::IOM_VERTCOLOR)    != 0) && m.hasDataMask(MeshModel::MM_VERTCOLOR);
	const bool hasPerVertexTexCoord = ((mask & vcg::tri::io::Mask::IOM_VERTTEXCOORD) != 0) && m.hasDataMask(MeshModel::MM_VERTTEXCOORD);

	const CMeshO & cm = m.cm;

	const std::string filename = QFile::encodeName(fileName).constData();

	std::ofstream os(filename.c_str());
	if (!os.is_open()) return false;

	os << "{" << std::endl;

	os << "  \"version\" : \"0.1.0\"," << std::endl;
	os << std::endl;

	os << "  \"comment\" : \"Generated by MeshLab JSON Exporter\"," << std::endl;
	os << std::endl;

	os << "  \"id\"      : 1," << std::endl;
	os << "  \"name\"    : \"mesh\"," << std::endl;
	os << std::endl;

	os << "  \"vertices\" :" << std::endl;
	os << "  [" << std::endl;

	bool prevDone = false;

	if (hasPerVertexPosition)
	{
		os << "    {" << std::endl;
		os << "      \"name\"       : \"position_buffer\"," << std::endl;
		os << "      \"size\"       : 3," << std::endl;
		os << "      \"type\"       : \"float32\"," << std::endl;
		os << "      \"normalized\" : false," << std::endl;
		os << "      \"values\"     :" << std::endl;
		os << "      [" << std::endl;

		size_t it = 0;
		const size_t sz = cm.vert.size();
		while (it < sz)
		{
			const size_t n = std::min(it + maxValuesPerLine, sz);
			if (n > 0)
			{
				os << "        ";
				{
					const CMeshO::VertexType::CoordType & p = cm.vert[it].cP();
					os << p[0] << ", " << p[1] << ", " << p[2];
					it++;
				}
				for (; it<n; ++it)
				{
					const CMeshO::VertexType::CoordType & p = cm.vert[it].cP();
					os << ", " << p[0] << ", " << p[1] << ", " << p[2];
				}
				if (it <= (sz - 1))
				{
					os << ",";
				}
				os << std::endl;
			}
		}

		os << "      ]" << std::endl;
		os << "    }";

		prevDone = true;
	}

	if (hasPerVertexNormal)
	{
		if (prevDone)
		{
			os << "," << std::endl;
			os << std::endl;
		}
		os << "    {" << std::endl;
		os << "      \"name\"       : \"normal_buffer\"," << std::endl;
		os << "      \"size\"       : 3," << std::endl;
		os << "      \"type\"       : \"float32\"," << std::endl;
		os << "      \"normalized\" : false," << std::endl;
		os << "      \"values\"     :" << std::endl;
		os << "      [" << std::endl;

		size_t it = 0;
		const size_t sz = cm.vert.size();
		while (it < sz)
		{
			const size_t n = std::min(it + maxValuesPerLine, sz);
			if (n > 0)
			{
				os << "        ";
				{
					const CMeshO::VertexType::NormalType & n = cm.vert[it].cN();
					os << n[0] << ", " << n[1] << ", " << n[2];
					it++;
				}
				for (; it<n; ++it)
				{
					const CMeshO::VertexType::NormalType & n = cm.vert[it].cN();
					os << ", " << n[0] << ", " << n[1] << ", " << n[2];
				}
				if (it <= (sz - 1))
				{
					os << ",";
				}
				os << std::endl;
			}
		}

		os << "      ]" << std::endl;
		os << "    }";

		prevDone = true;
	}

	if (hasPerVertexColor)
	{
		if (prevDone)
		{
			os << "," << std::endl;
			os << std::endl;
		}
		os << "    {" << std::endl;
		os << "      \"name\"       : \"color_buffer\"," << std::endl;
		os << "      \"size\"       : 4," << std::endl;
		os << "      \"type\"       : \"uint8\"," << std::endl;
		os << "      \"normalized\" : true," << std::endl;
		os << "      \"values\"     :" << std::endl;
		os << "      [" << std::endl;

		size_t it = 0;
		const size_t sz = cm.vert.size();
		while (it < sz)
		{
			const size_t n = std::min(it + maxValuesPerLine, sz);
			if (n > 0)
			{
				os << "        ";
				{
					const CMeshO::VertexType::ColorType & c = cm.vert[it].cC();
					os << int(c[0]) << ", " << int(c[1]) << ", " << int(c[2]) << ", " << int(c[3]);
					it++;
				}
				for (; it<n; ++it)
				{
					const CMeshO::VertexType::ColorType & c = cm.vert[it].cC();
					os << ", " << int(c[0]) << ", " << int(c[1]) << ", " << int(c[2]) << ", " << int(c[3]);
				}
				if (it <= (sz - 1))
				{
					os << ",";
				}
				os << std::endl;
			}
		}

		os << "      ]" << std::endl;
		os << "    }";

		prevDone = true;
	}

	if (hasPerVertexTexCoord)
	{
		if (prevDone)
		{
			os << "," << std::endl;
			os << std::endl;
		}
		os << "    {" << std::endl;
		os << "      \"name\"       : \"texcoord_buffer\"," << std::endl;
		os << "      \"size\"       : 2," << std::endl;
		os << "      \"type\"       : \"float32\"," << std::endl;
		os << "      \"normalized\" : false," << std::endl;
		os << "      \"values\"     :" << std::endl;
		os << "      [" << std::endl;

		size_t it = 0;
		const size_t sz = cm.vert.size();
		while (it < sz)
		{
			const size_t n = std::min(it + maxValuesPerLine, sz);
			if (n > 0)
			{
				os << "        ";
				{
					const CMeshO::VertexType::TexCoordType & t = cm.vert[it].cT();
					os << t.P()[0] << ", " << t.P()[1];
					it++;
				}
				for (; it<n; ++it)
				{
					const CMeshO::VertexType::TexCoordType & t = cm.vert[it].cT();
					os << ", " << t.P()[0] << ", " << t.P()[1];
				}
				if (it <= (sz - 1))
				{
					os << ",";
				}
				os << std::endl;
			}
		}

		os << "      ]" << std::endl;
		os << "    }";

		prevDone = true;
	}

	if (prevDone)
	{
		os << std::endl;
	}

	os << "  ]," << std::endl;
	os << std::endl;

	os << "  \"connectivity\" :" << std::endl;
	os << "  [" << std::endl;

	if ((m.cm.vn > 0) && (m.cm.fn > 0))
	{
		os << "    {" << std::endl;
		os << "      \"name\"      : \"triangles\"," << std::endl;
		os << "      \"mode\"      : \"triangles_list\"," << std::endl;
		os << "      \"indexed\"   : true," << std::endl;
		os << "      \"indexType\" : \"uint32\"," << std::endl;
		os << "      \"indices\"   :" << std::endl;
		os << "      [" << std::endl;
		{
			const CMeshO::VertexType * v0 = &(m.cm.vert[0]);

			size_t k = 0;
			size_t c = 0;
			const size_t sz = cm.fn;
			while (c < sz)
			{
				const size_t n = std::min(c + maxValuesPerLine, sz);
				if (n > 0)
				{
					os << "        ";
					{
						while (cm.face[k].IsD()) k++;
						const CMeshO::FaceType & f = cm.face[k];
						os << int(f.cV(0) - v0) << ", " << int(f.cV(1) - v0) << ", " << int(f.cV(2) - v0);
						c++;
						k++;
					}
					for (; c<n; ++c)
					{
						while (cm.face[k].IsD()) k++;
						const CMeshO::FaceType & f = cm.face[k];
						os << ", " << int(f.cV(0) - v0) << ", " << int(f.cV(1) - v0) << ", " << int(f.cV(2) - v0);
						k++;
					}
					if (c <= (sz - 1))
					{
						os << ",";
					}
					os << std::endl;
				}
			}
		}
		os << "      ]" << std::endl;
		os << "    }" << std::endl;
	}

	os << "  ]," << std::endl;
	os << std::endl;

	os << "  \"mapping\" :" << std::endl;
	os << "  [" << std::endl;
	if ((m.cm.vn > 0) && (m.cm.fn > 0))
	{
		os << "    {" << std::endl;
		os << "      \"name\"       : \"standard\"," << std::endl;
		os << "      \"primitives\" : \"triangles\"," << std::endl;
		os << "      \"attributes\" :" << std::endl;
		os << "      [" << std::endl;

		prevDone = false;
		if (hasPerVertexPosition)
		{
			os << "        {" << std::endl;
			os << "          \"source\"   : \"position_buffer\"," << std::endl;
			os << "          \"semantic\" : \"position\"," << std::endl;
			os << "          \"set\"      : 0" << std::endl;
			os << "        }";
			prevDone = true;
		}
		if (hasPerVertexNormal)
		{
			if (prevDone)
			{
				os << "," << std::endl;
			}
			os << "        {" << std::endl;
			os << "          \"source\"   : \"normal_buffer\"," << std::endl;
			os << "          \"semantic\" : \"normal\"," << std::endl;
			os << "          \"set\"      : 0" << std::endl;
			os << "        }";
			prevDone = true;
		}
		if (hasPerVertexColor)
		{
			if (prevDone)
			{
				os << "," << std::endl;
			}
			os << "        {" << std::endl;
			os << "          \"source\"   : \"color_buffer\"," << std::endl;
			os << "          \"semantic\" : \"color\"," << std::endl;
			os << "          \"set\"      : 0" << std::endl;
			os << "        }";
			prevDone = true;
		}
		if (hasPerVertexTexCoord)
		{
			if (prevDone)
			{
				os << "," << std::endl;
			}
			os << "        {" << std::endl;
			os << "          \"source\"   : \"texcoord_buffer\"," << std::endl;
			os << "          \"semantic\" : \"texcoord\"," << std::endl;
			os << "          \"set\"      : 0" << std::endl;
			os << "        }";
			prevDone = true;
		}

		if (prevDone)
		{
			os << std::endl;
		}

		os << "      ]" << std::endl;
		os << "    }" << std::endl;
	}
	os << "  ]," << std::endl;
	os << std::endl;

	os << "  \"custom\" : null" << std::endl;

	os << "}" << std::endl;

	os.close();

	return true;
}

/*
	returns the list of the file's type which can be imported
*/
QList<MeshIOInterface::Format> JSONIOPlugin::importFormats(void) const
{
	QList<Format> formatList;
	//formatList << Format("JavaScript JSON", tr("JSON"));
	return formatList;
}

/*
	returns the list of the file's type which can be exported
*/
QList<MeshIOInterface::Format> JSONIOPlugin::exportFormats(void) const
{
	QList<Format> formatList;
	formatList << Format("JavaScript JSON", tr("JSON"));
	return formatList;
}

/*
	returns the mask on the basis of the file's type.
	otherwise it returns 0 if the file format is unknown
*/
void JSONIOPlugin::GetExportMaskCapability(QString & format, int & capability, int & defaultBits) const
{
	capability = 0;

	if (format.toUpper() == tr("JSON"))
	{
		// vertex
		capability |= vcg::tri::io::Mask::IOM_VERTNORMAL;
		capability |= vcg::tri::io::Mask::IOM_VERTCOLOR;
		capability |= vcg::tri::io::Mask::IOM_VERTTEXCOORD;

		// face
		//capability |= vcg::tri::io::Mask::IOM_FACECOLOR;
		//capability |= vcg::tri::io::Mask::IOM_FACENORMAL;

		// wedge
		//capability |= vcg::tri::io::Mask::IOM_WEDGTEXCOORD;
		//capability |= vcg::tri::io::Mask::IOM_WEDGNORMAL;
		//capability |= vcg::tri::io::Mask::IOM_WEDGCOLOR;

		defaultBits = capability;
	}
}

MESHLAB_PLUGIN_NAME_EXPORTER(JSONIOPlugin)
