/*@@
 *  Program: Star File Format Library  - $RCSfile: ccmioread.c,v $
 *  Author:  Geoff Prewett
 *  Date:    January 5, 2004
 *
 *
 *  Star File Format Library - Copyright (C) 2004 by adapco, Ltd.
 *
 *  This program is the property of adapco, Ltd. and contains
 *  confidential and proprietary information.  The unauthorized use,
 *  distribution, or duplication of this program is prohibited.
 *  All rights reserved.
 *
 *  $Id: ccmioread.c,v 1.6 2004/08/18 21:39:06 wayne Exp $
 */

/* This file is not intended to be compiled directly.  Instead it
   implements a "template" function.  The macros TYPE, TYPE_ABBREV, and CCMIOTYPE
   must be defined before inclusion and this file must be included for
   each type of "template" that will be instantiated.  A poor man's template
   function. */

/* Need to use nested macros because concatenation (and stringification)
   do not expand their arguments.  But STRCAT does, so then it can pass it
   onto MAKE_NAME to get concatenated. */
#define STRCAT(X, Y)		MAKE_NAME(X, Y)
#define MAKE_NAME(X, Y)		X##Y

#define CONVERT		STRCAT(ConvertTo,TYPE)
TYPE CONVERT( const void *data, CCMIODataType type )
{
    if (type == kCCMIOInt32)
	return((TYPE)(*(int *)data));
    if (type == kCCMIOFloat32)
	return((TYPE)(*(float *)data));
    if (type == kCCMIOFloat64)
	return((TYPE)(*(double *)data));
    return((TYPE)0xbad0bad0);
}

#define OLDREAD STRCAT(CCMIOOldRead,TYPE_ABBREV)
CCMIOError OLDREAD( CCMIOError *err, CCMIONode node, int dimension,
		    int swapDims, TYPE *data, unsigned int start,
		    unsigned int end )
{
    void *buffer;
    unsigned int size;
    int i, j, k, nDims, *dims, adfErr, typeSize;
    TYPE *cBuffer;
    CCMIODataType nodeType;
    CHECK_ERROR(err);

    CCMIOGetDimensions(err, node, &nDims, &dims);
    if (nDims != dimension)
    {
	free(dims);
	return(*err = kCCMIOWrongDataTypeErr);
    }
    CCMIOGetDataType(err, node, &nodeType);
    typeSize = CCMIOGetDataTypeSize(nodeType);

    CCMIOGetDataSize(err, node, &size);
    buffer = malloc(size);
    if (!buffer)
    {
	free(dims);
	return(*err = kCCMIONoMemoryErr);
    }
    /* You'd think it wouldn't be necessary to allocate an entire new buffer.
       Unfortunately, the original algorithm is completely messed up and
       requires it. */
    cBuffer = (TYPE *)malloc(size);
    if (!cBuffer)
    {
	free(dims);  free(cBuffer);
	return(*err = kCCMIONoMemoryErr);
    }

    if (swapDims)
    {
	if (end == 0 || end > (unsigned int)dims[nDims - 1])
	    end = dims[0];
    }
    else
    {
	if (end == 0 || end > (unsigned int)dims[nDims - 1])
	    end = dims[nDims - 1];
    }

    ADF_Read_All_Data(node.node, buffer, &adfErr);
    if (!IsADFError(adfErr))
    {
	int delta = end - start, itemSize = 1;

	if (nDims == 2)
	{
	    for (i = 0;  i < dims[0];  ++i)
		for (j = 0;  j < dims[1]; ++j)
		    *(cBuffer + i*dims[1] + j) = CONVERT((char *)buffer + typeSize * (dims[0] * (j + start) + i), nodeType);
	}
	else if (nDims == 3)
	{
	    for (i = 0;  i < dims[0];  ++i)
		for (j = 0;  j < dims[1]; ++j)
		    for (k = 0;  k < dims[2]; ++k)
			*(cBuffer + i + j * dims[0] + k * dims[1] * dims[2]) = CONVERT((char *)buffer + typeSize*(i*dims[1]*dims[2] + j*dims[0] + k), nodeType);
	}
	if (swapDims)
	{
	    for (i = 1;  i < nDims;  ++i)
		itemSize *= dims[i];
	}
	else
	{
	    for (i = nDims - 2;  i >= 0;  --i)
		itemSize *= dims[i];
	}
	for (i = 0;  i < itemSize * delta;  ++i)
	    data[i] = cBuffer[i];
	fflush(stdin);
    }
    free(buffer);
    free(cBuffer);

    free(dims);
    return(*err = ADFToCCMIOError(adfErr));
}

#define READ STRCAT(Read,TYPE_ABBREV)
CCMIOError READ( CCMIOError *err, CCMIONode node, int dimension, TYPE *data,
		 unsigned int start, unsigned int end )
{
    unsigned int nItems, itemSize = 1;
    int i, nDims, *dims, adfErr;
    CCMIODataType nodeType;

    CHECK_ERROR(err);

    CCMIOGetDimensions(err, node, &nDims, &dims);
    if (nDims != dimension)
    {
	free(dims);
	return(*err = kCCMIOWrongDataTypeErr);
    }

    /* start and end are in conceptual units.  For example, for a 2D array
       verts[100][3], there are 100 units (each of size three), but map[100]
       also has 100 units (size one).  This is complicated a bit because
       ADF is in FORTRAN order, so the order of the dimensions is reversed from
       C. */
    nItems = dims[nDims - 1];
    for (i = 0;  i < nDims - 1;  ++i)
	itemSize *= dims[i];

    if (end == 0 || end > nItems)
	end = nItems;
    end *= itemSize;
    start = start * itemSize + 1;	/* ADF uses FORTRAN indexing */
    if (start > end)
    {
	free(dims);
	return(*err = kCCMIOBadParameterErr);
    }

    CCMIOGetDataType(err, node, &nodeType);
    if (nodeType == CCMIOTYPE && start == 1 && end == nItems * itemSize)
	ADF_Read_All_Data(node.node, (char *)data, &adfErr);
    else if (nodeType == CCMIOTYPE)
	ADF_Read_Block_Data(node.node, start, end, (char *)data, &adfErr);
    else
    {
	void *buffer;
	unsigned int i, n, typeSize = CCMIOGetDataTypeSize(nodeType);

	buffer = malloc((end - start + 1) * typeSize);
	if (!buffer)
	{
	    free(dims);
	    return(*err = kCCMIONoMemoryErr);
	}
	ADF_Read_Block_Data(node.node, start, end, buffer, &adfErr);
	if (!IsADFError(adfErr))
	{
	    n = end - start;
	    for (i = 0;  i <= n;  ++i)
		data[i] = CONVERT((char *)buffer + i * typeSize, nodeType);
	}
	free(buffer);
    }

    free(dims);
    return(*err = ADFToCCMIOError(adfErr));
}

#define WRITE STRCAT(Write,TYPE_ABBREV)
CCMIOError WRITE( CCMIOError *err, CCMIONode node, int nDims, const int *dims,
		  const TYPE *data, unsigned int start, unsigned int end )
{
    unsigned int nItems, itemSize = 1;
    int i, adfErr;
    CHECK_ERROR(err);

    CCMIOSetDataTypev(err, node, CCMIOTYPE, nDims, dims);
    nItems = dims[nDims - 1];
    for (i = 0;  i < nDims - 1;  ++i)
	itemSize *= dims[i];

    /* See comment in READ() */
    if (end == 0 || end > nItems)
	end = nItems;
    end *= itemSize;
    start = start * itemSize + 1;	/* ADF uses FORTRAN indexing */
    if (start > end)
	return(*err = kCCMIOBadParameterErr);

    if (start == (unsigned)1 && end == nItems * itemSize)
	ADF_Write_All_Data(node.node, (char *)data, &adfErr);
    else
	ADF_Write_Block_Data(node.node, start, end, (char *)data, &adfErr);

    return(*err = ADFToCCMIOError(adfErr));
}

#define READ_1D STRCAT(CCMIORead1,TYPE_ABBREV)
CCMIOError READ_1D( CCMIOError *err, CCMIONode node, TYPE *data,
		    unsigned int start, unsigned int end )
{
    return(READ(err, node, 1, data, start, end));
}

#define WRITE_1D STRCAT(CCMIOWrite1,TYPE_ABBREV)
CCMIOError WRITE_1D( CCMIOError *err, CCMIONode node, int n, const TYPE *data,
		     unsigned int start, unsigned int end )
{
/*    int dims[] = { n };     Sun compilers don't like this code */
    int dims[1];
    dims[0] = n;

    return(WRITE(err, node, 1, dims, data, start, end));
}

#define READ_2D STRCAT(CCMIORead2,TYPE_ABBREV)
CCMIOError READ_2D( CCMIOError *err, CCMIONode node, TYPE *data,
		    unsigned int start, unsigned int end )
{
    return(READ(err, node, 2, data, start, end));
}

#define WRITE_2D STRCAT(CCMIOWrite2,TYPE_ABBREV)
CCMIOError WRITE_2D( CCMIOError *err, CCMIONode node, int x, int y,
		     const TYPE *data, unsigned int start, unsigned int end )
{
/*    int dims[] = { x, y };	Sun compilers don't like this */
    int dims[2];
    dims[0] = x;
    dims[1] = y;

    return(WRITE(err, node, 2, dims, data, start, end));
}

#define READ_3D STRCAT(CCMIORead3,TYPE_ABBREV)
CCMIOError READ_3D( CCMIOError *err, CCMIONode node, TYPE *data,
		    unsigned int start, unsigned int end )
{
    return(READ(err, node, 3, data, start, end));
}

#define WRITE_3D STRCAT(CCMIOWrite3,TYPE_ABBREV)
CCMIOError WRITE_3D( CCMIOError *err, CCMIONode node, int x, int y, int z,
		   const TYPE *data, unsigned int start, unsigned int end )
{
/*    int dims[] = { x, y, z };		Sun compilers do not like this */
    int dims[3];
    dims[0] = x;
    dims[1] = y;
    dims[2] = z;

    return(WRITE(err, node, 3, dims, data, start, end));
}

#define READOPT STRCAT(CCMIOReadOpt,TYPE_ABBREV)
CCMIOError READOPT( CCMIOError *err, CCMIOID parent, const char *name,
		    TYPE *data )
{
    char buffer[64];	/* This ought to be big enough for any type */
    int adfErr;
    CCMIONode node;
    CCMIODataType type;
    CHECK_ERROR_AND_CLEAR_PTR(err, data);

    CCMIOGetNode(err, parent.node, name, &node);
    CCMIOGetDataType(err, node, &type);
    ADF_Read_Block_Data(node.node, 1, 1, buffer, &adfErr);
    *err = ADFToCCMIOError(adfErr);
    if (*err == kCCMIONoErr)
	*data = CONVERT(buffer, type);

    return(*err);
}

#define READOPT_1D STRCAT(CCMIOReadOpt1,TYPE_ABBREV)
CCMIOError READOPT_1D( CCMIOError *err, CCMIOID parent, const char *name,
		       TYPE *data, unsigned int start, unsigned int end )
{
    CCMIONode node;
    CHECK_ERROR(err);
    if (!data)  return(*err = kCCMIOBadParameterErr);

    CCMIOGetNode(err, parent.node, name, &node);
    return(READ_1D(err, node, data, start, end));
}

#define READOPT_2D STRCAT(CCMIOReadOpt2,TYPE_ABBREV)
CCMIOError READOPT_2D( CCMIOError *err, CCMIOID parent, const char *name,
		       TYPE *data, unsigned int start, unsigned int end )
{
    CCMIONode node;
    CHECK_ERROR(err);
    if (!data)  return(*err = kCCMIOBadParameterErr);

    CCMIOGetNode(err, parent.node, name, &node);
    if (parent.version < 20300)
    {
	int swap = FALSE, nDims, *dims;
	CCMIOGetDimensions(err, node, &nDims, &dims);
	if (nDims == 2 && dims[0] > dims[1])
	    swap = TRUE;
	free(dims);
	return(OLDREAD(err, node, 2, swap, data, start, end));
    }
    return(READ_2D(err, node, data, start, end));
}

#define READOPT_3D STRCAT(CCMIOReadOpt3,TYPE_ABBREV)
CCMIOError READOPT_3D( CCMIOError *err, CCMIOID parent, const char *name,
		       TYPE *data, unsigned int start, unsigned int end )
{
    CCMIONode node;
    CHECK_ERROR(err);
    if (!data)  return(*err = kCCMIOBadParameterErr);

    CCMIOGetNode(err, parent.node, name, &node);
    if (parent.version < 20300)
    {
	int swap = FALSE, nDims, *dims;
	CCMIOGetDimensions(err, node, &nDims, &dims);
	if (nDims == 2 && (dims[0] > dims[1] || dims[0] > dims[2]))
	    swap = TRUE;
	free(dims);
	return(OLDREAD(err, node, 2, swap, data, start, end));
    }
    return(READ_3D(err, node, data, start, end));
}

#define WRITEOPT_1D STRCAT(CCMIOWriteOpt1,TYPE_ABBREV)
CCMIOError WRITEOPT_1D( CCMIOError *err, CCMIOID parent, const char *name,
			int n, const TYPE *data,
			unsigned int start, unsigned int end )
{
    CCMIONode node;
    CHECK_ERROR(err);
    if (!data)  return(*err = kCCMIOBadParameterErr);

    CCMIOCreateNode(err, parent.node, TRUE, name, name, &node);
    return(WRITE_1D(err, node, n, data, start, end));
}
#define WRITEOPT_2D STRCAT(CCMIOWriteOpt2,TYPE_ABBREV)
CCMIOError WRITEOPT_2D( CCMIOError *err, CCMIOID parent, const char *name,
			int x, int y, const TYPE *data,
			unsigned int start, unsigned int end )
{
    CCMIONode node;
    CHECK_ERROR(err);
    if (!data)  return(*err = kCCMIOBadParameterErr);

    CCMIOCreateNode(err, parent.node, TRUE, name, name, &node);
    return(WRITE_2D(err, node, y, x, data, start, end));
}

#define WRITEOPT_3D STRCAT(CCMIOWriteOpt3,TYPE_ABBREV)
CCMIOError WRITEOPT_3D( CCMIOError *err, CCMIOID parent, const char *name,
			int x, int y, int z, const TYPE *data,
			unsigned int start, unsigned int end )
{
    CCMIONode node;
    CHECK_ERROR(err);
    if (!data)  return(*err = kCCMIOBadParameterErr);

    CCMIOCreateNode(err, parent.node, TRUE, name, name, &node);
    return(WRITE_3D(err, node, z, y, x, data, start, end));
}


/* Automatic setting of emacs local variables. */
/* Local Variables: */
/* mode: C++ */
/* tab-width: 8 */
/* End: */
