// 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.

#import "ONPortAddress.h"

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

#import "ONHost.h"
#import "ONHostAddress.h"

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/OmniNetworking/ONPortAddress.m,v 1.11 1999/06/08 00:27:57 bungi Exp $")

@implementation ONPortAddress

- initWithHost:(ONHost *)aHost portNumber:(unsigned short int)port;
{
    NSArray *hostAddresses;

    hostAddresses = [aHost addresses];
    if ([hostAddresses count] == 0)
        [NSException raise:ONInternetSocketConnectFailedExceptionName format:@"Unable to create port address: no IP address for host '%@'", [aHost hostname]];
    return [self initWithHostAddress:[hostAddresses objectAtIndex:0] portNumber:port];
}

- initWithHostAddress:(ONHostAddress *)hostAddress portNumber:(unsigned short int)port;
{
    if (![super init])
        return nil;

    // Make sure this starts as zeros so that the memcmp in -isEqual will work.
    portAddress = (struct sockaddr_in *)NSZoneCalloc(NULL, 1, sizeof(struct sockaddr_in));
    
    portAddress->sin_family = AF_INET;
    if (hostAddress)
	portAddress->sin_addr = *[hostAddress internetAddress];
    else
	portAddress->sin_addr.s_addr = htonl(INADDR_ANY);
    portAddress->sin_port = htons(port);

    return self;
}

- (void)dealloc;
{
    NSZoneFree(NULL, portAddress);
    [super dealloc];
}

- (const struct sockaddr_in *)portAddress;
{
    return portAddress;
}

- (const struct in_addr *)hostAddress;
{
    return &(portAddress->sin_addr);
}

- (unsigned long int)hostNumber;
{
    return ntohl(portAddress->sin_addr.s_addr);
}

- (unsigned short int)portNumber;
{
    return ntohs(portAddress->sin_port);
}

- (BOOL)isMulticastAddress;
{
    // IN_MULTICAST() expects its argument to be in host byte order, while the portAddress keeps its parameters in network byte order
    return IN_MULTICAST(ntohl(portAddress->sin_addr.s_addr));
}

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

    dict = [super debugDictionary];
    [dict setObject: [NSString stringWithCString: inet_ntoa(portAddress->sin_addr)] forKey: @"hostAddress"];
    [dict setObject: [NSNumber numberWithShort: ntohs(portAddress->sin_port)] forKey: @"portNumber"];

    return dict;
}

//
// NSObject methods
//

- (BOOL) isEqual: (id) otherObject;
{
    if (!otherObject)
        return NO;

    if (!OBClassIsSubclassOfClass(*(Class *)otherObject, isa))
        return NO;

    return memcmp(portAddress, ((ONPortAddress *)otherObject)->portAddress, sizeof(*portAddress)) == 0;
}

//
// NSCoding protocol
//

#define ONEncode(coder, var) [coder encodeValueOfObjCType:@encode(typeof(var)) at:&(var)];
#define ONDecode(coder, var) [coder decodeValueOfObjCType:@encode(typeof(var)) at:&(var)];

- (id)initWithCoder:(NSCoder *)coder;
{
    if (![super init])
        return nil;

    // Make sure this starts as zeros so that the memcmp in -isEqual will work.
    portAddress = (struct sockaddr_in *)NSZoneCalloc(NULL, 1, sizeof(struct sockaddr_in));

    portAddress->sin_family = AF_INET;

    // These two fields are always stored in network byte order internally, so we don't need to swap them.
    ONDecode(coder, portAddress->sin_addr.s_addr);
    ONDecode(coder, portAddress->sin_port);

    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder;
{
    // These two fields are always stored in network byte order internally, so we don't need to swap them.
    ONEncode(coder, portAddress->sin_addr.s_addr);
    ONEncode(coder, portAddress->sin_port);
}

// Make sure we go bycopy or byref as appropriate
- (id) replacementObjectForPortCoder: (NSPortCoder *) encoder;
{
    if ([encoder isByref])
        return [NSDistantObject proxyWithLocal: self connection: [encoder connection]];
    else
        return self;
}

@end
