/* MiscMutableData.m created by giddings on Tue 04-Feb-1997 */

//Written By Michael Giddings, 1996 and 1997
//giddings@barbarian.com
//giddings@whitewater.chem.wisc.edu

//MiscMutableData
//The purpose of this "subclass" of MiscMutableData is to handle some
//byte order swapping when using and NSMutableData to store arrays of
//Floats, Doubles, and Ints.

//The primary thing to remember about using this class is that you must
//tell it what type to use when you create it, otherwise it can't really
//know how to change byte order when appropriate.  You set this by sending
//it a -setType message, with a single character type code of i (int), 
//d (double-float), f (float), l (long int)

//This class may also be useful because it demonstrates (somewhat) how
//to effectively subclass an abstract class cluster, which is something
//I had difficulty getting to work at first.  That is the one downside
//of class clusters.  Anyway, for someone wishing to create their own
//subclass of NSMutableData, this gets you a substantial way there.


#import "MiscMutableData.h"

@implementation MiscMutableData

+ (id)allocWithZone:(NSZone *)zone
{
    return [super allocWithZone:zone];
}

+ (MiscMutableData *)data
{
    return [[[self alloc] init] autorelease];
}

+ (MiscMutableData *)dataWithBytes:(const void *)bytes length:(unsigned int)length
{
    return [[[self alloc] initWithBytes:bytes length:length] autorelease];
}

+ (MiscMutableData *)dataWithBytesNoCopy:(void *)bytes length:(unsigned int)length
{
    return [[[self alloc] initWithBytesNoCopy:bytes length:length] autorelease];
}

+ (MiscMutableData *)dataWithContentsOfFile:(NSString *)path
{
    return [[[MiscMutableData alloc] initWithContentsOfFile:path] autorelease];
}

+ (MiscMutableData *)dataWithContentsOfMappedFile:(NSString *)path
{
    return [[[MiscMutableData alloc] initWithContentsOfMappedFile:path] autorelease];
}

+ (MiscMutableData *)dataWithData:(NSMutableData *)aData
{
    MiscMutableData *temp = [MiscMutableData alloc];
    return ([[temp initWithData:aData] autorelease]);
}


+ (MiscMutableData *)dataWithCapacity:(unsigned int)aNumItems
{
    return [[[MiscMutableData alloc] initWithCapacity:aNumItems] autorelease];
}

+ (MiscMutableData *)dataWithLength:(unsigned int)length;
{
    return [[[MiscMutableData alloc] initWithLength:length] autorelease];
}

- (MiscMutableData *)init
{
    type = 0;
    [super init];
    data = [[NSMutableData allocWithZone:[self zone]] init];
    byteOrder = [self hostOrder];
    return self;
}

- (MiscMutableData *)initWithCapacity:(unsigned int)capacity
{
    [super init];
    byteOrder = [self hostOrder];
    data = [[NSMutableData allocWithZone:[self zone]] initWithCapacity:capacity];
    return self;
}

- (MiscMutableData *)initWithLength:(unsigned int)length{
    [super init];
    byteOrder = [self hostOrder];
    data = [[NSMutableData allocWithZone:[self zone]] initWithLength:length];
    return self;
}

- (id)initWithBytes:(const void *)bytes length:(unsigned int)length
{
    type = 0;
    [super init];
    data = [[NSMutableData allocWithZone:[self zone]] initWithBytes:bytes length:length];
    byteOrder = [self hostOrder];
    return self;
}

- (id)initWithBytesNoCopy:(void *)bytes length:(unsigned int)length{
    type = 0;
    [super init];
    data = [[NSMutableData allocWithZone:[self zone]] initWithBytesNoCopy:bytes length:length];
    byteOrder = [self hostOrder];
    return self;
}

- (id)initWithContentsOfFile:(NSString *)path
{
    type = 0;
    [super init];
    data = [[NSMutableData allocWithZone:[self zone]] initWithContentsOfFile:path];
    byteOrder = [self hostOrder];
    return self;
}

- (id)initWithContentsOfMappedFile:(NSString *)path
{
    type = 0;
    [super init];
    data = [[NSMutableData allocWithZone:[self zone]] initWithContentsOfMappedFile:path];
    byteOrder = [self hostOrder];
    return self;
}

- (MiscMutableData *)initWithData:(NSMutableData *)thedata
{
    type = 0;
    [super init];
    data = [thedata mutableCopy];
//[NSMutableData allocWithZone:[self zone]] initWithData:thedata];
    byteOrder = [self hostOrder];
    return self;
}


- (id)copyWithZone:(NSZone *)zone
{
    return [self retain];
}

- (id)mutableCopyWithZone:(NSZone *)zone
{
    MiscMutableData *copy = [MiscMutableData allocWithZone:zone];
    [copy initWithData:data];

    [copy setByteOrder:[self byteOrder]];
    [copy setType:[self type]];
    return copy;
}


- (id)setType:(unsigned char)thetype
{
    if ((thetype == 'i') || (thetype == 'l') || (thetype == 'd') || (thetype == 'f'))
        type = thetype;
    return self;
}

-(unsigned char)type
{
    return type;
}

- replacementObjectForPortCoder:(NSPortCoder *)encoder
{
    if ([encoder isBycopy])
        return self;
    else
        return [super replacementObjectForPortCoder:encoder];
}

- (void)setByteOrder:(char unsigned)order
{
    byteOrder=order;
}

-(NSMutableData *)data
{
    return data;
}

- (unsigned char)byteOrder
{
    return byteOrder;
}

- (void)dealloc
{
    fprintf(stderr, "Freeing myself (MiscMutableData)\n");
    if (data != NULL)
        [data release];
    [super dealloc];
}

- (unsigned char)hostOrder
{
    #ifdef __BIG_ENDIAN__
    return MISC_BIGENDIAN;
    #else
        return MISC_LITTLEENDIAN;
    #endif
}


- (BOOL)respondsToSelector:(SEL)aSelector
{
    if ( [super respondsToSelector:aSelector] )
        return YES;
    else if ([data respondsToSelector:aSelector])
        return YES;
    else
        return NO;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
    NSMethodSignature *result = [super methodSignatureForSelector:sel];

    if (nil == result)
        result = [data methodSignatureForSelector:sel];
    return result;
}


- (void)forwardInvocation:(NSInvocation *)invocation
{
    if ([data respondsToSelector:[invocation selector]])
        [invocation invokeWithTarget:data];
    else
      {
        [self doesNotRecognizeSelector:[invocation selector]];
        fprintf(stderr, "Cannot forward message, selector not recognized.\n");
      }

}


- (unsigned int)length
{
    return [data length];
}

- (const void *)bytes
{
    return [data bytes];
}

- (void *)mutableBytes
{
    return [data mutableBytes];
}

- (void)setLength:(unsigned int)length
{
    [data setLength:length];
}




- (void)swapDoubleToHost
{
    unsigned char curHostOrder = [self hostOrder];

    if (byteOrder != curHostOrder) {
        NSSwappedDouble *temp1 = (NSSwappedDouble *)[data bytes];
        double *temp = (double *)[data mutableBytes];
        unsigned i, count = ([data length] / sizeof(double));

        if (byteOrder == MISC_BIGENDIAN) {
            for (i = 0; i < count; i++)
                temp[i] = NSSwapBigDoubleToHost(temp1[i]);
            [self setByteOrder:MISC_LITTLEENDIAN];
            }
        else
          {
            for (i = 0; i < count; i++)
                temp[i] = NSSwapLittleDoubleToHost(temp1[i]);
            [self setByteOrder:MISC_LITTLEENDIAN];
          }
    }
}

- (void)swapFloatToHost
{
    unsigned char curHostOrder = [self hostOrder];

    if (byteOrder != curHostOrder) {
        NSSwappedFloat *temp1 = (NSSwappedFloat *)[data bytes];
        float *temp = (float *)[data mutableBytes];
        unsigned i, count = ([data length] / sizeof(float));

        if (byteOrder == MISC_BIGENDIAN) {
            for (i = 0; i < count; i++)
                temp[i] = NSSwapBigFloatToHost(temp1[i]);
            [self setByteOrder:MISC_LITTLEENDIAN];
            }
        else
          {
            for (i = 0; i < count; i++)
                temp[i] = NSSwapLittleFloatToHost(temp1[i]);
            [self setByteOrder:MISC_LITTLEENDIAN];
          }
    }
}

- (void)swapLongToHost
{
    unsigned char curHostOrder = [self hostOrder];

    if (byteOrder != curHostOrder) {
        long *temp = (long *)[data mutableBytes];
        unsigned i, count = ([data length] / sizeof(long));

        if (byteOrder == MISC_BIGENDIAN) {
            for (i = 0; i < count; i++)
                temp[i] = NSSwapBigLongToHost(temp[i]);
            [self setByteOrder:MISC_LITTLEENDIAN];
            }
        else
          {
            for (i = 0; i < count; i++)
                temp[i] = NSSwapLittleLongToHost(temp[i]);
            [self setByteOrder:MISC_LITTLEENDIAN];
          }
    }
}
- (void)swapIntToHost
{
    unsigned char curHostOrder = [self hostOrder];

    if (byteOrder != curHostOrder) {
        int *temp = (int *)[data mutableBytes];
        unsigned i, count = ([data length] / sizeof(int));

        if (byteOrder == MISC_BIGENDIAN) {
            for (i = 0; i < count; i++)
                temp[i] = NSSwapBigIntToHost(temp[i]);
            [self setByteOrder:MISC_LITTLEENDIAN];
            }
        else
          {
            for (i = 0; i < count; i++)
                temp[i] = NSSwapLittleIntToHost(temp[i]);
            [self setByteOrder:MISC_LITTLEENDIAN];
          }
    }
}

- (void)swapShortToHost
{
    unsigned char curHostOrder = [self hostOrder];

    if (byteOrder != curHostOrder) {
        short *temp = (short *)[data mutableBytes];
        unsigned i, count = ([data length] / sizeof(short));

        if (byteOrder == MISC_BIGENDIAN) {
            for (i = 0; i < count; i++)
                temp[i] = NSSwapBigShortToHost(temp[i]);
            [self setByteOrder:MISC_LITTLEENDIAN];
            }
        else
          {
            for (i = 0; i < count; i++)
                temp[i] = NSSwapLittleShortToHost(temp[i]);
            [self setByteOrder:MISC_LITTLEENDIAN];
          }
    }
}

- (void)swapToHost{
    if (type) {
        switch (type) {
            case 'i' : [self swapIntToHost];
                break;
            case 'l' : [self swapLongToHost];
                break;
            case 's' : [self swapShortToHost];
                break;
            case 'f' : [self swapFloatToHost];
                break;
            case 'd' : [self swapDoubleToHost];
                break;
        }
    }
}

- (void)encodeWithCoder:(NSCoder *)coder
{
    [coder encodeValueOfObjCType:@encode(unsigned char) at:&byteOrder];
    [coder encodeValueOfObjCType:@encode(unsigned char) at:&type];
    [coder encodeDataObject:data];
}

- (id)initWithCoder:(NSCoder *)coder
{
    [coder decodeValueOfObjCType:@encode(unsigned char) at:&byteOrder];
    [coder decodeValueOfObjCType:@encode(unsigned char) at:&type];
    data = [[NSMutableData dataWithData:[coder decodeDataObject]] retain];
    [self swapToHost];
    return self;
}
@end
