/***********************************************************************/
/* 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"          */
/***********************************************************************/
/*
 * $Header: /home/gda/dxcvs/dx/src/exec/dxmods/echo.c,v 1.3 1999/05/10 15:45:24 gda Exp $
 */

#include <dxconfig.h>



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


#define MAXPARMS   21
#define MSGLEN     1024   /* can this be bigger and not blow up the UI? */
#define SLOP	   128
#define AtEnd(p)    ((p)->atend)


#define MIN(a, b)  (a<b? a : b)

struct einfo {
    int maxlen;
    int atend;
    char *mp;
    char *msgbuf;
};

static int EndCheck(struct einfo *ep)
{
    if (ep->atend)
        return 1;

    if ((ep->mp - ep->msgbuf) >= ep->maxlen) {

        ep->atend = 1;

        sprintf(ep->mp, " <msg too long> ");
        while(*ep->mp) 
            ep->mp++;
        
        return 1;
    }
    
    return 0;
}


/* NOTE: If you change the definition, also change the usage in
 *	dxloutputvalue.m
 */
Error _dxf_ConvertObjectsToStringValues(Object *in, int nobj, char **retstr);
        
static char *ClassNameString(Class c);
static void pv(struct einfo *ep, Type t, Pointer value, int offset);
static void pc(struct einfo *ep, Type t, Pointer value, int offset);
static void pq(struct einfo *ep, Type t, Pointer value, int offset);
static void pa(struct einfo *ep, Category c, Type t, Pointer value, int offset);
static void pvalue(struct einfo *ep, Type type, Category category, 
                   int rank, int *shape, Pointer value);
static void pvlist(struct einfo *ep, int nitems, 
                   Type type, Category category, 
                   int rank, int *shape, Pointer value, int itemsize);
static void ps(struct einfo *ep, char *);


int
m_Echo(Object *in, Object *out)
{
    int input, incount;
    char *echo_string = NULL;



    /* ok, until we get an arg count from the exec, we can count up
     *  the parms ourselves.  print until the last non-null one.
     */
    for (input = 0, incount = -1; input < MAXPARMS; input++)
	if(in[input])
	    incount = input+1;

    /* the default message used to be an empty string - now it just
     *  returns WITHOUT printing if input is null.
     */
    if (incount <= 0)
	return OK;


    if (!_dxf_ConvertObjectsToStringValues(in,incount,&echo_string))
	goto error;

    /* prevent messages from being mangled if they start with # */
    if (echo_string[0] == '#')
        DXExpandMessage(0);

    DXUIMessage("ECHO", echo_string);

    if (echo_string[0] == '#')
	DXExpandMessage(1);
    
    if (echo_string)
	DXFree((Pointer)echo_string);
    
    return OK;
    
  error:
    if (echo_string)
	DXFree((Pointer)echo_string);
    return ERROR;
}



static char *
ClassNameString(Class c)
{
    switch(c) {    
      case CLASS_MIN:            return "Min object ";
      case CLASS_OBJECT:         return "Generic Object ";
      case CLASS_PRIVATE:        return "Private object ";
      case CLASS_STRING:	 return "String object ";	
      case CLASS_FIELD:	       	 return "Field object ";	
      case CLASS_GROUP:	       	 return "Group object ";	
      case CLASS_SERIES:         return "Series object ";
      case CLASS_COMPOSITEFIELD: return "Compositefield object ";
      case CLASS_MULTIGRID: 	 return "Multigrid object ";
      case CLASS_ARRAY:	       	 return "Array object ";		
      case CLASS_REGULARARRAY:   return "Regulararray object ";
      case CLASS_PATHARRAY:      return "Patharray object ";    
      case CLASS_PRODUCTARRAY:   return "Productarray object ";
      case CLASS_MESHARRAY:      return "Mesharray object ";
      case CLASS_XFORM:	       	 return "Xform object ";		
      case CLASS_SCREEN:	 return "Screen object ";		
      case CLASS_CLIPPED:	 return "Clipped object ";	
      case CLASS_CAMERA:	 return "Camera object ";	
      case CLASS_LIGHT:	       	 return "Light object ";	
      case CLASS_MAX:            return "Max object ";
      case CLASS_DELETED:        return "Deleted object ";        
      case CLASS_CONSTANTARRAY:  return "Constantarray object ";        
      default: 			 return "Unknown object ";
    }
    /* notreached */
}


static void pv(struct einfo *ep, Type t, Pointer value, int offset)
{
    switch(t) {
      case TYPE_STRING:
	if (((char *)value)[offset] == '%')
	    *ep->mp = '%'; ep->mp++;	
        sprintf(ep->mp, "%c", ((char *)value)[offset]); break;
      case TYPE_SHORT:
        sprintf(ep->mp, "%d", ((short *)value)[offset]); break;
      case TYPE_INT:
        sprintf(ep->mp, "%d", ((int *)value)[offset]); break;
      case TYPE_FLOAT:
        sprintf(ep->mp, "%g", ((float *)value)[offset]); break;
      case TYPE_DOUBLE:
        sprintf(ep->mp, "%g", ((double *)value)[offset]); break;
      case TYPE_UINT:
        sprintf(ep->mp, "%u", ((uint *)value)[offset]); break;
      case TYPE_USHORT:
        sprintf(ep->mp, "%u", ((ushort *)value)[offset]); break;
      case TYPE_UBYTE:
        sprintf(ep->mp, "%u", ((ubyte *)value)[offset]); break;
      case TYPE_BYTE:
        sprintf(ep->mp, "%d", ((byte *)value)[offset]); break;
      case TYPE_HYPER:
      default:
	*ep->mp = '?'; ep->mp++; break;
    }
    while(*ep->mp) ep->mp++;
    EndCheck(ep);
}

static void pc(struct einfo *ep, Type t, Pointer value, int offset)
{
    if(EndCheck(ep))
        return;    
    sprintf(ep->mp, "(");      while(*ep->mp) ep->mp++;
    pv(ep, t, value, offset);
    if (EndCheck(ep))
        return;    
    sprintf(ep->mp, ", ");     while(*ep->mp) ep->mp++;
    pv(ep, t, value, offset+1);
    if (EndCheck(ep))
        return;    
    sprintf(ep->mp, ")");      while(*ep->mp) ep->mp++;
}

static void pq(struct einfo *ep, Type t, Pointer value, int offset)
{
    if (EndCheck(ep)) 
        return;    
    sprintf(ep->mp, "(");      while(*ep->mp) ep->mp++;
    pv(ep, t, value, offset);
    if (EndCheck(ep)) 
        return;    
    sprintf(ep->mp, ", ");     while(*ep->mp) ep->mp++;
    pv(ep, t, value, offset+1);
    if (EndCheck(ep)) 
        return;    
    sprintf(ep->mp, ", ");     while(*ep->mp) ep->mp++;
    pv(ep, t, value, offset+2);
    if (EndCheck(ep)) 
        return;    
    sprintf(ep->mp, ", ");     while(*ep->mp) ep->mp++;
    pv(ep, t, value, offset+3);
    if (EndCheck(ep)) 
        return;    
    sprintf(ep->mp, ")");      while(*ep->mp) ep->mp++;
}

static void pa(struct einfo *ep, Category c, Type t, Pointer value, int offset)
{
    offset *= DXCategorySize(c);
    switch (c) {
      case CATEGORY_REAL:
        pv(ep, t, value, offset);
        if (EndCheck(ep)) 
            return;    
        break;
      case CATEGORY_COMPLEX:
        pc(ep, t, value, offset);
        if (EndCheck(ep)) 
            return;    
        break;
      case CATEGORY_QUATERNION:
        pq(ep, t, value, offset);
        if (EndCheck(ep)) 
            return;    
        break;
    }
}

static void pvalue(struct einfo *ep, Type type, Category category, 
                   int rank, int *shape, Pointer value)
{
    int i, j, k;
    
    switch(rank) {
      case 0:
        pa(ep, category, type, value, 0);
        if (EndCheck(ep)) 
            return;    
        sprintf(ep->mp, " ");         while(*ep->mp) ep->mp++;
        break;
      case 1:
        if (EndCheck(ep)) 
            return;
	if (type == TYPE_STRING)
	    ps(ep, (char *)value);
	else {
	    sprintf(ep->mp, "[");        while(*ep->mp) ep->mp++;
	    for(i=0; i<shape[0]; i++) {
		pa(ep, category, type, value, i);
		if (EndCheck(ep)) 
		    return;    
		if (i<shape[0]-1)
		    sprintf(ep->mp, " ");     while(*ep->mp) ep->mp++;
	    }
	    if (EndCheck(ep)) 
		return;    
	    sprintf(ep->mp, "] ");        while(*ep->mp) ep->mp++;
	}
	break;
      case 2:
        if (EndCheck(ep)) 
            return;    
        sprintf(ep->mp, "[");        while(*ep->mp) ep->mp++;
        for(i=0; i<shape[0]; i++) {
            if (EndCheck(ep)) 
                return;    
	    if (type == TYPE_STRING) 
	    {
		ps(ep, ((char *)value) + i*shape[1]);
		if (i < (shape[0]-1)) *ep->mp++ = ' ';
	    }
	    else {
		sprintf(ep->mp, "[");    while(*ep->mp) ep->mp++;
		for(j=0; j<shape[1]; j++) {
		    pa(ep, category, type, value, i*shape[1] + j);
		    if (EndCheck(ep)) 
			return;   
		    if (j<shape[1]-1)
			sprintf(ep->mp, " "); while(*ep->mp) ep->mp++;
		}
		if (EndCheck(ep))
		    return;    
		sprintf(ep->mp, "]");    while(*ep->mp) ep->mp++;
	    }
        }
        if (EndCheck(ep)) 
            return;    
        sprintf(ep->mp, "]");        while(*ep->mp) ep->mp++;
        break;
      case 3:
        if (EndCheck(ep)) 
            return;    
        sprintf(ep->mp, "[");            while(*ep->mp) ep->mp++;
        for(i=0; i<shape[0]; i++) {
            if (EndCheck(ep)) 
                return;    
            sprintf(ep->mp, "[");        while(*ep->mp) ep->mp++;
            for(j=0; j<shape[1]; j++) {
                if (EndCheck(ep)) 
                    return;    
		if (type == TYPE_STRING)
		{
		    ps(ep, ((char *)value + i*shape[1] + j*shape[2]));
		    if (i < (shape[1]-1)) *ep->mp++ = ' ';
		}
		else {
		    sprintf(ep->mp, "[");    while(*ep->mp) ep->mp++;
		    for(k=0; k<shape[2]; k++) {
			pa(ep, category, type, value, 
			   i*shape[1] + j*shape[2] + k);
			if (EndCheck(ep)) 
			    return;   
			if (k<shape[2]-1)
			    sprintf(ep->mp, " "); while(*ep->mp) ep->mp++;
		    }
		    if (EndCheck(ep)) 
			return;    
		    sprintf(ep->mp, "]");    while(*ep->mp) ep->mp++;
		}
            }
            if (EndCheck(ep)) 
                return;    
            sprintf(ep->mp, "]");        while(*ep->mp) ep->mp++;
        }
        if (EndCheck(ep)) 
            return;    
        sprintf(ep->mp, "]");            while(*ep->mp) ep->mp++;
        break;
      default:
        sprintf(ep->mp, "is rank %d ", rank); while(*ep->mp) ep->mp++;
        break;
    }
    EndCheck(ep);
}


static void pvlist(struct einfo *ep, int nitems, 
                   Type type, Category category, 
                   int rank, int *shape, Pointer value, int itemsize)
{
    int i;
    
    if (EndCheck(ep)) 
        return;    
    sprintf(ep->mp, "{ ");    while(*ep->mp) ep->mp++;
    for (i=0; i<nitems; i++) {
        pvalue(ep, type, category, rank, shape, 
               (Pointer)((long)value + i * itemsize));
        if (EndCheck(ep))
            return;
	if (i < (nitems-1)) *ep->mp++ = ' ';
    }
    
    if (EndCheck(ep)) 
        return;  
    sprintf(ep->mp, " }");    while(*ep->mp) ep->mp++;
}


static void ps(struct einfo *ep, char *cp)
{
    while(*cp) {
        if(*cp == '%')
            *ep->mp++ = '%';
        
        *ep->mp++ = *cp++;
        if (EndCheck(ep))
            return;
    }
}

Error _dxf_ConvertObjectsToStringValues(Object *in, int nobj, char **retstr)
{
    struct einfo ei;
    int input, items, i, rank, shape[100];
    char *cp;
    Pointer dp;
    Class class;
    Type t;
    Category c;

    /* allocate a buffer in which to collect the message.
     */
    ei.maxlen = MSGLEN;
    ei.atend = 0;
    ei.msgbuf = (char *)DXAllocateZero(MSGLEN+SLOP);
    if (!ei.msgbuf)
 	goto error;	

    /* loop through input, adding to the end of the accumulated string
     *  each time.  since we used DXAllocateZero, the first time through
     *  the loop, *mp will be null and won't be advanced.
     */
    ei.mp = ei.msgbuf;
    for (input=0; input < nobj && !AtEnd(&ei); input++) {
        
	/* check for null parms */
	if(!in[input]) {
	    sprintf(ei.mp, "NULL");
            while(*ei.mp) ei.mp++;
	}
	else
	    switch ((class = DXGetObjectClass(in[input]))) {
	      case CLASS_ARRAY:
		if (!DXGetArrayInfo((Array)in[input], &items,
					    &t, &c, &rank, shape))
		    goto error;
		
		dp = DXGetArrayData((Array)in[input]);
		if (!dp)
		    goto error;
		
		if (items == 1)
		    pvalue(&ei, t, c, rank, shape, dp);
		else
		    pvlist(&ei, items, t, c, rank, shape, 
			   dp, DXGetItemSize((Array)in[input]));
		break;
		
	      case CLASS_STRING:
		cp = DXGetString((String)(in[input]));
		ps(&ei, cp);
		break;
		
	      case CLASS_GROUP:
		class = DXGetGroupClass((Group)in[input]);
		/* fall thru! */
		
	      default:
		sprintf(ei.mp, "%s", ClassNameString(class));
		while(*ei.mp) ei.mp++;
		break;
	    }
	
	if (input < (nobj-1)) 
	    *ei.mp++ = ' ';
        
    }
    
    *retstr = ei.msgbuf;
    return OK;
    
  error:
    if (ei.msgbuf) DXFree((Pointer)ei.msgbuf);
    *retstr = NULL;
    return ERROR;
}
