// Copyright 1999-2000 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 "ONTCPDatagramSocket.h"

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>
#import "ONTCPSocket.h"

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/Frameworks/OmniNetworking/ONTCPDatagramSocket.m,v 1.4 2000/01/19 23:35:01 kc Exp $")

@implementation ONTCPDatagramSocket

- initWithTCPSocket:(ONSocket *)aSocket;
{
    if (![super init])
        return nil;

    socket = [aSocket retain];

    return self;
}

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

//

- (unsigned int)readBytes:(unsigned int)byteCount intoBuffer:(void *)aBuffer;
{
    unsigned int totalBytesRead, bytesRead, start;
    NSException *exception = nil;
    
    // read the packet length - if the socket raises we already have all the state we need to continue later
    if (readLength < sizeof(unsigned int)) {
        while(readLength < sizeof(unsigned int)) {
            bytesRead = [socket readBytes:(sizeof(unsigned int) - readLength) intoBuffer:((void *)&readPacketLength) + readLength];
            if (!bytesRead)
                return 0;
            readLength += bytesRead;
        }
        readPacketLength = NSSwapBigShortToHost(readPacketLength);
    }

    // check to see that the buffer is big enough for the packet
    if (byteCount < readPacketLength)
        [NSException raise:ONTCPDatagramSocketPacketTooLargeExceptionName format:@"Attempted to read a packet with a buffer that is too small"];

    // read the packet
    start = totalBytesRead = (readLength - sizeof(unsigned int));
    NS_DURING {
        while(totalBytesRead < readPacketLength) {
            bytesRead = [socket readBytes:(readPacketLength - totalBytesRead) intoBuffer:(aBuffer + totalBytesRead)];
            if (!bytesRead)
                break;
            totalBytesRead += bytesRead;
        }
    } NS_HANDLER {
        // if we got a partial packet save it to try more later
        if (totalBytesRead != start) {
            if (!readRemainder)
                readRemainder = NSZoneMalloc(NULL, readPacketLength);
            memcpy(readRemainder + start, aBuffer + start, totalBytesRead - start);
        }
        readLength = totalBytesRead + sizeof(unsigned int);
        exception = localException;
    } NS_ENDHANDLER;

    if (exception)
        [exception raise];
    if (!bytesRead)
        return 0;

    // copy previously saved partial packet into the buffer
    if (readRemainder) {
        memcpy(aBuffer, readRemainder, readLength - sizeof(unsigned int));
        NSZoneFree(NULL, readRemainder);
    }
    readLength = 0;
    return readPacketLength;
}

- (unsigned int)writeBytes:(unsigned int)byteCount fromBuffer:(const void *)aBuffer;
{
    unsigned int bytesWritten, position, packetLength;
    NSException *exception = nil;
    
    // finish writing a previous packet - if this raises we'll continue with the old packet again later, and the one we are called with now is lost
    if (writeRemainder) {
        while (writePosition < writeLength) {
            bytesWritten = [socket writeBytes:(writeLength - writePosition) fromBuffer:(writeRemainder + writePosition)];
            if (!bytesWritten)
                return 0;
            writePosition += bytesWritten;
        }
        NSZoneFree(NULL, writeRemainder);
    }

    // write the length of this packet
    NS_DURING {
        packetLength = NSSwapHostLongToBig(byteCount);
        position = 0;
        while (position < sizeof(unsigned int)) {
            bytesWritten = [socket writeBytes:(sizeof(unsigned int) - position) fromBuffer:((void *)(&packetLength)) + position];
            if (!bytesWritten)
                break;
            position += bytesWritten;
        }
    } NS_HANDLER {
        // didn't finish the length - if we wrote nothing just discard this packet, otherwise save it
        exception = localException;
        if (position) {
            writeRemainder = NSZoneMalloc(NULL, byteCount + sizeof(unsigned int));
            *(int *)writeRemainder = packetLength;
            memcpy(writeRemainder + sizeof(unsigned int), aBuffer, byteCount);
            writeLength = byteCount + sizeof(unsigned int);
            writePosition = position;
        }
    } NS_ENDHANDLER;

    if (exception)
        [exception raise];
    if (!bytesWritten)
        return 0;

    // write the packet data itself
    NS_DURING {
        position = 0;
        while (position < byteCount) {
            bytesWritten = [socket writeBytes:(byteCount - position) fromBuffer:(aBuffer + position)];
            if (!bytesWritten)
                break;
            position += bytesWritten;
        }        
    } NS_HANDLER {
        exception = localException;
        writeLength = byteCount - position;
        writeRemainder = NSZoneMalloc(NULL, writeLength);
        memcpy(writeRemainder, aBuffer + position, writeLength);
        writePosition = 0;
    } NS_ENDHANDLER;

    if (exception)
        [exception raise];
    if (!bytesWritten)
        return 0;
    
    return (unsigned int)bytesWritten;
}

- (void)abortSocket;
{
    [socket abortSocket];
}

- (BOOL)isReadable;
{
    return [socket isReadable];
}

- (BOOL)isWritable;
{
    return [socket isWritable];
}

@end

DEFINE_NSSTRING(ONTCPDatagramSocketPacketTooLargeExceptionName);
