#ifndef CCMIO_BUFFER_C
#define CCMIO_BUFFER_C

/*@@
 *  Program: Star File Format Library  - $RCSfile: ccmiobuffer.c,v $
 *  Author:  Geoff Prewett
 *  Date:    July 31, 2003
 *
 *
 *  Star File Format Library - Copyright (C) 2003 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: ccmiobuffer.c,v 1.1 2004/05/04 21:23:00 prewett Exp $
 */

#include <stdlib.h>
#include <string.h>	/* memcpy */
#include "ccmiocore.h"
#include "ccmioprivate.h"
#include "ADF.h"

#ifdef __cplusplus
extern "C" {
#endif

const long kCCMIOBufferBeginning = -1;

static CCMIOError ReadDataBuffer( CCMIOBuffer buff, int offset, void *data );

static long gBufferSize = kCCMIODefaultBufferSize;

void CCMIOSetBufferSize( int n )
{
    gBufferSize = n;
}

CCMIOError CCMIOCreateBuffer( CCMIOError *err, CCMIONode node,
			      CCMIOBufferType which, CCMIOBuffer *buff )
{
    int size;
    CCMIODataType type;

    CHECK_ERROR_AND_CLEAR_PTR(err, buff);

    *buff = (CCMIOBuffer)malloc(sizeof(struct _CCMIOBuffer));
    if (!(*buff))
	return(*err = kCCMIONoMemoryErr);

    (*buff)->dims = NULL;	/* Ensure that a free() won't crash if an err */
    (*buff)->buffer = NULL;	/* causes things to not be malloc()ed */
    CCMIOGetDataType(err, node, &type);
    CCMIOGetDimensions(err, node, &(*buff)->nDims, &(*buff)->dims);
    CCMIOGetDataSize(err, node, &(*buff)->max);
    if (*err || type == kCCMIOBadType)
    {
	free(*buff);
	return(*err = kCCMIOBadParameterErr);
    }

    if (which == kCCMIORead)
    {
	if (gBufferSize <= kCCMIOInfinite ||
	    gBufferSize * CCMIOGetDataTypeSize(type) >= (*buff)->max)
	    size = (*buff)->max;
	else
	    size = gBufferSize * CCMIOGetDataTypeSize(type);
    }
    else
	size = (*buff)->max;
    (*buff)->buffer = (CCMIOBufferDataType *)malloc(size);
    if (!(*buff)->buffer)
    {
	free((*buff)->dims);
	free(*buff);
	return(*err = kCCMIONoMemoryErr);
    }

    (*buff)->node = node;
    (*buff)->typeSize = CCMIOGetDataTypeSize(type);
    (*buff)->max = (*buff)->max / (*buff)->typeSize;
    (*buff)->size = size / (*buff)->typeSize;
    if (which == kCCMIORead)
	(*buff)->start = kCCMIOBufferBeginning;
    else
	(*buff)->start = 0;
    (*buff)->isRead = which == kCCMIORead;
    return(*err);
}

CCMIOError CCMIODestroyBuffer( CCMIOError *err, CCMIOBuffer buff )
{
    /* Do not call CHECK_ERROR, since we should deallocate stuff
       even if an error occurred */
    IGNORE_ERROR(err);

    if (buff)
    {
	if (!buff->isRead && buff->buffer)
	    CCMIOWriteData(err, buff->node, buff->buffer);
	
	if (buff->dims)
	    free(buff->dims);
	free(buff->buffer);
	free(buff);
    }
    return(*err);
}

CCMIOError CCMIOBufferReadDataPoint( CCMIOError *err, CCMIOBuffer buff,
				     void *data, ... )
{
    va_list args;
    CHECK_ERROR(err);

    va_start(args, data);
    CCMIOvBufferReadDataPoint(err, buff, data, args);
    va_end(args);
    return(*err);
}

CCMIOError CCMIOvBufferReadDataPoint( CCMIOError *err, CCMIOBuffer buff,
				      void *data, va_list args )
{
    int n, coord[ADF_MAX_DIMENSIONS], offset;

    CHECK_ERROR(err);
    if (!data) return(kCCMIOBadParameterErr);

    n = ParseArgs(args, coord);

#if StoreCStyleArrays
    FortranToCArray(n, coord);
#endif

    offset = CalcOffset(n, coord, buff->dims);
    return(*err = ReadDataBuffer(buff, offset, data));
}

CCMIOError CCMIOBufferReadNextDataPoint( CCMIOError *err, CCMIOBuffer buff,
					 int n, void *data )
{
    CHECK_ERROR(err);

    return(*err = ReadDataBuffer(buff, n, data));
}

CCMIOError CCMIOBufferWriteDataPoint( CCMIOError *err, CCMIOBuffer buff,
				      void *data, ... )
{
    va_list args;
    CHECK_ERROR(err);

    va_start(args, data);
    CCMIOvBufferWriteDataPoint(err, buff, data, args);
    va_end(args);
    return(*err);
}

CCMIOError CCMIOvBufferWriteDataPoint( CCMIOError *err, CCMIOBuffer buff,
				       void *data, va_list args )
{
    int n, coord[ADF_MAX_DIMENSIONS], offset;

    CHECK_ERROR(err);
    if (!data) return(kCCMIOBadParameterErr);

    n = ParseArgs(args, coord);
#if StoreCStyleArrays
    FortranToCArray(n, coord);
#endif

    offset = CalcOffset(n, coord, buff->dims);
    if (offset >= buff->size)
	return(*err = kCCMIOBadParameterErr);
    memcpy(buff->buffer + (offset * buff->typeSize), (char *)data,
	   buff->typeSize);

    return(*err);
}

CCMIOError CCMIOBufferGetNode( CCMIOError *err, CCMIOBuffer buff,
			       CCMIONode *node )
{
    CHECK_ERROR(err);
    if (!node) return(kCCMIOBadParameterErr);

    *node = buff->node;
    return(*err);
}

/* -------------------------------------------------------------------------- */
CCMIOError ReadDataBuffer( CCMIOBuffer buff, int offset, void *data )
{
    int start;
    ADFError adfErr;

    if (offset >= buff->max)
	return(kCCMIONoDataErr);

    if (buff->start == kCCMIOBufferBeginning ||
	offset < buff->start || offset >= buff->start + buff->size)
    {
	if (offset + buff->size >= buff->max)
	    start = buff->max - buff->size;
	else
	    start = offset;
	/* ADF_Read_Block_Data uses FORTRAN array indexing */
	ADF_Read_Block_Data(buff->node.node, start + 1,
			    start + buff->size, buff->buffer, &adfErr);
	if (IsADFError(adfErr)) return(ADFToCCMIOError(adfErr));
	buff->start = start;
    }
    memcpy(data, buff->buffer + (offset - buff->start) * buff->typeSize,
	   buff->typeSize);
    return(kCCMIONoErr);
}

#ifdef __cplusplus
}
#endif
#endif // CCMIO_BUFFER_C
