// Copyright 1997-1999 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.
//
// $Header: /Network/Developer/Source/CVS/OmniGroup/OmniFoundation/DataStructures.subproj/OFDataBuffer.h,v 1.9 1999/03/30 21:25:35 kc Exp $

#import "OFObject.h"

#import <Foundation/NSData.h>
#import <Foundation/NSString.h>
#import <Foundation/NSByteOrder.h>
#import <math.h>

#import "NSString-OFExtensions.h"
#import "OFByteOrder.h"

#define OFDataBufferInternalBufferSize 512

typedef struct {
    NSMutableData *data;
    byte tempBuffer[OFDataBufferInternalBufferSize];
    unsigned int tempBufferCount;
    OFByteOrder byteOrder;
} OFDataBuffer;

static inline void
OFDataBufferInit(OFDataBuffer *dataBuffer)
{
    dataBuffer->data = [[NSMutableData alloc] init];
    dataBuffer->tempBufferCount = 0;
    dataBuffer->byteOrder = NS_UnknownByteOrder;
}

static inline void
OFDataBufferRelease(OFDataBuffer *dataBuffer)
{
    [dataBuffer->data release];
}

static inline void
OFDataBufferFlush(OFDataBuffer *dataBuffer)
{
    [dataBuffer->data appendBytes:dataBuffer->tempBuffer length:dataBuffer->tempBufferCount];
    dataBuffer->tempBufferCount = 0;
}

static inline NSData *
OFDataBufferData(OFDataBuffer *dataBuffer)
{
    OFDataBufferFlush(dataBuffer);
    return dataBuffer->data;
}

static inline unsigned int
OFDataBufferCurrentLength(OFDataBuffer *dataBuffer)
{
    return [dataBuffer->data length] + dataBuffer->tempBufferCount;
}

static inline void
OFDataBufferFlushIfNecessary(OFDataBuffer *dataBuffer)
{
    if (dataBuffer->tempBufferCount < OFDataBufferInternalBufferSize)
	return;
    OFDataBufferFlush(dataBuffer);
}

static inline char
OFDataBufferHexCharacterForDigit(int digit)
{
    if (digit < 10)
	return digit + '0';
    else
	return digit + 'a' - 10;
}

static inline void
OFDataBufferAppendByte(OFDataBuffer *dataBuffer, byte aByte)
{
    OFDataBufferFlushIfNecessary(dataBuffer);
    dataBuffer->tempBuffer[dataBuffer->tempBufferCount++] = aByte;
}
 
static inline void
OFDataBufferAppendHexForByte(OFDataBuffer *dataBuffer, byte aByte)
{
    OFDataBufferAppendByte(dataBuffer, OFDataBufferHexCharacterForDigit((aByte & 0xf0) >> 4));
    OFDataBufferAppendByte(dataBuffer, OFDataBufferHexCharacterForDigit(aByte & 0x0f));
}

static inline void
OFDataBufferAppendCString(OFDataBuffer *dataBuffer, const char *cString)
{
    const char *characterPtr;
    
    for (characterPtr = cString; *characterPtr; characterPtr++)
	OFDataBufferAppendByte(dataBuffer, *characterPtr);
}
 
static inline void
OFDataBufferAppendBytes(OFDataBuffer *dataBuffer, const byte *bytes, unsigned int length)
{
    unsigned int byteCount;
    const byte *bytePtr;
    
    for (bytePtr = bytes, byteCount = length; byteCount--; bytePtr++)
	OFDataBufferAppendByte(dataBuffer, *bytePtr);
}
 

#define OFDataBufferSwapBytes(value, swapType)				\
    switch (dataBuffer->byteOrder) {					\
        case NS_UnknownByteOrder:      					\
            break;	   						\
        case NS_LittleEndian:      					\
            value = NSSwapHost ## swapType ## ToLittle(value);		\
            break;							\
        case NS_BigEndian:						\
            value = NSSwapHost ## swapType ## ToBig(value);		\
            break;							\
    }

#define OFDataBufferAppendOfType(cType, nameType, swapType)	 	\
static inline void OFDataBufferAppend ## nameType      			\
	(OFDataBuffer *dataBuffer, cType value)				\
{									\
    OFDataBufferSwapBytes(value, swapType);    				\
    OFDataBufferAppendBytes(dataBuffer, (void *)&value, sizeof(cType));	\
}

OFDataBufferAppendOfType(long int, LongInt, Long)
OFDataBufferAppendOfType(short int, ShortInt, Short)
OFDataBufferAppendOfType(unichar, Unichar, Short)
OFDataBufferAppendOfType(long long int, LongLongInt, LongLong)

#undef OFDataBufferAppendOfType
#undef OFDataBufferSwapBytes

static inline void OFDataBufferAppendFloat(OFDataBuffer *dataBuffer, float value)
{
    NSSwappedFloat swappedValue;

    switch (dataBuffer->byteOrder) {
        case NS_UnknownByteOrder:
            swappedValue = NSConvertHostFloatToSwapped(value);
            break;
        case NS_LittleEndian:
            swappedValue = NSSwapHostFloatToLittle(value);
            break;
        case NS_BigEndian:
            swappedValue = NSSwapHostFloatToBig(value);
            break;
    }
    OFDataBufferAppendBytes(dataBuffer, (void *)&swappedValue, sizeof(float));
}

static inline void OFDataBufferAppendDouble(OFDataBuffer *dataBuffer, double value)
{
    NSSwappedDouble swappedValue;

    switch (dataBuffer->byteOrder) {
        case NS_UnknownByteOrder:
            swappedValue = NSConvertHostDoubleToSwapped(value);
            break;
        case NS_LittleEndian:
            swappedValue = NSSwapHostDoubleToLittle(value);
            break;
        case NS_BigEndian:
            swappedValue = NSSwapHostDoubleToBig(value);
            break;
    }
    OFDataBufferAppendBytes(dataBuffer, (const void *)&swappedValue, sizeof(double));
}


static inline void
OFDataBufferAppendHexWithReturnsForBytes(OFDataBuffer *dataBuffer, const byte *bytes, unsigned int length)
{
    unsigned int byteIndex;
    
    byteIndex = 0;
    while (byteIndex < length) {
	OFDataBufferAppendHexForByte(dataBuffer, bytes[byteIndex]);
	byteIndex++;
	if ((byteIndex % 40) == 0)
	    OFDataBufferAppendByte(dataBuffer, '\n');
    }
}

 
static inline void
OFDataBufferAppendInteger(OFDataBuffer *dataBuffer, int integer)
{
    int divisor;
    
    if (integer < 0) {
	integer *= -1;
	OFDataBufferAppendByte(dataBuffer, '-');
    }
    
    divisor = log10(integer);
    if (divisor < 0)
	divisor = 0;
    divisor = pow(10, divisor);
    while (1) {
	OFDataBufferAppendByte(dataBuffer, (integer / divisor) + '0');
	if (divisor <= 1)
	    break;
	integer %= divisor;
	divisor /= 10;
    }
}
 
static inline void
OFDataBufferAppendData(OFDataBuffer *dataBuffer, NSData *data)
{
    OFDataBufferAppendBytes(dataBuffer, (const byte *)[data bytes], [data length]);
}

static inline void
OFDataBufferAppendHexWithReturnsForData(OFDataBuffer *dataBuffer, NSData *data)
{
    OFDataBufferAppendHexWithReturnsForBytes(dataBuffer, (const byte *)[data bytes], [data length]);
}

static inline void
OFDataBufferAppendString(OFDataBuffer *dataBuffer, NSString *string)
{
    OFDataBufferAppendCString(dataBuffer, [string cString]);
}

static inline void
OFDataBufferAppendUnicodeString(OFDataBuffer *dataBuffer, NSString *string)
{
    OFStringStartLoopThroughCharacters(string, ch) {
        OFDataBufferAppendUnichar(dataBuffer, ch);
    } OFStringEndLoopThroughCharacters;
}
