/****************************************************************************
* 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) : IOPlugin()
{
	;
}

JSONIOPlugin::~JSONIOPlugin(void)
{
	;
}

QString JSONIOPlugin::pluginName() const
{
	return "IOJson";
}

void JSONIOPlugin::open(const QString & formatName, const QString &, MeshModel &, int &, const RichParameterList &, vcg::CallBackPos *)
{
	wrongOpenFormat(formatName);
}

void JSONIOPlugin::save(const QString & formatName,const QString & fileName, MeshModel & m, const int mask, const RichParameterList &, vcg::CallBackPos *)
{
	if (formatName.toUpper() == tr("JSON")){
		vcg::tri::Allocator<CMeshO>::CompactVertexVector(m.cm);
		vcg::tri::Allocator<CMeshO>::CompactFaceVector(m.cm);

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

		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())
			throw MLException("Impossible to open file.");

		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();
	}
	else {
		wrongSaveFormat(formatName);
	}
}

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

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

/*
	returns the mask on the basis of the file's type.
	otherwise it returns 0 if the file format is unknown
*/
void JSONIOPlugin::exportMaskCapability(const 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)
