/***********************************************************************/
/* 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 <dx/dx.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <memory.h>     /* For memcpy() in _dxfByteSwap() */
 
#include "edf.h"
 
#if !defined(DXD_STANDARD_IEEE)
/* when they are ready, include whichever file defines swab, swaw and swad */
/* they will be part of a library, and written in assembler */

#define FLOAT_SIGN	0x80000000
#define FLOAT_EXP	0x7F800000
#define FLOAT_MANT	0x007FFFFF
#define DOUBLE_SIGN	0x80000000
#define DOUBLE_EXP	0x7FF00000
#define DOUBLE_MANT1	0x000FFFFF
#define DOUBLE_MANT2	0xFFFFFFFF

void _dxffloat_denorm(int *from, int count, int *denorm_fp, int *bad_fp)
{

    for ( ; count > 0; from++, count--) {
	
	switch(*from & FLOAT_EXP) {
	  case FLOAT_EXP:	/* NaN and Infinity */
	    *from = 0;
	    (*bad_fp)++;
	    break;

	  case 0:		/* check for real zero */
	    if ((*from & FLOAT_MANT) == 0)
		break;

	    *from = 0;
	    (*denorm_fp)++;

	  default:
	    break;
	}
    }

    return;

}

void _dxfdouble_denorm(int *from, int count, int *denorm_fp, int *bad_fp)
{

    for ( ; count > 0; from += 2, count--) {
	
	switch(*(from+1) & DOUBLE_EXP) {
	  case DOUBLE_EXP:	/* NaN and Infinity */
	    *from = 0;
	    *(from+1) = 0;
	    (*bad_fp)++;
	    break;

	  case 0:		/* check for real zero */
	    if (((*(from+1) & DOUBLE_MANT1)==0) && ((*from & DOUBLE_MANT2)==0))
		break;

	    *from = 0;
	    *(from+1) = 0;
	    (*denorm_fp)++;

	  default:
	    break;
	}
    }

    return;
}
#endif

 
/* 
 * read data array routines
 */
 
 
/* binary format.  reverse byte order if necessary.  the array space should
 *  have already been allocated before getting here.
 */
Error _dxfreadarray_binary(struct finfo *f, Array a, int format)
{
    FILE *fd;
    char *cp;

    int bytes;
    int done = 0;
    Error rc = OK;
    
    Type type;
    int items, actual;

#if WORDS_BIGENDIAN==1
    int nonnative = D_LSB;
#else
    int nonnative = D_MSB;
#endif

    fd = f->fd;

    if (!DXGetArrayInfo(a, &items, &type, NULL, NULL, NULL))
	return ERROR;
    
    if (items <= 0)
	DXErrorReturn(ERROR_INVALID_DATA, "bad item count, rank or shape");

    cp = (char *)DXGetArrayData(a);

    /* read the data 
     */
    if ((actual = fread(cp, DXGetItemSize(a), items, fd)) != items) {
	DXSetError(ERROR_INVALID_DATA, 
       "error reading binary data; %d items successfully read (%d expected)",
		   actual, items);
	return ERROR;
    }
 
    /* if user specifically requested the non-native format, 
     *  swab bytes within the right size word.
     */
    if ((format & D_BYTES) == nonnative) {

	bytes = DXGetItemSize(a) * items;

	/* what you swap depends on the datatype */
	switch(type) {
	  case TYPE_SHORT:
	  case TYPE_USHORT:
	    if (!SWAB(cp, cp, bytes))
		return ERROR;
	    break;
	
	  case TYPE_INT:
	  case TYPE_UINT:
	    if  (!SWAW(cp, cp, bytes))
		return ERROR;
	    break;

	  case TYPE_FLOAT:
	    if  (!SWAW(cp, cp, bytes))
		return ERROR;
#if !defined(DXD_STANDARD_IEEE)
	    _dxffloat_denorm((int *)cp, bytes/sizeof(float), 
			     &f->denorm_fp, &f->bad_fp);
#endif
	    break;
	    
	  case TYPE_DOUBLE:
	    if (!SWAD(cp, cp, bytes))
		return ERROR;
	
#if !defined(DXD_STANDARD_IEEE)
	    _dxfdouble_denorm((int *)cp, bytes/sizeof(double), 
			      &f->denorm_fp, &f->bad_fp);
#endif
	    break;
 
	  default:
	    break;
 
	}
    } 
#if !defined(DXD_STANDARD_IEEE)
    else {  
	/* don't have to swab data, but still have to check for denorms 
	 *  if the data is float or double and the processor is i860
	 */

	/* only for floats or doubles */
	switch(type) {
	  case TYPE_FLOAT:
	    bytes = DXGetItemSize(a) * items;
	    _dxffloat_denorm((int *)cp, bytes/sizeof(float), 
			     &f->denorm_fp, &f->bad_fp);
	    break;
	    
	  case TYPE_DOUBLE:
	    bytes = DXGetItemSize(a) * items;
	    _dxfdouble_denorm((int *)cp, bytes/sizeof(double), 
			      &f->denorm_fp, &f->bad_fp);
	    break;
	}
    }
#endif
 
    return rc;
}
 
 
/* binary format.  skip over the right number of bytes.
 */
Error _dxfskiparray_binary(struct finfo *f, int items, int itemsize)
{
    int rc;

    rc = fseek(f->fd, (long)items * itemsize, 1);
    if (rc == 0)
	return OK;

    return ERROR;
}
 
 
/* text format
 */
Error _dxfreadarray_text(struct finfo *f, Array a)
{
    int done = 0;
    Error rc = OK;
 
    Type type;
    Category cat;
    int items, i;
    int rank;
    int shape[MAXRANK];
 
    if(!DXGetArrayInfo(a, &items, &type, &cat, &rank, shape))
	return ERROR;
 
    if(items <= 0)
	DXErrorReturn(ERROR_INVALID_DATA, "bad item count");

    for (i=0; i<rank; i++)
	items *= shape[i];

    if(items <= 0)
	DXErrorReturn(ERROR_INVALID_DATA, "bad shape value");


    items *= DXCategorySize(cat);
    if (items <= 0)
	DXErrorReturn(ERROR_INVALID_DATA, "bad category value");

/* the seterror message below looks strange because dname is a string.
 *  it will say "... type" "float" etc. after macro expansion.
 */ 
#define GETITEMS(dtype,dname) \
{ \
    dtype *dp; \
    int i; \
\
    dp = (dtype *)DXGetArrayData(a); \
    for(i=0; i<items; i++, dp++) { \
        rc = _dxfmatch##dtype(f, dp); \
        if(!rc) { \
            DXSetError(ERROR_INVALID_DATA,  \
                       "error reading text data; item %d, type " dname, i+1); \
            return ERROR; \
        } \
    } \
}      

#if 0
    /* at this point we haven't parsed the next thing after FOLLOWS yet.
     *  for strings, we want to set nodict first.  for all the others,
     *  go ahead and read in the first number.
     */
    if (type != TYPE_STRING) {
	rc = skipkeyword(f);
	if (!rc)
	    return rc;
    }
#endif

    /* datatype */
    switch(type) {
      case TYPE_STRING:
	{ 
	    char *cp = (char *)DXGetArrayData(a);
	    char *dp;
	    int id, j, len;

	    /* make sure rank-1 means something down below 
	     * for strings, last rank is max length of each string
	     */
	    if (rank == 0) {
		rank = 1;
		shape[0] = 1;
	    }

	    /* for the following strings - don't put them in the
             *  dictionary since they are likely not to be used again.
	     */
	    _dxfsetdictstringmode(f, 1);

	    for (j=0; j<items/shape[rank-1]; j++, cp+= shape[rank-1]) {
		if (j != 0)
		    rc = _dxflexinput(f);
		dp = _dxfgetstringinfo(f, &len);
		if (!rc || !dp) {
		    DXSetError(ERROR_INVALID_DATA,
		       "error reading text data; item %d, type string",  j+1);
		    return ERROR;
		}
		if (len >= shape[rank-1]) {
		    DXSetError(ERROR_INVALID_DATA,
	   "error reading text data; string item %d longer than array shape",
			       j+1);
		    return ERROR;
		}
		strcpy(cp, dp);
	    }

	    /* start putting strings back into the dictionary */
	    _dxfsetdictstringmode(f, 0);
	    _dxflexinput(f);

        }
	break;

      case TYPE_UBYTE:
        GETITEMS(ubyte, "unsigned byte")
	break;
 
      case TYPE_BYTE:
        GETITEMS(byte, "signed byte")
	break;
 
      case TYPE_SHORT:
        GETITEMS(short, "short");
        break;
	
      case TYPE_USHORT:
        GETITEMS(ushort, "unsigned short");
        break;
	
      case TYPE_INT:
        GETITEMS(int, "integer");
        break;
	
      case TYPE_UINT:
        GETITEMS(uint, "unsigned integer");
        break;

      case TYPE_FLOAT:
        GETITEMS(float, "float");
        break;
 
      case TYPE_DOUBLE:
        GETITEMS(double, "double");
        break;

      default:
	DXErrorReturn(ERROR_NOT_IMPLEMENTED, "unsupported data type");
 
    }
 
    return rc;
}
 
 
/* text format
 */
Error _dxfskiparray_text(struct finfo *f, int items, Type type)
{
    int done = 0;
    Error rc = OK;
 
    if(items <= 0)
	DXErrorReturn(ERROR_INVALID_DATA, "bad item count");

    /* set it to NOT construct numbers but still return ok and to
     *  not construct strings.
     */
    _dxfsetskipnum(f, 1);
    _dxfsetdictstringmode(f, 1);

/* the seterror message below looks strange because dname is a string.
 *  it will say "... type" "float" etc. after macro expansion.
 */ 
#define GETITEMSX(dtype,dname) \
{ \
    dtype d; \
    int i; \
\
    for(i=0; i<items; i++) { \
        rc = _dxfmatch##dtype(f, &d); \
        if(!rc) { \
            DXSetError(ERROR_INVALID_DATA,  \
                       "error reading text data; item %d, type " dname, i+1); \
            goto error; \
        } \
    } \
}      

    /* datatype */
    switch(type) {
      case TYPE_STRING:
	{ 
	    int rc;
	    int j, len;

	    for (j=0; j<items; j++) {
		rc = _dxfgetstringnodict(f);
		if (!rc) {
		    DXSetError(ERROR_INVALID_DATA,
		       "error reading text data; item %d, type string",  j+1);
		    goto error;
		}
	    }

        }
	break;

      case TYPE_UBYTE:
        GETITEMSX(ubyte, "unsigned byte")
	break;
 
      case TYPE_BYTE:
        GETITEMSX(byte, "signed byte")
	break;
 
      case TYPE_SHORT:
        GETITEMSX(short, "short");
        break;
	
      case TYPE_USHORT:
        GETITEMSX(ushort, "unsigned short");
        break;
	
      case TYPE_INT:
        GETITEMSX(int, "integer");
        break;
	
      case TYPE_UINT:
        GETITEMSX(uint, "unsigned integer");
        break;

      case TYPE_FLOAT:
        GETITEMSX(float, "float");
        break;
 
      case TYPE_DOUBLE:
        GETITEMSX(double, "double");
        break;

      default:
	DXSetError(ERROR_NOT_IMPLEMENTED, "unsupported data type");
	break;
 
    }

 error: 
    _dxfsetskipnum(f, 0);
    _dxfsetdictstringmode(f, 0);

    return rc;
}
 
 
 
/* xdr format
 */
Error _dxfreadarray_xdr(FILE *fd, Array a)
{
#if 1
    DXErrorReturn(ERROR_NOT_IMPLEMENTED, "no xdr support yet");
#else
    unsigned char cvalue, *cp;
    int *ip;
    float *fp;
 
    Object o;
    int i, done = 0;
    Error rc = OK; 
 
    Type type;
    Category cat;
    int items;
    int rank;
    int shape[MAXRANK];


    if(!DXGetArrayInfo(a, &items, &type, &cat, &rank, shape))
	return ERROR;
 
    if(items <= 0)
	DXErrorReturn(ERROR_INVALID_DATA, "bad item count");
 
    while(rank > 0) {
	items *= shape[rank-1];
	rank--;
    }
    if(items <= 0)
	DXErrorReturn(ERROR_INVALID_DATA, "bad shape value");
 
    items *= DXCategorySize(cat);
    if (items <= 0)
	DXErrorReturn(ERROR_INVALID_DATA, "bad category value");
 
 
    /* switch on datatype */
    switch(type) {
      case TYPE_UBYTE:
	cp = (ubyte *)DXGetArrayData(a);

	for(i=0; i<items; i++, cp++) {
	    cvalue = 0;  /* call XDR routine here */  
            *cp = cvalue;
	}
	break;
 
      case TYPE_INT:
	ip = (int *)DXGetArrayData(a);
	
	for(i=0; i<items; i++, ip++) {
	    *ip = 0;   /* call XDR */
	}
	break;
	
      case TYPE_FLOAT:
	fp = (float *)DXGetArrayData(a);
 
	for(i=0; i<items; i++, fp++) {
	    *fp = 0.0;   /* call XDR */
	}
	break;
 
      default:
	DXErrorReturn(ERROR_NOT_IMPLEMENTED, 
		    "only byte, int or float data types");
 
    }
 
    return rc;

#endif

}
 
/* xdr format.  skip the right number of bytes.
 */
Error _dxfskiparray_xdr(FILE *fd, int items, int itemsize)
{
#if 1
    DXErrorReturn(ERROR_NOT_IMPLEMENTED, "no xdr support yet");
#else
    int rc;

    rc = fseek(fd->fd, (long)items * itemsize, 1);
    if (rc == 0)
	return OK;

 
    return rc;

#endif

}
 
 
/* read at another offset in this file
 */
Error _dxfreadoffset(struct finfo *f, Array a, int offset, int type)
{
    int rc = OK;
    int oldoffset;
    struct finfo nf;
    
    if (f->onepass) {
	DXSetError(ERROR_INVALID_DATA, 
		   "can't use forward offsets in this file in this mode");
	return ERROR;
    }
	
    _dxfdupfinfo(f, &nf);

    if ((oldoffset = ftell(f->fd)) < 0) {
	DXSetError(ERROR_INTERNAL, "ftell() returned bad value");
	return ERROR;
    }
    
    if (fseek(f->fd, offset + f->headerend, 0) != 0) {
	DXSetError(ERROR_INVALID_DATA, "can't seek to data offset %d", offset);
	return ERROR;
    }
    
    switch(type & D_FORMATS) {
      case D_TEXT:
      default:
	_dxfsetparse(&nf, (long)(offset + f->headerend), 0, 0);
	
	if (_dxfreadarray_text(&nf, a) != OK) {
	    DXAddMessage("array starting at data offset %d", offset);
	    rc = ERROR;
	    goto done;
	}
	break;

      case D_IEEE:
	if (_dxfreadarray_binary(&nf, a, type) != OK) {
	    DXAddMessage("array starting at data offset %d", offset); 
	    rc = ERROR;
	    goto done;
	}
	/* bad float warnings will be printed when this file is closed */
	break;
	
      case D_XDR:
	if (_dxfreadarray_xdr(f->fd, a) != OK) {
	    DXAddMessage("array starting at data offset %d", offset);
	    rc = ERROR;
	    goto done;
	}
	break;
    }

  done:
    if (fseek(f->fd, oldoffset, 0) != 0) {
	DXSetError(ERROR_INTERNAL, "fseek() returned error");
	rc = ERROR;
    }
 
    return rc;
}
 
 
/* read at an absolute byte offset in another file
 */
Error _dxfreadremote(struct finfo *f, Array a, int id, int type)
{
    int rc = OK;
    struct finfo nf;
    struct getby **gpp;
    char *fname;
    int offset;
 
    /* getby struct is setup by file_lists() */
    gpp = _dxfgetobjgb(f, id);
    if (!gpp)
	return ERROR;

    /* this is going to screw up the dictionary and line counts unless
     *  this is a separate structure...  does it need a separate getby?
     */
    _dxfdupfinfo(f, &nf);

    nf.fd = _dxfopen_dxfile((*gpp)->fname, f->fname, &nf.fname,".dx");
    if (!nf.fd) {
	rc = ERROR;
	goto done;
    }
    
    if (fseek(nf.fd, (*gpp)->num, 0) != 0)  {
	DXSetError(ERROR_INVALID_DATA, "can't seek to offset %d in file '%s'",
		   (*gpp)->num, nf.fname);
	rc = ERROR;
	goto done;
    }

    switch(type & D_FORMATS) {
      case D_TEXT:
      default:
	rc = _dxfsetparse(&nf, (long)(*gpp)->num, 0, 0);
	if (!rc)
	    goto done;
	
	if(_dxfreadarray_text(&nf, a) != OK) {
	    DXAddMessage("data file '%s'", nf.fname);
	    rc = ERROR;
	    goto done;
	}
	break;

      case D_IEEE:
	if (_dxfreadarray_binary(&nf, a, type) != OK) {
	    DXAddMessage("data file '%s'", nf.fname);
	    rc = ERROR;
	    goto done;
	}

	/* have to do this here since we aren't going to get to the
	 *  code which normally prints this message out 
	 */
#if !defined(DXD_STANDARD_IEEE)
	/* bad binary floating point numbers */
	if (nf.denorm_fp > 0)
	    DXMessage("#1010", nf.denorm_fp, nf.fname);
	if (nf.bad_fp > 0)
	    DXMessage("#1020", nf.bad_fp, nf.fname);
#endif
	
	break;
	
      case D_XDR:
	if (_dxfreadarray_xdr(nf.fd, a) != OK) {
	    DXAddMessage("data file '%s'", nf.fname);
	    rc = ERROR;
	    goto done;
	}
	break;
    }
    
  done: 
    if (nf.fd)
	fclose(nf.fd);
    DXFree((Pointer)nf.fname);
    return rc;
    
}


/*** Everthing below here is byte order stuff and belongs in library ********/

/*
 * LSB   - bytes within doubles are ordered in memory 76543210
 * MSB   - bytes within doubles are ordered in memory 01234567
 */
ByteOrder
_dxfLocalByteOrder(void)
{
    union {
	long l;
	char c[4];
    } u;
    
    u.l = 0x00112233;
    if (u.c[0] == 0)
	return BO_MSB;
    else if (u.c[0] == 0x33)
	return BO_LSB;
    else
	return BO_UNKNOWN;
}


#define WORD_SWAP	1
#define HWORD_SWAP	2
#define BYTE_SWAP	4
/* 
 * DXSwap the half words (16 bits) within a word (32 bits) 
 * abcd -> cdab
 */
#define SWAP_HWORDS(A) 	(((A) >> 16) & 0x0000ffff) | (((A) << 16) & 0xffff0000)
/* 
 * DXSwap the each pair of bytes (8 bits) within a word (32 bits) 
 * abcd -> badc
 */
#define SWAP_BYTES(A) 	(((A) >>  8) & 0x00ff00ff) | (((A) <<  8) & 0xff00ff00)

/*
 * Byte swap n 64-bit quantities  according to swaps: 
 * If (swaps & WORD_SWAP)	swap words
 * If (swaps & HWORD_SWAP)	swap half words within words
 * If (swaps & BYTE_SWAP)	swap bytes within half words 
 *
 * NOTE: this works when src=dest.
 8
 * FIXME: This can be improved if there is a 64-bit int with double word loads.
 */
static Error
swap_64(void *src, void *dest, int n, int swaps) 
{
    unsigned int *s = (unsigned int*)src;
    unsigned int *d = (unsigned int*)dest;
    unsigned int x,y,i;
    
    if (((int)s & 3) || ((int)d & 3)) 
	DXErrorGoto(ERROR_INTERNAL,"Tried to swap non-word aligned data");
    
    
    switch (swaps) {
      case (WORD_SWAP|HWORD_SWAP|BYTE_SWAP):
	  for (i=0 ; i<n ; i++) {
	      x = *s; 
	      y = *(++s);  s++;
	      x = SWAP_HWORDS(x); x = SWAP_BYTES(x);	
	      y = SWAP_HWORDS(y); y = SWAP_BYTES(y);	
	      *d = y;
	      *(++d) = x; d++;
	  }
	  break;
	case (HWORD_SWAP|BYTE_SWAP):
	  n *= 2;	/* Do swaps a word at a time instead of double words */
	  for (i=0 ; i<n ; i++)   {
	      x = *(s++); 
	      x = SWAP_HWORDS(x); x = SWAP_BYTES(x);	
	      *(d++) = x;
	  }
	  break;
	case (BYTE_SWAP):
	  n *= 2;	/* Do swaps a word at a time instead of double words */
	  for (i=0 ; i<n ; i++)  {
	      x = *(s++); 
	      x = SWAP_BYTES(x);	
	      *(d++) = x;
	  }
	  break;
	default:
	  DXErrorGoto(ERROR_INTERNAL,"Unexpected byte swapping");
      }
    return OK;
  error:
    return ERROR;
}

/*
 * Given a pointer src to n number of items of type t, perform
 * byte_swap on the data.  That is change from msb (big-endian) 
 * to lsb (little-endian) order (or vice versa).  Swapping is done 
 * in place and it is ok if dest=src;
 *
 *  	MSB stores double words in 0x0011223344556677 order 
 *  	LSB stores double words in 0x7766554433221100 order 
 * 
 */

Error
_dxfByteSwap(void *dest, void *src, int ndata, Type t) 
{
    unsigned int swaps, doubles, mask,x, tsize;
    
    tsize = DXTypeSize(t);		/* The number of bytes in an item */
    
    if (t == TYPE_UBYTE) {
	if (dest != src) 	/* DXCopy the buffers */
	    if (!memcpy(dest, src, tsize * ndata))
		DXErrorGoto(ERROR_INTERNAL,"Can not copy byte swap buffers");
	return OK;
    }
    
    ASSERT(sizeof(double) == 8);
    ASSERT(sizeof(int) == 4);
    ASSERT(sizeof(short) == 2);
    
    tsize *= 8;			/* The number of bits in an item */
    
    /* 
     * Determine which swaps we need to make, based on the size of the
     * item and assuming that we are swapping between msb and lsb.
     */
    swaps = 0;
    switch (tsize) {
      case 64: swaps |= WORD_SWAP;   /* Assuming msb <->lsb */
	/* Fall through */
      case 32: swaps |= HWORD_SWAP;  /* Assuming msb <->lsb */
	/* Fall through */
      case 16: swaps |= BYTE_SWAP;   /* Assuming msb <->lsb */
	break;
      case 8: return OK;             /* bytes are bytes are bytes */
      default:
	DXErrorGoto(ERROR_INTERNAL,"Don't know how to swap data type");
    }
    
    /* 
     * Set up and do the swapping. 
     */
    doubles = ndata;
    switch (tsize) {
      case 16:
	doubles /= 4;
	if ((ndata & 3) && (swaps & BYTE_SWAP)) {
	    /* Handle last odd shorts */
	    int shorts = doubles * 4;
	    unsigned short *s = (unsigned short*)src  + shorts;
	    unsigned short *d = (unsigned short*)dest + shorts;
	    shorts = ndata & 3;
	    while (shorts--) {
		x = *s;  s++;
		x = SWAP_BYTES(x); 
		*d = x;  d++;
	    }
	}
	break;
      case 32:
	doubles /= 2;
	if (ndata & 1)  {
	    /* Handle last odd word */
	    int words = doubles * 2;
	    unsigned int *s = (unsigned int*)src  + words;
	    unsigned int *d = (unsigned int*)dest + words;
	    x = *s; 
	    if (swaps & HWORD_SWAP)  x = SWAP_HWORDS(x); 
	    if (swaps & BYTE_SWAP)   x = SWAP_BYTES(x); 
	    *d = x;
	    
	}
	/* Fall through */
	break;
      case 64:
	break;
    }
    if (!swap_64(src,dest,doubles,swaps))
	goto error;
    
    return OK;
  error:
    return ERROR;
}
