// Copyright 1997-2002 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.

#import <OWF/OWDataStreamCursor.h>

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>
#import <OmniFoundation/OmniFoundation.h>

#import "OWDataStream.h"
#import "OWDataStreamCharacterCursor.h"

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OWF/Content.subproj/OWDataStreamCursor.m,v 1.23 2002/03/09 01:53:49 kc Exp $")

@implementation OWDataStreamCursor

NSException *OWDataStreamCursor_UnderflowException;
// NSException *OWDataStreamCursor_EndOfDataException;  // never used --wim

+ (void)initialize;
{
    OBINITIALIZE;

    OWDataStreamCursor_UnderflowException = [[NSException alloc] initWithName:@"Underflow" reason:@"Attempted read off end of buffer" userInfo:nil];
    // OWDataStreamCursor_EndOfDataException = [[NSException alloc] initWithName:@"Out of data" reason:@"More data required, but no more data available" userInfo:nil];   // never used anymore --wim
}

// Init and dealloc

- initForDataStream:(OWDataStream *)aStream;
{
    if (![super init])
	return nil;

    dataStream = [aStream retain];
    byteOrder = NS_UnknownByteOrder;
    dataOffset = 0;

    return self;
}

- initFromCursor:(id)aCursor;
{
    return [self initForDataStream:[aCursor dataStream]];
}

- (void)dealloc;
{
    [dataStream release];
    [super dealloc];
}

//

- (OWDataStream *)dataStream;
{
    return dataStream;
}

//

- (void)setByteOrder:(OFByteOrder)newByteOrder;
{
    byteOrder = newByteOrder;
}

// These macros make the data access functions use the same code base without being deadly slow

static inline void _getBytes(OWDataStreamCursor *self, void *buffer, unsigned int count)
{
    if (self->abortException)
        [self->abortException raise];
    if (![self->dataStream getBytes:buffer range:(NSRange){self->dataOffset, count}])
        [OWDataStreamCursor_UnderflowException raise];
    self->bitsLeft = 0;
}

static inline void _ensureBytesAvailable(OWDataStreamCursor *self, unsigned int count)
{
    if (self->abortException)
        [self->abortException raise];
    if (![self->dataStream waitForBufferedDataLength:self->dataOffset + count])
        [OWDataStreamCursor_UnderflowException raise];
}

static inline NSData *_getData(OWDataStreamCursor *self, unsigned int count)
{
    NSData *result;
    
    if (self->abortException)
        [self->abortException raise];
    if (!(result = [self->dataStream dataWithRange:(NSRange){self->dataOffset, count}]))
        [OWDataStreamCursor_UnderflowException raise];
    return result;
}

static inline NSData *_getBufferedData(OWDataStreamCursor *self, BOOL incrementOffset)
{
    unsigned int count;
    NSData *result;

    if (![self->dataStream waitForBufferedDataLength:(self->dataOffset + 1)])
        return nil;
    count = [self->dataStream bufferedDataLength] - self->dataOffset;
    result = [self->dataStream dataWithRange:(NSRange){self->dataOffset, count}];
    if (incrementOffset)
        self->dataOffset += count;
    return result;
}

#define SWAP_BYTES(inputValue, returnValue, readType, swapType)		\
{									\
    switch (byteOrder) {						\
        case NS_UnknownByteOrder:	     				\
            memcpy(&returnValue, &inputValue, sizeof(returnValue));	\
            break;	   						\
        case NS_LittleEndian:						\
            returnValue = NSSwapLittle ## swapType ## ToHost(inputValue); \
            break;     							\
        case NS_BigEndian:     						\
            returnValue = NSSwapBig ## swapType ## ToHost(inputValue);	\
            break;	   						\
    }									\
}

#define READ_DATA_OF_TYPE(readType, swapType)				\
- (readType)read ## swapType;						\
{									\
    OWSwapped ## swapType inputValue;					\
    readType returnValue;						\
									\
    _getBytes(self, &inputValue, sizeof(readType));			\
    SWAP_BYTES(inputValue, returnValue, readType, swapType);		\
    dataOffset += sizeof(readType);					\
    return returnValue;							\
}

#define PEEK_DATA_OF_TYPE(readType, swapType)				\
- (readType)peek ## swapType;						\
{									\
    OWSwapped ## swapType inputValue;					\
    readType returnValue;						\
									\
    _getBytes(self, &inputValue, sizeof(readType));			\
    SWAP_BYTES(inputValue, returnValue, readType, swapType);		\
    return returnValue;							\
}

- (unsigned int)currentOffset;
{
    return dataOffset;
}

- (BOOL)isAtEOF
{
    if ([dataStream knowsDataLength]) {
        return !([dataStream dataLength] > dataOffset);
    } else {
        if ([dataStream bufferedDataLength] > dataOffset)
            return NO;
        if ([dataStream waitForBufferedDataLength:(dataOffset + 1)])
            return NO;
        return YES;
    }
}

- (void)skipBytes:(unsigned int)count;
{
    _ensureBytesAvailable(self, count);
    dataOffset += count;
}

- (void)readBytes:(unsigned int)count intoBuffer:(void *)buffer;
{
    _getBytes(self, buffer, count);
    dataOffset += count;
}

- (void)peekBytes:(unsigned int)count intoBuffer:(void *)buffer;
{
    _getBytes(self, buffer, count);
}

- (void)bufferBytes:(unsigned int)count;
{
    _ensureBytesAvailable(self, count);
}

- (BOOL)haveBufferedBytes:(unsigned int)count
{
    if (abortException)
        [abortException raise];
    
    if ([dataStream bufferedDataLength] >= dataOffset + count)
        return YES;
    else
        return NO;
}

- (unsigned int)readMaximumBytes:(unsigned int)maximum intoBuffer:(void *)buffer;
{
    unsigned int count;

    if (![dataStream waitForBufferedDataLength:dataOffset + 1])
        return 0;
    count = MIN([dataStream bufferedDataLength] - dataOffset, maximum);
    _getBytes(self, buffer, count);
    dataOffset += count;
    return count;
}

- (unsigned int)peekMaximumBytes:(unsigned int)maximum intoBuffer:(void *)buffer;
{
    unsigned int count;

    if (![dataStream waitForBufferedDataLength:dataOffset + 1])
        return 0;
    count = MIN([dataStream bufferedDataLength] - dataOffset, maximum);
    _getBytes(self, buffer, count);
    return count;
}

- (NSData *)readData;
{
    return _getBufferedData(self, YES);
}

- (NSData *)peekData;
{
    return _getBufferedData(self, NO);
}

- (unsigned int)readUnderlyingBuffer:(void **)returnedBufferPtr;    
{
    unsigned int count;

    count = [dataStream accessUnderlyingBuffer:returnedBufferPtr startingAtLocation:dataOffset];
    if (count == 0) {
        if (![dataStream waitForBufferedDataLength:(dataOffset + 1)])
            return 0;
        count = [dataStream accessUnderlyingBuffer:returnedBufferPtr startingAtLocation:dataOffset];
    }
    dataOffset += count;
    return count;
}

- (NSData *)readAllData;
{
    [dataStream waitForDataEnd];
    return _getBufferedData(self, YES);
}

- (NSString *)readAllAsString;
{
    OWDataStreamCharacterCursor *characterCursor;
    NSString *fragment;
    NSMutableString *mutableString;

    characterCursor = [[OWDataStreamCharacterCursor alloc] initForDataCursor:self encoding:[dataStream stringEncoding]];
    mutableString = [[NSMutableString alloc] init];
    while ((fragment = [characterCursor readString]) != nil)
        [mutableString appendString:fragment];
    [characterCursor release];
    return [mutableString autorelease];
}

- (NSData *)readBytes:(unsigned int)count;
{
    NSData *returnData;

    returnData = _getData(self, count);
    dataOffset += count;
    return returnData;
}

- (NSData *)peekBytes:(unsigned int)count;
{
    return _getData(self, count);
}

- (OFByte)readByte;
{									
    OFByte returnValue;
    
    _getBytes(self, &returnValue, 1);
    dataOffset += 1;
    return returnValue;							
}

- (OFByte)peekByte;
{
    OFByte returnValue;

    _getBytes(self, &returnValue, 1);
    return returnValue;							
}

typedef int OWSwappedInt;
typedef short OWSwappedShort;
typedef long OWSwappedLong;
typedef long long OWSwappedLongLong;
typedef NSSwappedFloat OWSwappedFloat;
typedef NSSwappedDouble OWSwappedDouble;

READ_DATA_OF_TYPE(int, Int);
PEEK_DATA_OF_TYPE(int, Int);
READ_DATA_OF_TYPE(short, Short);
PEEK_DATA_OF_TYPE(short, Short);
READ_DATA_OF_TYPE(long, Long);
PEEK_DATA_OF_TYPE(long, Long);
READ_DATA_OF_TYPE(long long, LongLong);
PEEK_DATA_OF_TYPE(long long, LongLong);
READ_DATA_OF_TYPE(float, Float);
PEEK_DATA_OF_TYPE(float, Float);
READ_DATA_OF_TYPE(double, Double);
PEEK_DATA_OF_TYPE(double, Double);

- (unsigned int)readBits:(unsigned int)number;
{
    unsigned int result = 0;

    if (bitsLeft) {
        partialByte &= (1 << bitsLeft) - 1;
        if (number > bitsLeft) {
            number -= bitsLeft;
            result = partialByte << number;
        } else {
            bitsLeft -= number;
            result = partialByte >> bitsLeft;
            number = 0;
        }
    }
    while (number) {
        _getBytes(self, &partialByte, 1);
        dataOffset += 1;
        if (number <= 8) {
            bitsLeft = 8 - number;
            result |= (partialByte >> bitsLeft);
            number = 0;
        } else {
            number -= 8;
            result |= (partialByte << number);
        }
    }
    return result;
}

- (int)readSignedBits:(unsigned int)number;
{
    int result = (int)[self readBits:number];

    if (result & (1 << (number-1)))
        result |= (-1 << number);
    return result;
}

- (void)skipToNextFullByte;
{
    bitsLeft = 0;
}

- (unsigned)scanUpToByte:(OFByte)byteMatch
{
    unsigned int scanOffset = dataOffset;
    
    while (![self isAtEOF]) {
        OFByte *buffer;
        void *fetch;
        unsigned int bufferSize;
        
        bufferSize = [dataStream accessUnderlyingBuffer:&fetch startingAtLocation:scanOffset];
        buffer = fetch;
        
        while (bufferSize > 0) {
            if(*buffer == byteMatch) {
                unsigned int bytesSkipped = scanOffset - dataOffset;
                [self skipBytes:bytesSkipped];
                return bytesSkipped;
            }
            bufferSize --;
            buffer ++;
            scanOffset ++;
        }
        
        if (![dataStream waitForBufferedDataLength:(scanOffset + 1)])
            break;
    }
    
    /* byte not found */
    [OWDataStreamCursor_UnderflowException raise];
    /* NOTREACHED */
    return 0;
}

// OWCursor subclass

- (unsigned int)seekToOffset:(int)offset fromPosition:(OWCursorSeekPosition)position;
{
    switch (position) {
        case OWCursorSeekFromEnd:
            offset = [dataStream dataLength] + offset - dataOffset;
            break;
        case OWCursorSeekFromCurrent:
            break;
        case OWCursorSeekFromStart:
            offset = offset - dataOffset;
            break;
    }
    if (offset > 0)
        [self skipBytes:offset];
    else
        dataOffset += offset;
    return dataOffset;    
}

// OWContent protocol

- (OWContentType *)contentType;
{
    return [dataStream contentType];
}

- (unsigned long int)cacheSize;
{
    return [(id <OWOptionalContent>)dataStream cacheSize];
}

- (BOOL)contentIsValid;
{
    return [(id <OWOptionalContent>)dataStream contentIsValid];
}

// Debugging

- (NSMutableDictionary *)debugDictionary;
{
    NSMutableDictionary *debugDictionary;

    debugDictionary = [super debugDictionary];
    if (dataStream)
	[debugDictionary setObject:dataStream forKey:@"dataStream"];
    [debugDictionary setObject:[NSNumber numberWithInt:dataOffset] forKey:@"dataOffset"];

    return debugDictionary;
}


@end
