//
//  RendezvousClient.m
//  CyTV Client
//
//  Created by Andreas Junghans on 13.05.05.
//  Copyright 2005 __MyCompanyName__. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <sys/socket.h>
#import <netdb.h>
#import "RendezvousClient.h"

@implementation RendezvousClient

- init {
    if (self = [super init]) {
        _searchingFinished = FALSE;
        _toResolveUntilFinished = 1;
        _services = [[NSMutableArray alloc] init];
        _servicesDict = [[NSMutableDictionary alloc] init];
        _browser = [[NSNetServiceBrowser alloc] init];
        [_browser setDelegate: self];
        [_browser searchForServicesOfType: @"_cytv._tcp" inDomain: @""];
        return self;
    }
    return nil;
}

- (void) dealloc {
    [_browser release];
    [_services release];
    [_servicesDict release];
    [_delegate release];
    [super dealloc];
}

- (void)setDelegate:(id)delegate {
    [_delegate autorelease];
    _delegate = [delegate retain];
}

- (NSArray *) resultsOfBrowsing {
    if (!_searchingFinished) {
        return nil;
    }
    return _services;
}

- (void) stopBrowsing {
    [_browser stop];
}

- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser {
    // called when searching starts - ignored
    printf("willSearch\n");
}

- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser {
    // called when searching was stopped
    printf("didStopSearch\n");
    BOOL searchingFinished = _searchingFinished;
    _searchingFinished = TRUE;
    if (_delegate != nil && !searchingFinished) {
        [_delegate browsingFinished:[self resultsOfBrowsing]];
    }
}

- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
        didNotSearch:(NSDictionary *)errorDict {
    // called when searching failed - for now, treated the
    // same way as when no service can be found
    printf("didNotSearch\n");
    BOOL searchingFinished = _searchingFinished;
    _searchingFinished = TRUE;
    if (_delegate != nil && !searchingFinished) {
        [_delegate browsingFinished:[self resultsOfBrowsing]];
    }
}

- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
        didFindService:(NSNetService *)aNetService
        moreComing:(BOOL)moreComing {
    // called when a service was found
    printf("didFindService\n");
    NSString *serviceName = [aNetService name];
    if ([_servicesDict objectForKey:serviceName] == nil) {
        printf(" serviceName is nil\n");
        [_servicesDict setObject:aNetService forKey:serviceName];
        [_services addObject: aNetService];
        ++_toResolveUntilFinished;
        [aNetService setDelegate: self];
        [aNetService resolve];
    } else {
        printf(" serviceName is not nil\n");
    }
    if (!moreComing) {
        printf(" !moreComing\n");
        //[browser stop];
        if (--_toResolveUntilFinished == 0) {
            BOOL searchingFinished = _searchingFinished;
            _searchingFinished = TRUE;
            if (_delegate != nil && !searchingFinished) {
                [_delegate browsingFinished:[self resultsOfBrowsing]];
            }
        }
    } else {
        printf(" moreComing\n");
    }
}

- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
        didRemoveService:(NSNetService *)aNetService
        moreComing:(BOOL)moreComing {
    // called when a service was removed - ignored for now
    printf("didRemoveService\n");
}

// NSNetService delegate methods
- (void)netServiceDidResolveAddress:(NSNetService *)netService {
	NSArray* addresses = [netService addresses];
    printf("netServiceDidResolveAddress; count: %d\n", [addresses count]);
    if ([RendezvousClient getAddressOfService:netService] != nil) {
        [netService stop];
        if (--_toResolveUntilFinished == 0) {
            BOOL searchingFinished = _searchingFinished;
            _searchingFinished = TRUE;
            if (_delegate != nil && !searchingFinished) {
                [_delegate browsingFinished:[self resultsOfBrowsing]];
            }
        }
        printf("found net service: %.8X\n", (int) netService);
        printf("found address: %s\n", [[RendezvousClient getAddressOfService:netService] cString]);
        printf("found domain: %s\n", [[netService domain] cString]);
        printf("found name: %s\n", [[netService name] cString]);
        printf("found type: %s\n", [[netService type] cString]);
    }
}

- (void)netService:(NSNetService *)netService
        didNotResolve:(NSDictionary *)errorDict {
    printf("didNotResolve: %s\n", [[errorDict description] cString]);
    [netService stop];
    [_services removeObject: netService];
    if (--_toResolveUntilFinished == 0) {
        BOOL searchingFinished = _searchingFinished;
        _searchingFinished = TRUE;
        if (_delegate != nil && !searchingFinished) {
            [_delegate browsingFinished:[self resultsOfBrowsing]];
        }
    }
}

+ (NSString *)getAddressOfService:(NSNetService *)netService {
	NSArray* addresses = [netService addresses];
    int i;
    for (i = 0; i < [addresses count]; ++i) {
        struct sockaddr *socketAddress = (struct sockaddr *)
            [[addresses objectAtIndex:i] bytes];
        if (socketAddress && socketAddress->sa_family == AF_INET) {
            struct sockaddr_in *address;
            address = (struct sockaddr_in *) socketAddress;
            //struct hostent *host = gethostbyaddr((char*)&address->sin_addr.s_addr,
            //                                     sizeof address->sin_addr.s_addr, AF_INET);
            //return [NSString stringWithCString: (host ? host->h_name : inet_ntoa(address->sin_addr))];
            // commented out: we don't want to establish an internet connection just for a fancy name
            return [NSString stringWithFormat:@"%s:%hu",
                    inet_ntoa(address->sin_addr),
                    address->sin_port];
        }
    }
    return nil;
}

+ (NSString *)getDisplayNameOfService:(NSNetService *)netService {
    // returns a name to display for the service
    return [netService name];
}

@end
