/*
 * Copyright (C) 2003-2004, 2012 Clemens Fuchslocher
 *
 * 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#include <stdio.h>
#include <errno.h>
#include <locale.h>

#include <map>
#include <stack>
#include <cstdlib>
#include <iostream>

#include "Utility.h"
#include "VRMLReader.h"
#include "VRMLReaderInternals.h"

#include "Group.h"
#include "Material.h"
#include "Transform.h"
#include "VertexArray.h"
#include "SceneGraphBuilder.h"


using nsSceneGraph::Group;
using nsSceneGraph::GraphNode;
using nsSceneGraph::Material;
using nsSceneGraph::MATERIAL;
using nsSceneGraph::Transform;
using nsSceneGraph::TRANSFORM;
using nsSceneGraph::VertexArray;
using nsSceneGraph::VERTEXARRAY;
using nsSceneGraph::SceneGraphBuilder;

using nsUtility::PI;
using nsUtility::CStringCompare;

static SceneGraphBuilder *builder = NULL;

extern int VRMLReader_lineno;

namespace nsReader {

VRMLReader::VRMLReader(void)
{
	DEBUG_MESSAGE("VRMLReader::VRMLReader()")
}


VRMLReader::~VRMLReader(void)
{
	DEBUG_MESSAGE("VRMLReader::~VRMLReader()")
}


GraphNode *VRMLReader::readFile(const char *filename)
{
	builder = new SceneGraphBuilder();

	VRMLReader_in = fopen(filename, "r");
	if (VRMLReader_in == NULL)
	{
		std::cerr << __FILE__ << "[" << __LINE__ << "]: " << strerror(errno) << std::endl;
		return NULL;
	}

	VRMLReader_restart(VRMLReader_in);
	VRMLReader_lineno = 0;

	/*
	 * The atof() function, which is used in VRMLReader.lex,
	 * is locale sensitive. So if the locale is set to de_DE
	 * only a comma is recognized as the decimal point. The
	 * problem is that VRML defines the decimal point of a
	 * float as a dot.
	 *
	 * Solution: Setting the locale. (I am not sure if this
	 * is evil. So let me know if it is.)
	 */
	char *locale = strdup(setlocale(LC_NUMERIC, NULL));
	if (setlocale(LC_NUMERIC, "C") == NULL)
	{
		std::cerr << __FILE__ << "[" << __LINE__ << "]: setlocale()" << std::endl;
		return NULL;
	}

	// Invoke YACC/LEX-generated parser
	if (VRMLReader_parse() != VRML_READER_OK)
	{
		std::cerr << __FILE__ << "[" << __LINE__ << "]: VRMLReader_parse() != VRML_READER_OK" << std::endl;
		return NULL;
	}

	if (setlocale(LC_NUMERIC, locale) == NULL)
	{
		std::cerr << __FILE__ << "[" << __LINE__ << "]: setlocale()" << std::endl;
		return NULL;
	}
	free(locale);

	GraphNode *scene = builder->getSceneGraph();
	delete builder;
	return scene;
}


}


// GraphNode
int VRMLReader_GraphNode_setNodeName(char *name)
{
	GraphNode *node = builder->getCurrentNode();
	if (node == NULL)
	{
		std::cerr << __FILE__ << "[" << __LINE__ << "]: VRMLReader_GraphNode_setName()" << std::endl;
		return VRML_READER_KO;
	}

	node->setNodeName(name);

	return VRML_READER_OK;
}


int VRMLReader_GraphNode_addDefReference(char *name)
{
	return builder->addNodeReference(name) != SCENE_GRAPH_BUILDER_OK ? VRML_READER_KO : VRML_READER_OK;
}


int VRMLReader_GraphNode_resolveUseReference(char *name)
{
	return builder->resolveNodeReference(name) != SCENE_GRAPH_BUILDER_OK ? VRML_READER_KO : VRML_READER_OK;
}


// Group
int VRMLReader_Group_new(void)
{
	return builder->addNewNode(new Group()) != SCENE_GRAPH_BUILDER_OK ? VRML_READER_KO : VRML_READER_OK;
}


int VRMLReader_Group_finish(void)
{
	return builder->finishNode() != SCENE_GRAPH_BUILDER_OK ? VRML_READER_KO : VRML_READER_OK;
}


// Transform
int VRMLReader_Transform_new(void)
{
	return builder->addNewNode(new Transform()) != SCENE_GRAPH_BUILDER_OK ? VRML_READER_KO : VRML_READER_OK;
}


int VRMLReader_Transform_finish(void)
{
	return builder->finishNode() != SCENE_GRAPH_BUILDER_OK ? VRML_READER_KO : VRML_READER_OK;
}


int VRMLReader_Transform_setCenter(double x, double y, double z)
{
	NODE_CHECK_ASSERT(TRANSFORM)

	((Transform *) node)->setCenter(x, y, z);

	return VRML_READER_OK;
}


int VRMLReader_Transform_setRotation(double rx, double ry, double rz, double angle)
{
	NODE_CHECK_ASSERT(TRANSFORM)

	((Transform *) node)->setRotation(180 / PI * angle, rx, ry, rz);

	return VRML_READER_OK;
}


int VRMLReader_Transform_setTranslation(double x, double y, double z)
{
	NODE_CHECK_ASSERT(TRANSFORM)

	((Transform *) node)->setTranslation(x, y, z);

	return VRML_READER_OK;
}


// Material
int VRMLReader_Material_new(void)
{
	return builder->addNewNode(new Material()) != SCENE_GRAPH_BUILDER_OK ? VRML_READER_KO : VRML_READER_OK;
}


int VRMLReader_Material_finish(void)
{
	return builder->finishNode() != SCENE_GRAPH_BUILDER_OK ? VRML_READER_KO : VRML_READER_OK;
}


int VRMLReader_Material_setDiffuseColor(double r, double g, double b)
{
	NODE_CHECK_ASSERT(MATERIAL)

	((Material *) node)->setDiffuseColor(r, g, b);

	return VRML_READER_OK;
}


int VRMLReader_Material_setEmissiveColor(double r, double g, double b)
{
	NODE_CHECK_ASSERT(MATERIAL)

	((Material *) node)->setEmissiveColor(r, g, b);

	return VRML_READER_OK;
}


int VRMLReader_Material_setSpecularColor(double r, double g, double b)
{
	NODE_CHECK_ASSERT(MATERIAL)

	((Material *) node)->setSpecularColor(r, g, b);

	return VRML_READER_OK;
}


int VRMLReader_Material_setAmbientIntensity(double intensity)
{
	NODE_CHECK_ASSERT(MATERIAL)

	((Material *) node)->setAmbientColor(intensity, intensity, intensity);

	return VRML_READER_OK;
}


int VRMLReader_Material_setTransparency(double transparency)
{
	NODE_CHECK_ASSERT(MATERIAL)

	((Material *) node)->setTransparency(transparency);

	return VRML_READER_OK;
}


int VRMLReader_Material_setShininess(double shininess)
{
	NODE_CHECK_ASSERT(MATERIAL)

	((Material *) node)->setShininess(shininess > 1 ? shininess : 128 * shininess);

	return VRML_READER_OK;
}


// VertexArray
int VRMLReader_VertexArray_new(void)
{
	return builder->addNewNode(new VertexArray()) != SCENE_GRAPH_BUILDER_OK ? VRML_READER_KO : VRML_READER_OK;
}


int VRMLReader_VertexArray_finish(void)
{
	return builder->finishNode() != SCENE_GRAPH_BUILDER_OK ? VRML_READER_KO : VRML_READER_OK;
}


int VRMLReader_VertexArray_addVertexArrayEntry(double x, double y, double z)
{
	NODE_CHECK_ASSERT(VERTEXARRAY)

	((VertexArray *) node)->addVertexArrayEntry(x, y, z);

	return VRML_READER_OK;
}


int VRMLReader_VertexArray_addVertexIndexEntry(int a, int b, int c)
{
	NODE_CHECK_ASSERT(VERTEXARRAY)

	((VertexArray *) node)->addVertexIndexEntry(a, b, c);

	return VRML_READER_OK;
}


int VRMLReader_VertexArray_addNormalArrayEntry(double x, double y, double z)
{
	NODE_CHECK_ASSERT(VERTEXARRAY)

	((VertexArray *) node)->addNormalArrayEntry(x, y, z);

	return VRML_READER_OK;
}


int VRMLReader_VertexArray_addNormalIndexEntry(int a, int b, int c)
{
	NODE_CHECK_ASSERT(VERTEXARRAY)

	((VertexArray *) node)->addNormalIndexEntry(a, b, c);

	return VRML_READER_OK;
}


int VRMLReader_VertexArray_setNormalPerVertex(int status)
{
	NODE_CHECK_ASSERT(VERTEXARRAY)

	((VertexArray *) node)->setNormalPerVertex(status == 1 ? true : false);

	return VRML_READER_OK;
}

