//
//  CyTVClient.m
//  CyTV Client
//
//  Created by Andreas Junghans on Thu Aug 21 2003.
//  Copyright (c) 2003-2004 Andreas Junghans. All rights reserved.
//

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


/**
 * Implementation of the browser facade to be used by AppleScript.
 * This is also used as the delegate for NSNetServiceBrowser.
 */

@implementation BrowserFacade

- 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];
    [super dealloc];
}

- (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");
    _searchingFinished = TRUE;
}

- (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");
    _searchingFinished = TRUE;
}

- (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) {
            _searchingFinished = TRUE;
        }
    } 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]);
	struct sockaddr *socketAddress = (struct sockaddr *)
		[[addresses objectAtIndex:
		  [addresses count]-1] bytes];
	if (socketAddress && socketAddress->sa_family == AF_INET) {
		[netService stop];
		if (--_toResolveUntilFinished == 0) {
			_searchingFinished = TRUE;
		}
        printf("found net service: %.8X\n", (int) netService);
        printf("found address: %s\n", [[[NSApplication sharedApplication] 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]);
	} else {
		printf("got ipv6\n");
	}
}

- (void)netService:(NSNetService *)netService
        didNotResolve:(NSDictionary *)errorDict {
    printf("didNotResolve: %s\n", [[errorDict description] cString]);
    [netService stop];
    [_services removeObject: netService];
    if (--_toResolveUntilFinished == 0) {
        _searchingFinished = TRUE;
    }
}

@end


/**
 * Implementation of the interface available to the AppleScript parts.
 */

@implementation NSApplication (CyTVClientInterface)

- (BrowserFacade *) startBrowsing {
    // starts browsing for Rendezvous services
    return [[BrowserFacade alloc] init];
}

- (NSString *) getAddressOfService: (NSNetService *) netService {
    // returns the address of a service as string
    // (format: <host>:<port>)
    NSArray* addresses = [netService addresses];
    if ([addresses count] == 0) {
        return @"";
    }
    struct sockaddr_in *address;
    address = (struct sockaddr_in *) [[addresses objectAtIndex:[addresses count]-1] bytes];
    //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] retain];
}

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

- (BOOL)hasPreference:(NSString*) key {
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    if ([defaults objectForKey:key] == nil) {
        return FALSE;
    }
    return TRUE;
}

- (NSString *) loadPreference: (NSString*) key {
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    NSString* retVal = [defaults stringForKey: key];
    if (retVal == nil) {
        return @"";
    }
    return [retVal retain];
}

- (void) savePreference: (NSString*) key value: (NSString*) value {
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject: value forKey: key];
    [defaults synchronize];
}

- (int)loadIntPreference:(NSString*)key {
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    return [defaults integerForKey:key];
}

- (void)saveIntPreference:(NSString*)key value:(int)value {
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    [defaults setInteger:value forKey:key];
    [defaults synchronize];
}

- (float)loadFloatPreference:(NSString*)key {
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    return [defaults floatForKey:key];
}

- (void)saveFloatPreference:(NSString*)key value:(float)value {
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    [defaults setFloat:value forKey:key];
    [defaults synchronize];
}

- (int)findPortStartingWith:(int)startPort {
    return [Util findPortStartingWith:startPort];
}

- (int)findPortStartingWith:(int)startPort excluding:(int)excludedPort {
    return [Util findPortStartingWith:startPort excluding:excludedPort];
}

- (NSString*)getCyTVLogPath {
    return [[Util getCyTVLogPath] retain];
}

- (NSString*)getCyTVSupportPath {
    return [[Util getCyTVSupportPath] retain];
}

- (void)setToMinSizeWithoutAnimation:(NSWindow*)window {
    NSRect frame = [window frame];
    frame.origin.y += [window maxSize].height - [window minSize].height;
    frame.size = [window minSize];
    // VINCENT
    [window setFrame:frame display:NO animate:NO];
}

@end
