/*************************************************************************

	form*Z by auto*des*sys, Inc.
	Copyright (c) 1989-2004 auto*des*sys, Inc.  All rights reserved.

	File:			rainbow.c
	Author:			auto*des*sys, Inc.
	Created on:		02/23/04
	Description:	Rainbow project utility plugin.
					Changes color of all objects in scene to plain-colored
					objects in the spectrum of a rainbow according to
					their locations.

*************************************************************************/

/* this plugin's header */

#include "rainbow.h"

/* required for plugin's source file implementing plugin entry function */

#include "fz_plugin_glue_api.h"

/* function called by formZ to fill a utility's function set */
/* with pointers to rainbow's own callback functions. */
static fzrt_error_td rainbow_fill_fset(const fzpl_fset_def_ptr fset_def, fzpl_fset_td* const fset);


/************************************************************************
						Rainbow Data Structures
*************************************************************************/

/************************************************************************
	bbox_td

	Description
		data structure type that represents an axis-aligned bounding box.
		
	Members
		min_xyz - the minimum xyz coordinate of the axis-aligned
				  bounding box.
		max_xyz - the maximum xyz coordinate of the axis-aligned
				  bounding box.
*************************************************************************/
typedef struct
{	fz_xyz_td min_xyz;
	fz_xyz_td max_xyz;
} bbox_td;

/************************************************************************
	obj_td

	Description
		data structure type that represents an object.
		
	Members
		ref	 - memory location of objt_ptr
		bbox - bounding box of the entire object
*************************************************************************/
typedef struct
{	fz_objt_ptr	ref;
	bbox_td		bbox;
} rainbow_obj_td;

/* temporary array of structures representing objects */
rainbow_obj_td* objs;

/************************************************************************
							Rainbow Callbacks
*************************************************************************/

/************************************************************************
	rainbow_uuid
		Tells formZ what this plugin's uuid is.
*************************************************************************/
static fzrt_error_td rainbow_uuid(fzrt_UUID_td uuid)
/************************************************************************/
{
	_base_funcs.fzrt_UUID_copy(RAINBOW_PLUGIN_UUID, uuid);
	return(FZRT_NOERR);
}

/************************************************************************
	rainbow_name
		Tells formZ what the display name/title of this plugin is.
*************************************************************************/
static fzrt_error_td rainbow_name(char* name, fzrt_int max_len)
/************************************************************************/
{
	strncpy(name, _short_name, max_len);
	return(FZRT_NOERR);
}

/************************************************************************
	rainbow_main
		Executes upon running utility.
*************************************************************************/
static fzrt_error_td rainbow_main(fzrt_int windex)
/************************************************************************/
{
	fzrt_error_td err = FZRT_NOERR;
	/* object tracing */
	fzrt_int	obj_count = -1;
	fz_objt_ptr	obj_next;
	fzrt_int	obj_iter;
	/* object sorting */
	fzrt_int 	i, j;
	rainbow_obj_td	tmp_obj;
	double	min_x, max_x;
	double	norm, factor;
	double	scale_color;
	/* surface sytles */
	fz_rmtl_ptr			rmtl;
	fz_type_td			data;
	fz_rgb_float_td		color;
	fzrt_hsv_color_td	hsv_color;
	fzrt_rgb_color_td	rgb_color;
	fz_tag_td			rmtl_tag;
	fz_shdr_ptr			shdr;


	/* iterate through all objects, sort by x, y, z, and reassign object colors */

	_model_funcs.fz_objt_get_count(windex, &obj_count);
	num_objs = obj_count;

	if (obj_count > 0)
	{
		/* allocate memory for objects */
		err = _mem_funcs.fz_mem_zone_alloc(NULL, obj_count*sizeof(rainbow_obj_td), TRUE, (fzrt_ptr*)&objs);

		if (err == FZRT_NOERR)
		{
			obj_next = NULL;
				
			/* get the first object on the list */
			_model_funcs.fz_objt_get_next(windex, NULL, &obj_next);
			/* we've found the first object. */
		
			obj_iter = 0;
			while (obj_next != NULL && obj_iter < obj_count) /* quit when obj_next is NULL (end of object list) */
			{
				/* for each object, get its bbox. */
				_model_funcs.fz_objt_alys_get_objt_bbox(windex, obj_next, &objs[obj_iter].bbox.min_xyz, &objs[obj_iter].bbox.max_xyz);
				
				/* for each object, set its reference */
				objs[obj_iter].ref = (fzrt_int)obj_next;

				_model_funcs.fz_objt_get_next(windex, obj_next, &obj_next);
				
				obj_iter++;
			}
			
			/* find global max/min x values. */
			min_x = objs[0].bbox.min_xyz.x;
			max_x = objs[0].bbox.min_xyz.x;

			for (i = 1; i < num_objs; i++)
			{
				if (objs[i].bbox.min_xyz.x < min_x)
				{
					min_x = objs[i].bbox.min_xyz.x;
				}
				if (objs[i].bbox.min_xyz.x > max_x)
				{
					max_x = objs[i].bbox.min_xyz.x;
				}
			}
			
			/* sort objects by x, y, z. */
			
			/* iterate through all objects */
			for (i = 0; i < num_objs-1; i++)
			{
				/* for each object, test if another object has a smaller */
				/* x value, if so, swap */
				for (j = i+1; j < num_objs; j++)
				{
					if (objs[i].bbox.min_xyz.x > objs[j].bbox.min_xyz.x)
					{
						/* swap */
						tmp_obj.ref = objs[i].ref;
						tmp_obj.bbox = objs[i].bbox;
						objs[i].ref = objs[j].ref;
						objs[i].bbox = objs[j].bbox;
						objs[j].ref = tmp_obj.ref;
						objs[j].bbox = tmp_obj.bbox;
					}
					/* else don't swap */
				}
				/* go on to next object to test */
			}
			/* done swapping all objects */
			
			/* now, go through objects, and assign colors */
			norm = 65535.0 / (max_x - min_x);

			hsv_color.hue = 0; /* start at "red" */
			hsv_color.saturation = 65535;
			hsv_color.value = 65535;
			
			scale_color = 1.0 / 65535;
			
			for (i = 0; i < num_objs; i++)
			{
				obj_next = (fz_objt_ptr)objs[i].ref;

				/* color on color wheel */
				factor = norm*(objs[i].bbox.min_xyz.x - min_x);
				hsv_color.hue = (unsigned short)(factor);
				/* convert to rgb */
				_graf_funcs.fzrt_hsv_2_rgb(&hsv_color, &rgb_color);
				color.red   = (float)(rgb_color.red   * scale_color);
				color.green = (float)(rgb_color.green * scale_color);
				color.blue  = (float)(rgb_color.blue  * scale_color);
				_type_funcs.fz_type_set_rgb_float(&color, &data);
				
				_rmtl_funcs.fz_rmtl_find_by_rgb(windex, &color, &rmtl);
				
				if (rmtl == NULL)
				{
					/* create new surface style with color */
					_rmtl_funcs.fz_rmtl_create_new(windex, &rmtl);
					_rmtl_funcs.fz_rmtl_get_parm(windex, rmtl, FZ_RMTL_PARM_COL_SHADER, &data);
					_type_funcs.fz_type_get_ptr(&data, (void**)&shdr);
					_rmtl_funcs.fz_shdr_ptr_set_shdr_type(windex, shdr, FZ_SHDR_COLOR_PLAIN_UUID);
					_rmtl_funcs.fz_rmtl_set_generic_parm(windex,rmtl,&color,NULL,NULL,1.0,0.75,0,0,0,0,0,NULL,NULL,NULL,NULL,NULL,0);
					_rmtl_funcs.fz_rmtl_gen_preview(windex, rmtl);
				}
				
				_rmtl_funcs.fz_rmtl_ptr_to_tag(windex, rmtl, &rmtl_tag);
				
				/* assign object color to new surface style */
				_model_attr_funcs.fz_objt_attr_set_objt_rmtl(windex, obj_next, &rmtl_tag);
			}


			/* now delete mem */
			_mem_funcs.fz_mem_zone_free(NULL, (fzrt_ptr*)&objs);
		
		} /* else problem allocating memory for objects */
	}
	else /* num objs == 0 */
	{
		_fuim_funcs.fz_fuim_alrt_std_confirm("Works best with multiple objects present", FZ_FUIM_ALRT_CONFIRM_OK);
	}

	return(err);
}



/************************************************************************
							Plugin Functions
*************************************************************************/

/************************************************************************
	rainbow_acquire_fsets
		Acquires formZ function sets.
*************************************************************************/
static fzrt_error_td rainbow_acquire_fsets(const fzpl_fset_glue_fset* const fset_glue)
/************************************************************************/
{
	fzrt_error_td err = FZRT_NOERR;

	err = fset_glue->fzpl_fset_acquire ( FZ_FZRT_BASE_FSET_TYPE, FZ_FZRT_BASE_FSET_VERSION, FZRT_UUID_NULL,
								 FZPL_TYPE_STRING(fz_fzrt_base_fset), sizeof(_base_funcs),
								 (fzpl_fset_td *)&_base_funcs );

	if(err == FZRT_NOERR)
	{
		err = fset_glue->fzpl_fset_acquire ( FZ_FUIM_FSET_TYPE, FZ_FUIM_FSET_VERSION, FZRT_UUID_NULL,
									 FZPL_TYPE_STRING(fz_fuim_fset), sizeof(_fuim_funcs),
									 (fzpl_fset_td *)&_fuim_funcs );
	}

	
	if(err == FZRT_NOERR)
	{
		err = fset_glue->fzpl_fset_acquire ( FZ_MODEL_FSET_TYPE, FZ_MODEL_FSET_VERSION, FZRT_UUID_NULL,
									 FZPL_TYPE_STRING(fz_model_fset), sizeof(_model_funcs),
									 (fzpl_fset_td *)&_model_funcs );
	}
	
	if(err == FZRT_NOERR)
	{
		err = fset_glue->fzpl_fset_acquire ( FZ_RMTL_FSET_TYPE,
								  FZ_RMTL_FSET_VERSION,
								  FZRT_UUID_NULL,
								  FZPL_TYPE_STRING(fz_rmtl_fset),
								  sizeof(_rmtl_funcs),
								  (fzpl_fset_td *)&_rmtl_funcs );
	}

	if(err == FZRT_NOERR)
	{
		err = fset_glue->fzpl_fset_acquire ( FZ_TYPE_FSET_TYPE,
								  FZ_TYPE_FSET_VERSION,
								  FZRT_UUID_NULL,
								  FZPL_TYPE_STRING(fz_type_fset),
								  sizeof(_type_funcs),
								  (fzpl_fset_td *)&_type_funcs );
	}

	if(err == FZRT_NOERR)
	{
		err = fset_glue->fzpl_fset_acquire ( FZ_FZRT_MEM_FSET_TYPE,
								  FZ_FZRT_MEM_FSET_VERSION,
								  FZRT_UUID_NULL,
								  FZPL_TYPE_STRING(fz_fzrt_mem_fset),
								  sizeof(_mem_funcs),
								  (fzpl_fset_td *)&_mem_funcs );
	}
	
	if(err == FZRT_NOERR)
	{
		err = fset_glue->fzpl_fset_acquire ( FZ_OBJT_ATTR_FSET_TYPE,
								  FZ_OBJT_ATTR_FSET_VERSION,
								  FZRT_UUID_NULL,
								  FZPL_TYPE_STRING(fz_objt_attr_fset),
								  sizeof(_model_attr_funcs),
								  (fzpl_fset_td *)&_model_attr_funcs );
	}

	if(err == FZRT_NOERR)
	{
		err = fset_glue->fzpl_fset_acquire ( FZ_FZRT_GRAF_FSET_TYPE,
								  FZ_FZRT_GRAF_FSET_VERSION,
								  FZRT_UUID_NULL,
								  FZPL_TYPE_STRING(fz_fzrt_graf_fset),
								  sizeof(_graf_funcs),
								  (fzpl_fset_td *)&_graf_funcs );
	}

	if(err == FZRT_NOERR)
	{
		err = fset_glue->fzpl_fset_acquire(	FZ_FZRT_FILE_FSET_TYPE,
										FZ_FZRT_FILE_FSET_VERSION,
										FZRT_UUID_NULL,
										FZPL_TYPE_STRING(fz_fzrt_file_fset),
										sizeof(_file_funcs),
										(fzpl_fset_td *)&_file_funcs );
	}
	
	return err;
}

/************************************************************************
	rainbow_release_fsets
		Releases all function sets used from formZ.
*************************************************************************/
static void rainbow_release_fsets(const fzpl_fset_glue_fset* const fset_glue)
/************************************************************************/
{
	fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_file_funcs );
	fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_graf_funcs );
	fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_model_attr_funcs );
	fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_mem_funcs );
	fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_type_funcs );
	fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_rmtl_funcs );
	fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_model_funcs );
	fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_fuim_funcs );
	fset_glue->fzpl_fset_release( (fzpl_fset_td*)&_base_funcs );
}


/************************************************************************
	rainbow_check_config
		A first pass check to see if the plugin can be loaded.  It does
		the following:
			Acquires formZ function sets.
			Checks licensing.
			Informs formZ if this plugin is dependent on
				function sets from other plugins.
			Opens localization resource file and loads strings.
*************************************************************************/
static fzrt_error_td rainbow_check_config(const fzpl_fset_glue_fset* const fset_glue, const fzpl_host_config_td* const hostConfig)
/************************************************************************/
{
	fzrt_error_td err = FZRT_NOERR;
	/* resource file variables */
	fzrt_floc_ptr	floc;
	fzrt_fzr_ref_td	resource_ref;
	char			buffer[STRING_SIZE];

	_fset_glue = fset_glue;

	/*  Make sure required formZ defined function sets are available */
	/*  Release the function sets at unload. */

	err = rainbow_acquire_fsets(fset_glue);

	/* IF NEEDED: Check License */

	/* IF NEEDED: Tell the plugin manager what plugins this plugins are dependent on  */

	/* get localized strings */

	if (err == FZRT_NOERR)
	{
		/* initialize file locator structure */
		err = _file_funcs.fzrt_file_floc_init(&floc);
		if (err == FZRT_NOERR)
		{
			/* get the location of this plugin */
			err = _fset_glue->fzpl_plugin_file_get_floc(floc);

			if (err == FZRT_NOERR)
			{
				/* open the resource file eg. rainbow.ENU.fzr */
				err = _base_funcs.fzrt_fzr_open(floc, "rainbow", &resource_ref);

				if (err == FZRT_NOERR)
				{
					/* get the localized resource strings for this plugin */
					_base_funcs.fzrt_fzr_get_string(resource_ref, 1, 1, buffer);
					strcpy(_name, buffer);
					_base_funcs.fzrt_fzr_get_string(resource_ref, 1, 2, buffer);
					strcpy(_short_name, buffer);
					
					/* close the resource file */
					err = _base_funcs.fzrt_fzr_close(resource_ref);
				}
				/* throw away floc */
				_file_funcs.fzrt_file_floc_finit(&floc);
			}
		}
	}
	
	/* Tell the plugin manager how many plugins and what types we'll be loading */

	if (err != FZRT_NOERR)
	{
		rainbow_release_fsets(fset_glue);
	}

	return err;
}

/************************************************************************
	rainbow_init
		Registers plugins and their associated function sets.
*************************************************************************/
static fzrt_error_td rainbow_init(const fzpl_fset_glue_fset* const fset_glue, const fzpl_host_config_td* const hostConfig)
/************************************************************************/
{
	fzrt_error_td err = FZRT_NOERR;

	/* TODO : Get Localized plugin name form rsrc file */
	err = fset_glue->fzpl_plugin_register( RAINBOW_PLUGIN_UUID,
									 _name,
									 RAINBOW_PLUGIN_VERSION,
									 RAINBOW_PLUGIN_VENDOR,
									 RAINBOW_PLUGIN_URL,
									 FZ_UTIL_PROJ_EXTS_TYPE,
									 FZ_UTIL_PROJ_EXTS_VERSION,
									 NULL /*error string function*/,
									 0,
									 NULL,
									 &_rainbow_plugin_runtime_id);
	if (err == FZRT_NOERR)
	{
		err = fset_glue->fzpl_plugin_add_fset( _rainbow_plugin_runtime_id,
										 FZ_UTIL_CBAK_PROJ_FSET_TYPE,
										 FZ_UTIL_CBAK_PROJ_FSET_VERSION,
										 FZ_UTIL_CBAK_PROJ_FSET_NAME,
										 FZPL_TYPE_STRING(fz_util_cbak_proj_fset),
										 sizeof(fz_util_cbak_proj_fset),
										 rainbow_fill_fset,
										 FALSE);
	}

	if (err != FZRT_NOERR)
	{
		rainbow_release_fsets(fset_glue);
	}

	return err;
}

/************************************************************************
	rainbow_finit
		Unregisters plugins.  Releases function sets.
*************************************************************************/
static fzrt_error_td rainbow_finit(const fzpl_fset_glue_fset* const fset_glue)
/************************************************************************/
{
	fzrt_error_td err = FZRT_NOERR;

	err = fset_glue->fzpl_plugin_unregister(_rainbow_plugin_runtime_id);

	rainbow_release_fsets(fset_glue);

	return err;
}


/************************************************************************
						Utility Plugin Fill Function
*************************************************************************/

/************************************************************************
	rainbow_fill_fset
		Fills the fset structure with the callback functions for a
		project utility plugin.
*************************************************************************/
static fzrt_error_td rainbow_fill_fset(const fzpl_fset_def_ptr fset_def, fzpl_fset_td* const fset)
/************************************************************************/
{
	fzrt_error_td			  err = FZRT_NOERR;
	fz_util_cbak_proj_fset* install_struct;

	if ( fset_def && fset )
	{
		err = _fset_glue->fzpl_fset_def_check ( fset_def, FZ_UTIL_CBAK_PROJ_FSET_VERSION, FZPL_TYPE_STRING(fz_util_cbak_proj_fset), sizeof ( fz_util_cbak_proj_fset ), FZPL_VERSION_OP_NEWER );

		if (err == FZRT_NOERR)
		{
			install_struct = (fz_util_cbak_proj_fset*)fset;

			install_struct->fz_util_cbak_proj_uuid	= rainbow_uuid;
			install_struct->fz_util_cbak_proj_name	= rainbow_name;
			install_struct->fz_util_cbak_proj_main	= rainbow_main;
		}
	}
	else
		return (_base_funcs.fzrt_error_set(FZRT_BAD_PARAM_ERROR, FZRT_ERROR_SEVERITY_ERROR, FZRT_ERROR_CONTEXT_FZRT, 0));

	return err;
}

/************************************************************************
						Required Plugin Functions
*************************************************************************/

/************************************************************************
	fz_plugin_entry
		The main entry point for a formZ plugin.
*************************************************************************/
FZPL_PLUGIN_FUNC(fzrt_error_td) fz_plugin_entry(const fzpl_fset_glue_fset* const fset_glue, fzpl_command_td cmd, const fzpl_host_config_td* const hostConfig)
/************************************************************************/
{
	fzrt_error_td err = FZRT_NOERR;

	switch ( cmd )
	{
		case FZPL_PLUGIN_CHECK:
			err = rainbow_check_config ( fset_glue, hostConfig );
		break;
		
		case FZPL_PLUGIN_INITIALIZE:
			err = rainbow_init ( fset_glue, hostConfig );
		break;
		
		case FZPL_PLUGIN_EXIT:
			err = rainbow_finit ( fset_glue );
		break;
	}

	return ( err );
}
