/* DX module to read XmdvTool format multivariate data and create 3 dx fields
   which will form the parallel coordinates view, the data itself, the labels,
   and the axes.  Used in parcoord.net
   Last modified: 6/13/96 by MOW */

#include <stdio.h>
#include <string.h>
#include <dx/dx.h>

#define MAXDIM 20    /* arbitrary, to avoid dynamic allocation of arrays */
#define SCALE 100.0  /* this allows text to be of proper proportion to plot */


Error m_ReadMDVpc(Object *input, Object *out)
{
  FILE *in;	/* input data file */
  int i, j, vert = 0;
  float datum, norm, *p_dp, *p_lp, *p_ap;
  int dim, count, *p_dc, *p_ac;
  char *filename, *p_ld;
  Array dp = NULL, dc = NULL, lp = NULL, ld = NULL, ap = NULL, ac = NULL;
  Field d = NULL, l = NULL, a = NULL;
  float min[MAXDIM], max[MAXDIM], xpos[MAXDIM];
  int card[MAXDIM];
  char label[MAXDIM][80], *label_p[MAXDIM];
  
  if(!input[0])	{
    DXSetError(ERROR_BAD_PARAMETER, "missing filename");
    goto error;
    }
  else if(!DXExtractString(input[0], &filename))	{
    DXSetError(ERROR_BAD_PARAMETER, "filename must be a string");
    goto error;
    }
  in = fopen(filename,"r");
  if (!in) {
    fprintf(stderr, "cannot open filename %s\n", filename);
    return 0;
  }
  
/* read first line of input and insure it is valid */
  fscanf(in, "%d %d", &dim, &count);
  if (dim < 1 || dim >= MAXDIM || count < 1) {
    fprintf(stderr, "bad dimension size or count: %d, %d\n", dim, count);
    return 0;
  }
  
/* skip end of line from previous read, then scan in labels */
  fgets(label[0], 80, in); 
  for(i = 0;i < dim;i++)	{
    fgets(label[i], 80, in); 
/* strip off newline character */
    label[i][strlen(label[i])-1] = '\0';
    label_p[i] = label[i];
    }

/* read in min and max for each dimension */
  for(i = 0;i < dim;i++)
    fscanf(in, "%f %f %d", &min[i], &max[i], &card[i]);

/* compute the normalized (between 0 and SCALE) x positions based on the number 
   of dimensions */
  for(i = 0;i < dim;i++)
    xpos[i] = SCALE * (float)i / ((float)dim - 1.0);
  
/* create 6 arrays */
  dp = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 2);
  dc = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, 2);
  lp = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 2);
  ld = DXMakeStringListV(dim, label_p);
  ap = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, 2);
  ac = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, 2);
  if(!dp || !dc || !lp || !ld || !ap || !ac)
    goto error;

/* create 3 fields */
  d = DXNewField();
  l = DXNewField();
  a = DXNewField();
  if(!d || !l || !a)
    goto error;
  
/* allocate space in arrays to hold data */
  if(!DXAddArrayData(dp, 0, dim*count, NULL)) goto error;
  if(!DXAddArrayData(dc, 0, (dim-1)*count, NULL)) goto error;
  if(!DXAddArrayData(lp, 0, dim, NULL)) goto error;
  if(!DXAddArrayData(ap, 0, dim*2, NULL)) goto error;
  if(!DXAddArrayData(ac, 0, dim, NULL)) goto error;

/* read the data, normalize it (between 0 and SCALE), and write it to the 
   first output field */
  p_dp = (float *)DXGetArrayData(dp);
  if(!p_dp) goto error;

  for(i = 0;i < count;i++)	{
    for(j = 0;j < dim;j++)	{
	fscanf(in, "%f", &datum);
	norm = SCALE * (datum - min[j]) / (max[j] - min[j]);
	*(p_dp++) = xpos[j];
	*(p_dp++) = norm;
	}
    }
  
/* now compute connections - note only have N-1 connections per datum
   for N dimensions */
  
  p_dc = (int *)DXGetArrayData(dc);
  if(!p_dc) goto error;

  for(i = 0;i < count;i++)	{
    for(j = 0;j < dim-1; j++)	{
	*(p_dc++) = vert;
	*(p_dc++) = vert+1;
	vert++;
	}
/* last vertex doesn't connect to anything - all connections 1-way */
    vert++;
    }
  if(!DXSetStringAttribute((Object)dc, "element type", "lines")) goto error;

/* finish up field description */
  if(!DXSetComponentValue(d, "positions", (Object)dp)) goto error;
  if(!DXSetComponentValue(d, "connections", (Object)dc)) goto error;

  if(!DXEndField(d)) goto error;
  out[0] = (Object)d;
  
/* now create field for text labels (positions and data) */
/* note the y-value of position is fixed */
  p_lp = (float *)DXGetArrayData(lp);
  if(!p_lp) goto error;
  for(i = 0;i < dim;i++)	{
    *(p_lp++) = xpos[i];
    *(p_lp++) = SCALE;
    }


  if(!DXSetComponentValue(l, "positions", (Object)lp)) goto error;
  if(!DXSetComponentValue(l, "data", (Object)ld)) goto error;
  if(!DXEndField(l)) goto error;
  out[1] = (Object)l;
  
/* finally, create field for axes (positions and connections) */
  p_ap = (float *)DXGetArrayData(ap);
  if(!p_ap) goto error;
/* each axis has 2 positions and 1 connection */
  for(i = 0;i < dim;i++)	{
    *(p_ap++) = xpos[i];
    *(p_ap++) = SCALE;
    *(p_ap++) = xpos[i];
    *(p_ap++) = 0.0;
    }

  p_ac = (int *)DXGetArrayData(ac);
  if(!p_ac) goto error;
  for(i = 0;i < dim*2;i= i+2)	{
    *(p_ac++) = i;
    *(p_ac++) = i+1;
    }

  if(!DXSetStringAttribute((Object)ac, "element type", "lines")) goto error;
  if(!DXSetComponentValue(a, "positions", (Object)ap)) goto error;
  if(!DXSetComponentValue(a, "connections", (Object)ac)) goto error;
  if(!DXEndField(a)) goto error;
  out[2] = (Object)a;

  return OK;
error:
  DXDelete((Object)d);
  DXDelete((Object)l);
  DXDelete((Object)a);
  DXDelete((Object)dp);
  DXDelete((Object)dc);
  DXDelete((Object)lp);
  DXDelete((Object)ld);
  DXDelete((Object)ap);
  DXDelete((Object)ac);
}


