#include "pv.h"
#include "df.h"
#include "vg.h"

void load_vgroup(DF *, VGROUP *, char *, dataset_t **);

static char vgname[80];


void
build_images(args, images, datasets)
	args_t args[];
	image_t ** images;
	dataset_t ** datasets;
/* DESCRIPTION:  build_images uses information from the "args" data structure
to load data into the "images" data structure.  If there are already images
present in the data structure, the image is added to the head of the list.
If a dataset from the specified file has already been loaded and has the same
name, it is re-used in this image.
*/
{
	DF * hdf_fp;
	VGROUP vgbuf;
	VSUBGROUP vsbuf;
	VGROUP * vg;
	VSUBGROUP * vs;
	int vgid, vsid;
	int group_matched;
	int nvertices, interlace, vsize;
	char vsname[80], fields[80];

	float min, max;
	image_t * image;

	VGROUP * Vattach(DF *, int, char *);


	/* Open the HDF file for reading the data in */
	if ((hdf_fp = DFopen(main_image.fn, DFACC_READ, 0)) == NULL) {
		printf("ERROR:  Cannot open %s.\n",
		       main_image.fn);
		exit(-1);
		return;
	}

	/* Build a list of images, one for each group in the HDF file that */
	/* matches the main_image.group name.  Add a dataset to each one */
	/* for vertices, edges, and scalar information. */
	vgid = -1;
	group_matched = FALSE;
	while ( (vgid = Vgetid(hdf_fp, vgid)) != -1 ) {
		vg = Vattach(hdf_fp, vgid, "r");
		Vgetname(vg, vgname);

		/* Scan the list of images to find one with a */
		/* matching group name. */
		if (patmatch(main_image.group, vgname)) {

			/* Add image to list in alphabetical order */
			cur_image = add_image(images, vgname);

			/* Add datasets to the image */
			add_set(datasets, cur_image,
				&(cur_image->p[X]));
			add_set(datasets, cur_image,
				&(cur_image->p[Y]));
			add_set(datasets, cur_image,
				&(cur_image->p[Z]));
			add_set(datasets, cur_image,
				&(cur_image->connect));
			add_set(datasets, cur_image,
				&(cur_image->color));
		}

		/* Scan the list of images to find one with a */
		/* matching group name. */
		for (cur_image = *images; cur_image != NULL;
		     cur_image = cur_image->next) {
			if (strcasecmp(cur_image->group, vgname) == 0) {
				/* Remember that we found it */
				group_matched = TRUE;

				/* Try to load the datasets */
				load_vgroup(hdf_fp, vg, vgname, datasets);
				break;
			}
		}
		Vdetach(vg);
	}

	DFclose(hdf_fp);


	/* If the group was not found, abort the program. */
	if (!group_matched) {
		printf("ERROR:  Group '%s' was not found.\n",
		       main_image.group);
		exit(-1);
	}


	/* Check all datasets for empty value fields, filling them with */
	/* the first dataset's information that has the same name */
	for (image = *images; image != NULL; image = image->next) {
		fill_set(*datasets, &(image->p[X]));
		fill_set(*datasets, &(image->p[Y]));
		fill_set(*datasets, &(image->p[Z]));
		fill_set(*datasets, &(image->connect));
		fill_set(*datasets, &(image->color));
	}

	/* Find the maxima for the scalar data sets and propagate it to */
	/* all of them */
	min = MAX_FLOAT;
	max = MIN_FLOAT;
	for (image = *images; image != NULL; image = image->next) {
		min = MIN(min, image->color.ds->min);
		max = MAX(max, image->color.ds->max);
	}
	for (image = *images; image != NULL; image = image->next) {
		image->color.ds->min = min;
		image->color.ds->max = max;
	}

	cur_image = *images;
}

	image_t *
	add_image(images, group)
		image_t ** images;
		char * group;
	/* DESCRIPTION:  add_image creates a new record of image_t type,
	copies its fields from the main_image record, sets its group field
	to the value of "group", and inserts the record into the list of
	images using the fn and group fields as primary and secondary sort
	keys.
	*/
	{
		int fn_diff;
		image_t * new_image;
		image_t * curr;
		image_t * next;


		/* Create a new image record */
		new_image = (image_t *) malloc(sizeof(image_t));

		/* Copy information from the main_image record, except for */
		/* the group field whose value we copy from "group". */
		*new_image = main_image;
		strcpy(new_image->group, group);
		new_image->next = NULL;



		if (*images == NULL) {
			/* If the images list is empty, make this record its */
			/* only member */
			*images = new_image;
		}
		else {
			/* Otherwise search the list for the proper inser- */
			/* tion point and insert the record */
			curr = NULL;
			next = *images;
			while (next != NULL) {
			    fn_diff = strcmp(next->fn, new_image->fn);
			    if (fn_diff > 0) {
				/* Passed filename, time to insert */
				break;
			    }
			    else if (fn_diff == 0) {
				/* We match file key, check secondary */
				if (strcmp(next->group, new_image->group) > 0) {
				    /* We've passed group, time to insert */
				    break;
				}
			    }

			    /* Advance the pointers */
			    curr = next;
			    next = next->next;
			}

			/* Insert the new image record at the current point */
			if (curr == NULL) {
				*images = new_image;
			}
			else {
				curr->next = new_image;
			}
			new_image->next = next;
		}

		/* Return the pointer to the new image */
		return (new_image);
	}



	void
	add_set(datasets, image, field)
		dataset_t ** datasets;
		image_t * image;
		datadesc_t * field;
	/* DESCRIPTION:  add_set sets "field" to point to a data set record
	that contains the desired data.  If the data is already in "datasets",
	we use it, otherwise we load it from the file and create a new record.
	*/
	{
		dataset_t * ds;
		float min;
		float max;
		float * value;

		/* Scan the data set looking for a match to the field */
		for (ds = *datasets; ds != NULL; ds = ds->next) {
			if ( (strcmp(ds->fn, image->fn) == 0) &&
			     (strcmp(ds->group, image->group) == 0) && 
			     (strcmp(ds->name, field->name) == 0) ) {
				field->ds = ds;

				/* Increment count of uses */
				(ds->used)++;
				return;
			}
		}

		/* If we didn't find the dataset in the list, add it to */
		/* the head of the list in a new data structure. */
		ds = (dataset_t *) malloc(sizeof(dataset_t));
		strcpy(ds->fn, image->fn);
		strcpy(ds->group, image->group);
		strcpy(ds->name, field->name);
		ds->used = 0;
		ds->min = 0.0;
		ds->max = 0.0;
		ds->value = NULL;
		ds->next = *datasets;
		*datasets = ds;

		/* Attach the dataset to the proper field in the image */
		field->ds = ds;
	}


	void
	fill_set(datasets, ds)
		dataset_t * datasets;
		datadesc_t * ds;
	{
		/* If the datadesc ds field is empty, we have an error */
		if (ds->ds == NULL) {
			printf("ERROR(fill_set):  %s ds field is NULL.\n",
				ds->name);
			return;
		}

		/* If the dataset value field is empty, search for another */
		/* occurrance of a dataset with the same name and link its */
		/* information into this dataset. */
		if (ds->ds->value == NULL) {
			while (datasets != NULL) {
				if ((strcasecmp(datasets->name,
						ds->name) == 0) &&
				    (datasets->value != NULL)){
					ds->ds->type = datasets->type;
					ds->ds->min = datasets->min;
					ds->ds->max = datasets->max;
					ds->ds->size = datasets->size;
					ds->ds->value = datasets->value;
					break;
				}

				datasets = datasets->next;
			}
		}
	}


	void
	get_stats(ds)
		dataset_t * ds;
	/* DESCRIPTION:  get_stats calculates the maximum and minimum values
	for the data in "ds".
	LIMITATION:  Assumes data is floating point.  The routine is applied
	to integer connectivity data but the results are ignored.
	*/
	{
		float min, max;
		float * value;
		long size;


		/* Initialize the minimum and maximum values */
		min = MAX_FLOAT;
		max = MIN_FLOAT;

		/* Examine every element of the data set... */
		for (value = (float *) ds->value, size = ds->size;
		     size > 0; value++, size--) {

			/* ...is it minimum? */
			if (min > *value) {
				min = *value;
			}

			/* ...is it maximum? */
			if (max < *value) {
				max = *value;
			}
		}

		/* Assign the final values to the dataset */
		ds->min = (double) min;
		ds->max = (double) max;
	}


	void
	load_vgroup(hdf_fp, vgbuf, group, datasets)
		DF * hdf_fp;
		VGROUP * vgbuf;
		char * group;
		dataset_t ** datasets;
	/* DESCRIPTION:  load_dataset loads all of the datasets from the
	group referenced by "vgbuf" in the file "hdf_fp" that are needed by
	"datasets".
	*/
	{
		dataset_t * ds;
		VGROUP * vg;
		char vsname[80];
		char fields[80];
		char * data;
		int vsid;
		VSUBGROUP * vs;

		int nv, interlace, vsize;


		vsid = -1;
		while ( (vsid = Vgetnext(vgbuf, vsid)) != -1) {
			vs = (VSUBGROUP *) VSattach(hdf_fp, vsid, "r");
			VSinquire(vs, &nv, &interlace, fields, &vsize,
				  vsname);
			
			/* Check our in-memory datasets to see if we need */
			/* the currently viewed file-based dataset. */
			for (ds = *datasets; ds != NULL; ds = ds->next) {

			    /* If the dataset is not already filled and the */
			    /* name the dataset matches one of the sets in */
			    /* the group, then load the set into memory. */
			    if ((ds->value == NULL) &&
			        (VSfexist(vs, ds->name) == 1) &&
				(strcasecmp(ds->group, group) == 0)) {

				/* Malloc the space we need, attach to the *.
				/* proper field, read the field data */
				ds->value = (char *) malloc(nv * vsize);
				VSsetfields(vs, ds->name);
				printf("reading %s:%s/%s\n", main_image.fn,
				       vgname, ds->name);
				VSread(vs, ds->value, nv, interlace);
				ds->size = (long) nv * vsize / sizeof(int);
				Vdetach(vs);

				/* Calculate statistics */
				get_stats(ds);

				/* Since we've found a match, quit searching */
				/* the list of internal datasets */
				break;
		          }
		      }
		}
	}
