#define PATCHLEVEL 0
/*
**    ScEd: A Constraint Based Scene Editor.
**    Copyright (C) 1994-1995  Stephen Chenney (stephen@cs.su.oz.au)
**
**    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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
**	sced: A Constraint Based Object Scene Editor
**
**	genray.c : export functions for genray.
**
**	External function (there's just one).
**
**	void
**	Export_Genray(FILE *outfile)
**	Exports all the relevant info into outfile.
*/

#include <math.h>
#include <sced.h>
#include <attributes.h>
#include <base_objects.h>
#include <csg.h>
#include <hash.h>
#include <time.h>

static int	Export_Camera(FILE*, Camera);
static int	Export_Light(FILE*, ObjectInstancePtr);
static int	Export_Basetypes(FILE*, BaseObjectPtr);
static int	Export_CSG_Tree(FILE*, CSGNodePtr);
static int	Export_Instances(FILE*, InstanceList);
static void Export_Object_Type(FILE*, ObjectInstancePtr);
static void Export_Transformation(FILE*, ObjectInstancePtr);
static void Export_Attributes(FILE*, AttributePtr);

static HashTable	base_hash;

/*	int
**	Export_Genray(FILE *outfile, ScenePtr scene)
**	Exports all the relevant info into outfile.
*/
int
Export_Genray(FILE *outfile, ScenePtr scene)
{
	time_t	current_time;

	time(&current_time);

	base_hash = Hash_New_Table();

	if ( fprintf(outfile, "/* File generated by sced */\n") < 0 || 
		 fprintf(outfile, "/* %s */\n\n", ctime(&current_time)) < 0 ||
		 Export_Camera(outfile, scene->camera) < 0 ||
		 fprintf(outfile, "ambient %g %g %g\n\n",
				scene->ambient.red, scene->ambient.green,
				scene->ambient.blue) < 0 ||
		 Export_Light(outfile, scene->light) < 0 ||
		 Export_Instances(outfile, scene->instances) < 0 )
	{
		Popup_Error("Write failed!", main_window.shell, "Error");
		Hash_Free(base_hash);
		return 0;
	}

	Hash_Free(base_hash);

	return 1;
}



static int
Export_Camera(FILE *outfile, Camera cam)
{
	if ( ! cam.defined ) return 1;

	/* Genray takes all its camera info at the top of the file. */
	fprintf(outfile, "/* Camera definition */\n");
	fprintf(outfile, "lookfrom ");	VPrint(outfile, cam.location);
	fprintf(outfile, "lookat ");	VPrint(outfile, cam.look_at);
	fprintf(outfile, "lookup ");	VPrint(outfile, cam.look_up);
	fprintf(outfile, "eyedist %g\n", cam.eye_dist);

	fprintf(outfile, "window %g %g\n",
			camera.eye_dist * 2 * tan(camera.horiz_fov * M_PI / 360),
			camera.eye_dist * 2 * tan(camera.vert_fov * M_PI / 360));

	fprintf(outfile, "screen %d %d\n", cam.scr_width, cam.scr_height);

	return fprintf(outfile, "\n");
}


static int
Export_Light(FILE *outfile, ObjectInstancePtr light)
{
	if ( ! light ) return 1;

	fprintf(outfile, "light\n");
	fprintf(outfile, "\tposition ");
	VPrint(outfile, light->o_transform.displacement);
	fprintf(outfile, "\tcolour %g %g %g\n",
		((LightInfoPtr)light->o_attribs)->red,
		((LightInfoPtr)light->o_attribs)->green,
		((LightInfoPtr)light->o_attribs)->blue);
	fprintf(outfile, "end\n");

	return fprintf(outfile, "\n");
}


static void
Export_CSG_Bases(FILE *outfile, CSGNodePtr tree)
{
	if ( ! tree ) return;

	if ( tree->csg_op == csg_leaf_op )
	{
		if ( tree->csg_instance->o_parent->b_class == csg_obj )
			Export_Basetypes(outfile, tree->csg_instance->o_parent);
	}
	else
	{
		Export_CSG_Bases(outfile, tree->csg_left_child);
		Export_CSG_Bases(outfile, tree->csg_right_child);
	}
}


static int
Export_Basetypes(FILE *outfile, BaseObjectPtr base_obj)
{
	if ( Hash_Get_Value(base_hash, (unsigned long)base_obj) != (void*)-1 )
		return 1;

	if ( base_obj->b_class == csg_obj )
	{
		CSGNodePtr	new_tree = CSG_Contract_Tree(base_obj->b_csgptr);

		Export_CSG_Bases(outfile, new_tree);

		fprintf(outfile, "name \"%s\"\n", base_obj->b_label);
		Export_CSG_Tree(outfile, CSG_Contract_Tree(new_tree));

		CSG_Delete_Tree(new_tree, FALSE);

		Hash_Insert(base_hash, (unsigned long)base_obj, (void*)base_obj);
	}

	return fprintf(outfile, "\n");
}


static int
Export_CSG_Tree(FILE *outfile, CSGNodePtr tree)
{
	if ( ! tree ) return 1;

	if ( tree->csg_op == csg_leaf_op )
	{
		fprintf(outfile, "/* %s */\n", tree->csg_instance->o_label);

		if ( tree->csg_instance->o_parent->b_class == wireframe_obj )
			return 1;

		Export_Object_Type(outfile, tree->csg_instance);
		Export_Transformation(outfile, tree->csg_instance);
	}
	else
	{
		switch ( tree->csg_op )
		{
			case csg_union_op: 
				fprintf(outfile, "union\n");
				break;
			case csg_intersection_op: 
				fprintf(outfile, "intersection\n");
				break;
			case csg_difference_op: 
				fprintf(outfile, "difference\n");
				break;
			default:;
		}

		Export_CSG_Tree(outfile, tree->csg_left_child);
		Export_CSG_Tree(outfile, tree->csg_right_child);
	}

	return fprintf(outfile, "end\n");
}

static int
Export_Instances(FILE *outfile, InstanceList insts)
{
	InstanceList		inst_elmt;
	ObjectInstancePtr	inst;

	fprintf(outfile, "/* Instances */\n");

	for ( inst_elmt = insts ; inst_elmt != NULL ; inst_elmt = inst_elmt->next )
	{
		inst = inst_elmt->the_instance;

		if ( inst->o_parent->b_class == spotlight_obj ||
			 inst->o_parent->b_class == arealight_obj ||
			 inst->o_parent->b_class == dirlight_obj ||
			 inst->o_parent->b_class == wireframe_obj ||
			 Obj_Is_Construction(inst) )
			continue;

		if ( inst->o_parent->b_class == light_obj )
			Export_Light(outfile, inst);
		else
		{
			if ( inst->o_parent->b_class == csg_obj )
				Export_Basetypes(outfile, inst->o_parent);

			fprintf(outfile, "/* %s */\n", inst->o_label);

			Export_Object_Type(outfile, inst);
			Export_Transformation(outfile, inst);
			if ( ((AttributePtr)inst->o_attribs)->defined )
				Export_Attributes(outfile, ((AttributePtr)inst->o_attribs));

			fprintf(outfile, "end\n\n");
		}
	}

	return fprintf(outfile, "\n");
}


static void
Export_Object_Type(FILE *outfile, ObjectInstancePtr obj)
{
	switch ( obj->o_parent->b_class )
	{
		case sphere_obj:
			fprintf(outfile, "sphere\n");
			break;
		case cylinder_obj:
			fprintf(outfile, "cylinder\n");
			break;
		case cone_obj:
			fprintf(outfile, "cone\n");
			break;
		case cube_obj:
			fprintf(outfile, "cube\n");
			break;
		case torus_obj:
			fprintf(outfile, "torus\n\tradius %1.15g\n",
					((TorusPtr)obj->o_hook)->major_radius);
			break;
		case plane_obj:
			fprintf(outfile, "plane\n");
			break;
		case triangle_obj:
			/*
			fprintf(outfile, "triangle\n");
			VPrint(outfile, control_part(obj)->control_verts[0]);
			VPrint(outfile, control_part(obj)->control_verts[1]);
			VPrint(outfile, control_part(obj)->control_verts[2]);
			*/
			break;
		case csg_obj:
			fprintf(outfile, "instance \"%s\"\n", obj->o_parent->b_label);
			break;

		case bezier_obj:
		case wireframe_obj:
			/* These can be done with triangles. */
			/* If I ever decide to implement it. */
			break;

		case light_obj:
		case spotlight_obj:
		case arealight_obj:
		case dirlight_obj:;
	}
}


static void
Export_Transformation(FILE *outfile, ObjectInstancePtr inst)
{
	fprintf(outfile, "\ttransform\n");
	fprintf(outfile, "\t");	VPrint(outfile, inst->o_transform.matrix.x);
	fprintf(outfile, "\t");	VPrint(outfile, inst->o_transform.matrix.y);
	fprintf(outfile, "\t");	VPrint(outfile, inst->o_transform.matrix.z);

	/* Write out the displacement. */
	fprintf(outfile, "\ttranslate ");
	VPrint(outfile, inst->o_transform.displacement);
}



static void
Export_Attributes(FILE *outfile, AttributePtr attribs)
{
	fprintf(outfile, "\tcolour %g %g %g\n",
			attribs->colour.red, attribs->colour.green, attribs->colour.blue);
	fprintf(outfile, "\tdiffuse %g\n", attribs->diff_coef);
	fprintf(outfile, "\tspecular %g\n", attribs->spec_coef);
	fprintf(outfile, "\treflect %g\n", attribs->reflect_coef);
	fprintf(outfile, "\ttransparency %g\n", attribs->transparency);
	fprintf(outfile, "\trefract %g\n", attribs->refract_index);
}
