/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#include <dxconfig.h>




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


#define MAX(a,b)  (((a)>(b))?(a):(b))

/*
 * simple routines for common cases
 */  
Object DXExtractInteger(Object o, int *ip)
{
    return DXExtractParameter(o, TYPE_INT, 0, 1, (Pointer)ip);
}

Object DXExtractFloat(Object o, float *fp)
{
    return DXExtractParameter(o, TYPE_FLOAT, 0, 1, (Pointer)fp);
}

/* slightly different - it returns a pointer to the start of the string.
 *  the caller doesn't need to allocate space for it.
 */
Object DXExtractString(Object o, char **cp)
{
    char *c;
    int len;
    Type t;

    /* this does the checking to be sure it is either a String object, 
     *  or a TYPE_STRING array.
     */
    if(!DXQueryParameter(o, TYPE_STRING, 1, NULL))
	return NULL;

    
    if(DXGetObjectClass(o) == CLASS_STRING) {
	if (cp)
	    *cp = DXGetString((String)o);
	return o;
    }
    
    DXGetArrayInfo((Array) o, &len, &t, NULL, NULL, NULL);
    
    if (t == TYPE_STRING) {
	if (len > 1)
	    return NULL;

	if (cp)
	    *cp = (char *)DXGetArrayData((Array)o);    
	return o;
    }

    /* for backwards compatibility - TYPE_UBYTE */
    c = (char *)DXGetArrayData((Array)o);
    
    while (--len > 0) {
	
	if (*c++)      /* fail if there are imbedded NULLs... */
	    continue;
	
	return NULL;
    }

    if (*c)            /* ...or if there isn't a trailing NULL */
	return NULL;
    
    if (cp)
	*cp = (char *)DXGetArrayData((Array)o);    
    return o;
}


Object DXExtractNthString (Object o, int n, char **cp)
{
    char *c, *cs;
    int	len;
    Type type;
    int rank, shape;

    if (n < 0)
	return (NULL);

    /* only returns ok if object class String or Array TYPE_STRING or UBYTE */
    if (! DXQueryParameter (o, TYPE_STRING, 1, NULL))
	return (NULL);

    if (DXGetObjectClass(o) == CLASS_STRING) {

	/* String objects are 1 string, so asking for any string 
	 *  but the 0th is bad. 
	 */
	if (n)
	    return (NULL);
	
	if (cp)
	    *cp = DXGetString ((String) o);
	return (o);
    }

    DXGetArrayInfo ((Array) o, &len, &type, NULL, NULL, NULL);
    if (type == TYPE_STRING) {

	/* len is number of strings in array */
	if (n >= len)
	    return NULL;

	/* the Query call above made sure this is rank 1, 
	 * so shape is safe here 
	 */
	DXGetArrayInfo ((Array) o, NULL, NULL, NULL, NULL, &shape);
	cs = (char *) DXGetArrayData ((Array) o);

	if (cp)
	    *cp = cs + (n * shape);
	return o;
    }

    /* here for backward compatibility - 
     * null separated chars in a UBYTE array 
     */
    cs = (char *) DXGetArrayData ((Array) o);
    
    for (c=cs; len--; ) {
	
	if (*c++)
	    continue;
	
	if (n == 0) {
	    if (cp)
		*cp = cs;
	    return (o);
	}
	
	n--;
	cs = c;
    }
    
    return (NULL);
}


/* new code */

#define CONVTYPE(from, to) \
static Error 					\
from##2##to(int count, from *fp, to *tp)  	\
{                                               \
    int i;					\
						\
    for (i=0; i<count; i++) 			\
	*tp++ = (to) *fp++; 			\
						\
    return OK; 					\
}

CONVTYPE(byte, short)
CONVTYPE(byte, int)
CONVTYPE(byte, float)
CONVTYPE(byte, double)
CONVTYPE(ubyte, short )
CONVTYPE(ubyte, ushort)
CONVTYPE(ubyte, int)
CONVTYPE(ubyte, uint)
CONVTYPE(ubyte, float)
CONVTYPE(ubyte, double)
CONVTYPE(short, int)
CONVTYPE(short, float)
CONVTYPE(short, double)
CONVTYPE(ushort, int)
CONVTYPE(ushort, uint)
CONVTYPE(ushort, float)
CONVTYPE(ushort, double)
CONVTYPE(int, float)
CONVTYPE(int, double)
CONVTYPE(uint, float)
CONVTYPE(uint, double)
CONVTYPE(float, double)


struct convtable {
    int conversion;                        /* convert flags */
    Error (*convfunc)();                   /* convert routine */
                     /* parms are: (int count, Pointer from, Pointer to); */
    int hascommon;                         /* has common representation? */
    Type commontype;                       /* smallest common representation */
};

/* conversion flags */
#define CONV_ILLEGAL  0   /* types don't convert without truncation */
#define CONV_ALLOWED  1   /* conversion allowed */
#define CONV_EQUAL    2   /* types already same */
#define CONV_NOTIMPL  3   /* type not implemented */
#define CONV_LOSSY    4   /* conversion may lose precision (not supported) */

/* table indices for conversion function table */
#define NUM_TYPES   11

#define C_BYTE      0
#define C_UBYTE     1
#define C_SHORT     2
#define C_USHORT    3
#define C_INT       4
#define C_UINT      5
#define C_HYPER     6   /* currently unsupported */
#define C_UHYPER    7   /* currently unsupported */
#define C_FLOAT     8
#define C_DOUBLE    9
#define C_LDOUBLE  10   /* currently unsupported */

#define TYPE_XXXX   TYPE_BYTE  /* fill in value for unimplemented types */

/* hascommon flags */
#define NO_COMMON   0
#define HAS_COMMON  1


static char *typename(Type t)
{
    switch (t) {
      case TYPE_BYTE:		return "byte";
      case TYPE_UBYTE:		return "ubyte";
      case TYPE_SHORT:		return "short";
      case TYPE_USHORT:		return "ushort";
      case TYPE_INT:		return "int";
      case TYPE_UINT:		return "uint";
      case TYPE_HYPER:		return "hyper";
/*    case TYPE_UHYPER:		return "uhyper";  */
      case TYPE_FLOAT:		return "float";
      case TYPE_DOUBLE:		return "double";
/*    case TYPE_LDOUBLE:	return "ldouble"; */
      default:			return "unknown";
    }
    /* notreached */
}

static int typeindex(Type t)
{
    switch (t) {
      case TYPE_BYTE:		return C_BYTE;
      case TYPE_UBYTE:		return C_UBYTE;
      case TYPE_SHORT:		return C_SHORT;
      case TYPE_USHORT:		return C_USHORT;
      case TYPE_INT:		return C_INT;
      case TYPE_UINT:		return C_UINT;
      case TYPE_HYPER:		return C_HYPER;
/*    case TYPE_UHYPER:		return C_UHYPER;  */
      case TYPE_FLOAT:		return C_FLOAT;
      case TYPE_DOUBLE:		return C_DOUBLE;
/*    case TYPE_LDOUBLE:	return C_LDOUBLE; */
      default:			return -1;
    }
    /* notreached */
}

static struct convtable typetable[NUM_TYPES][NUM_TYPES] = 
{
 /* from BYTE */			                           /* to ... */ 
 { CONV_EQUAL,      NULL,   	    HAS_COMMON,     TYPE_BYTE,     /* BYTE */
   CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* UBYTE */
   CONV_ALLOWED,    byte2short,     HAS_COMMON,     TYPE_SHORT,    /* SHORT */
   CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* USHORT */
   CONV_ALLOWED,    byte2int,  	    HAS_COMMON,     TYPE_INT,      /* INT */
   CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* UINT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* HYPER */
   CONV_NOTIMPL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* UHYPER */
   CONV_ALLOWED,    byte2float,     HAS_COMMON,     TYPE_FLOAT,    /* FLOAT */
   CONV_ALLOWED,    byte2double,    HAS_COMMON,     TYPE_DOUBLE,   /* DOUBLE */
   CONV_NOTIMPL,    NULL, 	    HAS_COMMON,     TYPE_XXXX,  }, /* LDOUBLE */

 /* from UBYTE */			                           /* to ... */ 
 { CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* BYTE */
   CONV_EQUAL,	    NULL,   	    HAS_COMMON,     TYPE_UBYTE,    /* UBYTE */
   CONV_ALLOWED,    ubyte2short,    HAS_COMMON,     TYPE_SHORT,    /* SHORT */
   CONV_ALLOWED,    ubyte2ushort,   HAS_COMMON,     TYPE_USHORT,   /* USHORT */
   CONV_ALLOWED,    ubyte2int,      HAS_COMMON,     TYPE_INT,      /* INT */
   CONV_ALLOWED,    ubyte2uint,     HAS_COMMON,     TYPE_UINT,     /* UINT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* HYPER */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* UHYPER */
   CONV_ALLOWED,    ubyte2float,    HAS_COMMON,     TYPE_FLOAT,    /* FLOAT */
   CONV_ALLOWED,    ubyte2double,   HAS_COMMON,     TYPE_DOUBLE,   /* DOUBLE */
   CONV_NOTIMPL,    NULL, 	    HAS_COMMON,     TYPE_XXXX,  }, /* LDOUBLE */

 /* from SHORT */			                           /* to ... */ 
 { CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_SHORT,    /* BYTE */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_SHORT,    /* UBYTE */
   CONV_EQUAL,	    NULL,   	    HAS_COMMON,     TYPE_SHORT,    /* SHORT */
   CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* USHORT */
   CONV_ALLOWED,    short2int,      HAS_COMMON,     TYPE_INT,      /* INT */
   CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* UINT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* HYPER */
   CONV_NOTIMPL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* UHYPER */
   CONV_ALLOWED,    short2float,    HAS_COMMON,     TYPE_FLOAT,    /* FLOAT */
   CONV_ALLOWED,    short2double,   HAS_COMMON,     TYPE_DOUBLE,   /* DOUBLE */
   CONV_NOTIMPL,    NULL, 	    HAS_COMMON,     TYPE_XXXX,  }, /* LDOUBLE */

 /* from USHORT */			                           /* to ... */ 
 { CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* BYTE */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_USHORT,   /* UBYTE */
   CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* SHORT */
   CONV_EQUAL,	    NULL,   	    HAS_COMMON,     TYPE_USHORT,   /* USHORT */
   CONV_ALLOWED,    ushort2int,     HAS_COMMON,     TYPE_INT,      /* INT */
   CONV_ALLOWED,    ushort2uint,    HAS_COMMON,     TYPE_UINT,     /* UINT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* HYPER */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* UHYPER */
   CONV_ALLOWED,    ushort2float,   HAS_COMMON,     TYPE_FLOAT,    /* FLOAT */
   CONV_ALLOWED,    ushort2double,  HAS_COMMON,     TYPE_DOUBLE,   /* DOUBLE */
   CONV_NOTIMPL,    NULL, 	    HAS_COMMON,     TYPE_XXXX, },  /* LDOUBLE */

 /* from INT */			                                   /* to ... */ 
 { CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_INT,      /* BYTE */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_INT,      /* UBYTE */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_INT,      /* SHORT */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_INT,      /* USHORT */
   CONV_EQUAL,	    NULL,   	    HAS_COMMON,     TYPE_INT,      /* INT */
   CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* UINT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* HYPER */
   CONV_NOTIMPL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* UHYPER */
   CONV_ALLOWED,    int2float,      HAS_COMMON,     TYPE_FLOAT,    /* FLOAT */
   CONV_ALLOWED,    int2double,     HAS_COMMON,     TYPE_DOUBLE,   /* DOUBLE */
   CONV_NOTIMPL,    NULL,	    HAS_COMMON,     TYPE_XXXX,  }, /* LDOUBLE */

 /* from UINT */			                           /* to ... */ 
 { CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* BYTE */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_UBYTE,    /* UBYTE */
   CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* SHORT */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_USHORT,   /* USHORT */
   CONV_ILLEGAL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* INT */
   CONV_EQUAL,	    NULL,   	    HAS_COMMON,     TYPE_UINT,     /* UINT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* HYPER */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* UHYPER */
   CONV_ALLOWED,    uint2float,     HAS_COMMON,     TYPE_FLOAT,    /* FLOAT */
   CONV_ALLOWED,    uint2double,    HAS_COMMON,     TYPE_DOUBLE,   /* DOUBLE */
   CONV_NOTIMPL,    NULL, 	    HAS_COMMON,     TYPE_XXXX, },  /* LDOUBLE */

 /* from HYPER */			                           /* to ... */ 
 { CONV_NOTIMPL,    NULL,   	    HAS_COMMON,     TYPE_HYPER,    /* BYTE */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* UBYTE */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* SHORT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* USHORT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* INT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* UINT */
   CONV_EQUAL,	    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* HYPER */
   CONV_NOTIMPL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* UHYPER */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_FLOAT,    /* FLOAT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* DOUBLE */
   CONV_NOTIMPL,    NULL, 	    HAS_COMMON,     TYPE_XXXX, },  /* LDOUBLE */

 /* from UHYPER */			                           /* to ... */ 
 { CONV_NOTIMPL,    NULL,   	     NO_COMMON,     TYPE_BYTE,     /* BYTE */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* UBYTE */
   CONV_NOTIMPL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* SHORT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* USHORT */
   CONV_NOTIMPL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* INT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_HYPER,    /* UINT */
   CONV_NOTIMPL,    NULL,  	     NO_COMMON,     TYPE_BYTE,     /* HYPER */
   CONV_EQUAL,	    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* UHYPER */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_FLOAT,    /* FLOAT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* DOUBLE */
   CONV_NOTIMPL,    NULL, 	    HAS_COMMON,     TYPE_XXXX, },  /* LDOUBLE */

 /* from FLOAT */			                           /* to ... */ 
 { CONV_ILLEGAL,    NULL,   	    HAS_COMMON,     TYPE_FLOAT,    /* BYTE */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_FLOAT,    /* UBYTE */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_FLOAT,    /* SHORT */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_FLOAT,    /* USHORT */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_FLOAT,    /* INT */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_FLOAT,    /* UINT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_FLOAT,    /* HYPER */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_FLOAT,    /* UHYPER */
   CONV_EQUAL,	    NULL,  	    HAS_COMMON,     TYPE_FLOAT,    /* FLOAT */
   CONV_ALLOWED,    float2double,   HAS_COMMON,     TYPE_DOUBLE,   /* DOUBLE */
   CONV_NOTIMPL,    NULL, 	    HAS_COMMON,     TYPE_XXXX,  }, /* LDOUBLE */

 /* from DOUBLE */			                           /* to ... */ 
 { CONV_ILLEGAL,    NULL,   	    HAS_COMMON,     TYPE_DOUBLE,   /* BYTE */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* UBYTE */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* SHORT */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* USHORT */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* INT */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* UINT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* HYPER */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* UHYPER */
   CONV_ILLEGAL,    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* FLOAT */ 
   CONV_EQUAL,	    NULL,  	    HAS_COMMON,     TYPE_DOUBLE,   /* DOUBLE */
   CONV_NOTIMPL,    NULL, 	    HAS_COMMON,     TYPE_XXXX,  }, /* LDOUBLE */

 /* from LDOUBLE */			                           /* to ... */ 
 { CONV_NOTIMPL,    NULL,   	    HAS_COMMON,     TYPE_XXXX,     /* BYTE */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* UBYTE */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* SHORT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* USHORT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* INT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* UINT */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* HYPER */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* UHYPER */
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* FLOAT */ 
   CONV_NOTIMPL,    NULL,  	    HAS_COMMON,     TYPE_XXXX,     /* DOUBLE */
   CONV_NOTIMPL,    NULL, 	    HAS_COMMON,     TYPE_XXXX,  }, /* LDOUBLE */
};


/* these could probably be macro-ized somehow */
/* THESE MUST WORK INPLACE!!! */

#define INC(ptr, size)  (Pointer)((long)ptr + (size))
#define DEC(ptr, size)  (Pointer)((long)ptr - (size))

/* conversion is:  a -> a + 0i */
static Error real2complex(int count, Pointer from, Pointer to, int itemsize)
{
    int i;
    Pointer efrom, eto;

    efrom = INC(from, itemsize * (count-1));
    eto = INC(to, 2 * itemsize * (count-1));
    for (i=count; i>0; i--) {
	memset(eto, '\0', itemsize);
	eto = DEC(to, itemsize);
	memcpy(efrom, eto, itemsize);
	efrom = DEC(from, itemsize);
	eto = DEC(to, itemsize);
    }

    return OK;
}

/* conversion is:  a -> a + 0i + 0j + 0k */
static Error real2quatern(int count, Pointer from, Pointer to, int itemsize)
{
    int i;
    Pointer efrom, eto;

    efrom = INC(from, itemsize * (count-1));
    eto = INC(to, 4 * itemsize * (count-1));
    for (i=count; i>0; i--) {
	memset(to, '\0', 3 * itemsize);
	to = DEC(to, 3 * itemsize);
	memcpy(from, to, itemsize);
	from = DEC(from, itemsize);
	to = DEC(to, itemsize);
    }

    return OK;
}

/* conversion is:  a + bi  ->  a + bi + 0j + 0k */
static Error complex2quatern(int count, Pointer from, Pointer to, int itemsize)
{
    int i;
    Pointer efrom, eto;

    efrom = INC(from, itemsize * (count-1));
    eto = INC(from, 2 * itemsize * (count-1));
    for (i=count; i>0; i--) {
	memset(to, '\0', itemsize);
	to = DEC(to, itemsize);
	memcpy(from, to, itemsize);
	from = DEC(from, itemsize);
	to = DEC(to, itemsize);
    }

    return OK;
}


struct convtable2 {
    int conversion;
    Error (*convfunc)(int count, Pointer from, Pointer to, int itemsize);
    int hascommon;
    Category commoncat;
};

/* (uses same conversion flags as above) */

#define NUM_CATS    3

#define C_REAL	    0
#define C_COMPLEX   1
#define C_QUATERN   2

static char *catname(Category c)
{
    switch (c) {
      case CATEGORY_REAL:	return "real";
      case CATEGORY_COMPLEX:	return "complex";
      case CATEGORY_QUATERNION:	return "quaternion";
      default:			return "unknown";
    }
    /* notreached */
}

static int catindex(Category c)
{
    switch (c) {
      case CATEGORY_REAL:	return C_REAL;
      case CATEGORY_COMPLEX:	return C_COMPLEX;
      case CATEGORY_QUATERNION:	return C_QUATERN;
      default:			return -1;
    }
    /* notreached */
}


static struct convtable2 cattable[NUM_CATS][NUM_CATS] = 
{
 /* from REAL */			                              /* to ... */ 
 { CONV_EQUAL,	  NULL, 	 HAS_COMMON,  CATEGORY_REAL,          /* REAL */
   CONV_ALLOWED,  real2complex,  HAS_COMMON,  CATEGORY_COMPLEX,       /* COMPLEX */
   CONV_ALLOWED,  real2quatern,  HAS_COMMON,  CATEGORY_QUATERNION, }, /* QUATERN */

 /* from COMPLEX */		 	                              /* to ... */ 
 { CONV_ILLEGAL,  NULL,   	 HAS_COMMON,  CATEGORY_COMPLEX,       /* REAL */
   CONV_EQUAL,	  NULL,  	 HAS_COMMON,  CATEGORY_COMPLEX,       /* COMPLEX */
   CONV_ALLOWED, complex2quatern, HAS_COMMON, CATEGORY_QUATERNION, }, /* QUATERN */

 /* from QUATERN */			                              /* to ... */ 
 { CONV_ILLEGAL,  NULL,   	HAS_COMMON,  CATEGORY_QUATERNION,     /* REAL */
   CONV_ILLEGAL,  NULL,  	HAS_COMMON,  CATEGORY_QUATERNION,     /* COMPLEX */
   CONV_EQUAL,	  NULL,  	HAS_COMMON,  CATEGORY_QUATERNION, },  /* QUATERN */
};

/* this modifies shape in place to squeeze out shapes of 1
 *  (e.g. rank 3, shape 3 1 4 ->  rank 2 shape 3 4)
 */
static void SqueezeShape(int *rank, int *shape)
{
    int i, j;

    i = 0;
    while (i < *rank) {
	if (shape[i] > 1)
	    i++;
	else {
            (*rank)--;
            for (j=i; j<*rank; j++)
                shape[j] = shape[j+1];
        } 
    }
  
}

/* this checks to see if it can squeeze the first rank/shape pair
 *  to match the second, and if it can, it modifies them in place
 *  and returns OK.  otherwise it leaves them alone and returns ERROR
 *  and sets the error code. 
 * if it has to expand to match, it adds shape 1 at the end of the list.
 *
 * this routine isn't done yet.
 */
static Error SqueezeToShape(int *rank, int *shape, int trank, int *tshape)
{
    int i, j;

    i = 0;
    while (i < *rank) {
	if (shape[i] > 1)
	    i++;
	else {
            (*rank)--;
            for (j=i; j<*rank; j++)
                shape[j] = shape[j+1];
        } 
    }

    return ERROR;
}


static Error
ConvertArrayContents(Array from, Array to, int squeeze)
{
    int i, j, k;
    int fi, ti;
    Pointer fromdp, todp;
    int itemsize;
    int count;
    int fromcount, tocount;
    Type fromtype, totype;
    Category fromcat, tocat;
    int fromrank, torank;
    int fromshape[100], toshape[100];

    DXGetArrayInfo(from, &fromcount, &fromtype, &fromcat, &fromrank, fromshape);
    DXGetArrayInfo(to, &tocount, &totype, &tocat, &torank, toshape);

    if (fromcount != tocount)
	DXErrorReturn(ERROR_INVALID_DATA, "array sizes don't match");


    /* squeeze out shapes of 1 from both and make sure they match. */
    if (squeeze) {
	SqueezeShape(&fromrank, fromshape);
	SqueezeShape(&torank, toshape);
    }

    if (fromrank != torank) {
	if (squeeze)
	    DXErrorReturn(ERROR_INVALID_DATA, "data ranks don't match");

	return ConvertArrayContents(from, to, 1);
    }

    for (i=0; i<fromrank; i++)
	if (fromshape[i] != toshape[i]) {
	    if (squeeze)
		DXErrorReturn(ERROR_INVALID_DATA, "data shapes don't match");

	    return ConvertArrayContents(from, to, 1);
	}


    if (((fi = typeindex(fromtype)) < 0) || ((ti = typeindex(totype)) < 0))
        DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data type");
    
    fromdp = DXGetArrayData(from);
    todp = DXGetArrayData(to);

    switch (typetable[fi][ti].conversion) {
      case CONV_EQUAL:
        memcpy(todp, fromdp, fromcount * DXGetItemSize(from));
        break;
        
      case CONV_ALLOWED:
	count = fromcount * (DXGetItemSize(from) / DXTypeSize(fromtype));
        if ((typetable[fi][ti].convfunc)(count, fromdp, todp) == ERROR)
            DXErrorReturn(ERROR_INVALID_DATA, "type conversion failed");
        
        break;
        
      case CONV_NOTIMPL:
      default:
        DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data type");
        
      case CONV_ILLEGAL:
      case CONV_LOSSY:
        DXSetError(ERROR_INVALID_DATA, "cannot convert %s to %s", 
                   typename(fromtype), typename(totype));
        return ERROR;
        
    }
    
    if (((fi = catindex(fromcat)) < 0) || ((ti = catindex(tocat)) < 0))
        DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data category");
    
    switch (cattable[fi][ti].conversion) {
      case CONV_EQUAL:
        break;
        
      case CONV_ALLOWED:
	count = fromcount * (DXGetItemSize(from) / DXCategorySize(fromcat));
        if ((cattable[fi][ti].convfunc)(count, todp, todp, 
                                        DXGetItemSize(from)) == ERROR)
            DXErrorReturn(ERROR_INVALID_DATA, "category conversion failed");
        
        break;
        
      case CONV_NOTIMPL:
      default:
        DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data category");
        
      case CONV_ILLEGAL:
      case CONV_LOSSY:
        DXSetError(ERROR_NOT_IMPLEMENTED, "cannot convert %s to %s", 
                   catname(fromcat), catname(tocat));
        return ERROR;
    }

    return OK;
}


static Array 
ArrayConvert(Array a, Type t, Category c, int rank, int *shape)
{
    int i;
    Array out;
    Type nt;
    Category nc;
    int nrank;
    int nshape[100];

    ASSERT(rank<100);

    out = DXNewArrayV(t, c, rank, shape);
    
    DXGetArrayInfo(a, &i, NULL, NULL, NULL, NULL);
    if (!DXAddArrayData(out, 0, i, NULL))
	return NULL;

    if (!ConvertArrayContents(a, out, 0)) {
	DXDelete((Object)out);
	return NULL;
    }

    return out;
}


Array 
DXArrayConvert(Array a, Type t, Category c, int rank, ... )
{
    int i;
    int shape[100];
    va_list arg;

    ASSERT(rank<100);

    /* collect args */
    va_start(arg,rank);       /* no spaces allowed - this is a weird macro */
    for (i=0; i<rank; i++)
	shape[i] = va_arg(arg, int);
    va_end(arg);

    /* common code with V version. */
    return ArrayConvert(a, t, c, rank, shape);
}


Array 
DXArrayConvertV(Array a, Type t, Category c, int rank, int *shape)
{
    return ArrayConvert(a, t, c, rank, shape);
}


/* sees if there is a common format and returns it.  otherwise returns
 *  NULL but and sets error code.
 */
static Error
QueryArrayCommon(Type *t, Category *c, int *rank, int *shape, int n, 
		 Array *a, int squeeze)
{
    int i, j, k;
    int ni, cti, cci;
    Type nextt, comt;
    Category nextc, comc;
    int nextrank, comrank;
    int nextshape[100], comshape[100];

    ASSERT(n<100);

    if (n <= 0) {
	DXSetError(ERROR_INVALID_DATA, 
                   "array list count must be a positive integer");
	return ERROR;
    }

    if (!DXGetArrayInfo(a[0], NULL, &comt, &comc, &comrank, comshape))
	return ERROR;

    if (squeeze)
	SqueezeShape(&comrank, comshape);

  
    /* if the values don't match, see if they are compatible, and use
     *  the minimum necessary changes.
     */
    for (i=1; i<n; i++) {

	if (!DXGetArrayInfo(a[i], NULL, &nextt, &nextc, &nextrank, nextshape))
	    return NULL;
        
	/* data type */
	if (comt != nextt) {
            if ((cti = typeindex(comt)) < 0)
                DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data type");
    
            if ((ni = typeindex(nextt)) < 0)
                DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data type");
	
            switch (typetable[cti][ni].hascommon) {
              case HAS_COMMON:
                comt = typetable[cti][ni].commontype;
                break;
	    
              case NO_COMMON:
                DXSetError(ERROR_INVALID_DATA, "no common format for %s and %s", 
                           typename(comt), typename(nextt));
                return ERROR;
            }
            
        }

        /* data category */
        if (comc != nextc) {
            if ((cci = catindex(comc)) < 0)
                DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data category");
    
            if ((ni = catindex(nextc)) < 0) 
                DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data category");
            
            switch (cattable[cci][ni].hascommon) {
              case HAS_COMMON:
                comc = cattable[cci][ni].commoncat;	    
                break;
	    
              case NO_COMMON:
                DXSetError(ERROR_INVALID_DATA, "no common format for %s and %s", 
                           catname(comc), catname(nextc));
                return ERROR;
            }
        }
        
        /* data rank -  squeeze out shapes of 1 */
	if (squeeze)
	    SqueezeShape(&nextrank, nextshape);
  
	if (comrank != nextrank) {
	    if (squeeze) {
		DXSetError(ERROR_INVALID_DATA, 
			   "data rank does not match (%d != %d)", 
			   comrank, nextrank);
		return ERROR;
	    }

	    return QueryArrayCommon(t, c, rank, shape, n, a, 1);
	}



	for (j=0; j<comrank; j++) {
	    if (comshape[j] != nextshape[j]) {
		if (squeeze) {
		    DXSetError(ERROR_INVALID_DATA, 
			       "data shape %d does not match (%d != %d)", 
			       j, comshape[j], nextshape[j]);
		    
		    return ERROR;
		}

		
		return QueryArrayCommon(t, c, rank, shape, n, a, 1);
	    }
	    
	}
    }


    /* ok - finally.  return the results */
    if (t)
	*t = comt;
    if (c)
	*c = comc;
    if (rank)
	*rank = comrank;
    if (shape) {
	for (i=0; i<comrank; i++)
	    shape[i] = comshape[i];
    }
    return OK;
}

Error 
DXQueryArrayCommon(Type *t, Category *c, int *rank, int *shape, 
		    int n, Array a, ...) 
{
    int i;
    Array alist[100];
    va_list arg;

    ASSERT(n<100);

    /* collect args */
    va_start(arg,n);       /* no spaces allowed - this is a weird macro */
    for (i=0; i<n; i++)
	alist[i] = va_arg(arg, Array);
    va_end(arg);

    /* common code with V version. */
    return QueryArrayCommon(t, c, rank, shape, n, alist, 0);
}

Error 
DXQueryArrayCommonV(Type *t, Category *c, int *rank, int *shape, 
		     int n, Array *alist)
{
    return QueryArrayCommon(t, c, rank, shape, n, alist, 0);
}



/* sees if the array can be converted to the requested format.  
 *  should this set the error code??  (it does right now)
 */
static Error 
QueryArrayConvert(Array a, Type t, Category c, int rank, int *shape, int squeeze)
{
    int i, j, k;
    int ai, rti, rci;
    Type at;
    Category ac;
    int arank;
    int ashape[100], rshape[100];

    if (!DXGetArrayInfo(a, NULL, &at, &ac, &arank, ashape))
	return ERROR;

    /* data type */
    if (at != t) {
        if ((rti = typeindex(t)) < 0)
            DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data type");
        
        if ((ai = typeindex(at)) < 0)
            DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data type");
	
        switch (typetable[ai][rti].conversion) {
          case CONV_EQUAL:
          case CONV_ALLOWED:
            break;
	    
          default:
            DXSetError(ERROR_INVALID_DATA, "cannot convert type %s into %s", 
                       typename(at), typename(t));
            return ERROR;
        }
    }
    
    /* data category */
    if (ac != c) {
        if ((rci = catindex(c)) < 0)
            DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data category");
        
        if ((ai = catindex(ac)) < 0) 
            DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data category");
        
        switch (cattable[ai][rci].conversion) {
          case CONV_EQUAL:
          case CONV_ALLOWED:
            break;
	    
          default:
            DXSetError(ERROR_INVALID_DATA, "cannot convert category %s into %s", 
                       catname(at), catname(t));
            return ERROR;
        }
        
    }

    /* get rid of 1's */
    if (squeeze) {
	SqueezeShape(&arank, ashape);
	for (i=0; i<rank; i++)
	    rshape[i] = shape[i];
	SqueezeShape(&rank, rshape);
    }
  
    if (arank != rank) {
	if (squeeze) {
	    DXSetError(ERROR_INVALID_DATA, "data rank does not match (%d > %d)", 
		       arank, rank);
	    return ERROR;
	}

	return QueryArrayConvert(a, t, c, rank, shape, 1);
    }
    
    /* data rank and shape */
    for (i=0; i<rank; i++)
        if (shape[i] != rshape[i]) {
	    if (squeeze) {
		DXSetError(ERROR_INVALID_DATA, 
			   "data shape does not match (%d != %d)", 
			   shape[i], ashape[i]);
		
		return ERROR;
	    }
	    
	    return QueryArrayConvert(a, t, c, rank, shape, 1);
	}

    return OK;
}



Error 
DXQueryArrayConvert(Array a, Type t, Category c, int rank, ...)
{
    int i;
    int shape[100];
    va_list arg;

    ASSERT(rank<100);

    /* collect args */
    va_start(arg,rank);       /* no spaces allowed - this is a weird macro */
    for (i=0; i<rank; i++)
	shape[i] = va_arg(arg, int);
    va_end(arg);

    /* common code with V version. */
    return QueryArrayConvert(a, t, c, rank, shape, 0);
}

Error 
DXQueryArrayConvertV(Array a, Type t, Category c, int rank, int *shape)
{
    return QueryArrayConvert(a, t, c, rank, shape, 0);
}



#define MAXDIM 32

/* type check an object, and return the number if a list.
 */
Object DXQueryParameter(Object o, Type t, int dim, int *count)
{
    int nitems;
    Type at;
    Category ac;
    int ar, as[MAXDIM];

    /* assume the worst */
    if(count)
	*count = 0;

    if(!o)
	return NULL;

    /* slightly special case for char arrays, which might come in
     *  as string objects.
     */
    if ((t == TYPE_STRING) && (DXGetObjectClass(o) == CLASS_STRING)) {
        
        char *cp = DXGetString((String)o);
        
        if(!cp || dim > 1)
            return NULL;
        
        if(dim == 0  &&  strlen(cp) > 1)
            return NULL;
        
        if(count)
            *count = strlen(cp);
        
        return o;
    } 
	

    /* check general characteristics first, before worrying about
     *  specific data types
     */
    if( DXGetObjectClass(o) != CLASS_ARRAY
     || !DXGetArrayInfo((Array)o, &nitems, &at, &ac, &ar, as)
     || ac != CATEGORY_REAL
     || ar > 1
     || nitems == 0)
	return NULL;

    
    /* don't do these checks for strings */
    if (at != TYPE_STRING) {
	/* if dim > 1, rank must be 1 and shape must match dim.
	 * dim 0 or 1 both match rank 1 shape 1 and rank 0
	 */
	if(dim > 1) {
	    if(ar != 1  ||  as[0] != dim)
		return NULL;
	} else {
	    if(ar > 1 || (ar == 1  &&  as[0] != 1))
		return NULL;
	}
    }
	
    if(count)
	*count = nitems;

    /* if types match exactly, you're done */
    if(at == t)
	return o;


    /* see if the types could be promoted ok.  'at' is the actual
     *  array type, 't' is what they want.
     */
    if (dim == 0) {
        if (DXQueryArrayConvert((Array)o, t, CATEGORY_REAL, 0) == OK)
            return o;
    } else {
        if (DXQueryArrayConvert((Array)o, t, CATEGORY_REAL, 1, dim) == OK)
            return o;
    }

    DXResetError();
    return NULL;
}


/* given an object, and a caller-allocated buffer, copy the input
 * to the buffer, converting type if necessary.
 */
Object DXExtractParameter(Object o, Type t, int dim, int count, Pointer p)
{
    Array na;
    char *sp;
    int i, nitems;
    Type at;
    Category ac;
    int ar, as[MAXDIM];

    if(!o)
	return NULL;

    /* slightly special case for string arrays, which might come in
     *  as string objects.
     */
    if(t == TYPE_STRING  &&  DXGetObjectClass(o) == CLASS_STRING) {

	sp = DXGetString((String)o);

	if(!sp || dim > 1)
	    return NULL;

	if(dim == 0  &&  strlen(sp) > 1)
		return NULL;

	if(count != strlen(sp))
	    return NULL;

	if (p)
	    strcpy((char *)p, sp);

	return o;
    }
	

    /* check general characteristics first, before worrying about
     *  specific data types
     */
    if( DXGetObjectClass(o) != CLASS_ARRAY
     || !DXGetArrayInfo((Array)o, &nitems, &at, &ac, &ar, as)
     || ac != CATEGORY_REAL
     || ar > 1
     || nitems == 0
     || nitems != count)
	return NULL;


    /* if dim > 1, rank must be 1 and shape must match dim.
     * dim 0 or 1 both match rank 1 shape 1 and rank 0
     */
    if(dim > 1) {
	if(ar != 1  ||  as[0] != dim)
	    return NULL;
    } else {
	if(ar > 1 || (ar == 1  &&  as[0] != 1))
	    return NULL;
    }


    /* if types match exactly, just memcpy the number of bytes and return */
    if(at == t) {
	if (p)
	    memcpy(p, DXGetArrayData((Array)o), 
		   nitems * DXGetItemSize((Array)o));
	return o;
    }


    /* types don't match exactly, so see if they can be converted.
     */
    if (dim == 0)
        na = DXArrayConvert((Array)o, t, CATEGORY_REAL, 0);
    else
        na = DXArrayConvert((Array)o, t, CATEGORY_REAL, 1, dim);

    if (!na) {
	DXResetError();
        return NULL;
    }

    if (p)
	memcpy(p, DXGetArrayData(na), nitems * DXGetItemSize(na));

    DXDelete((Object)na);
    return o;

}
