// Server/Client for vending an object either through a NSMessagePort or NSSocketPort

#import <Foundation/Foundation.h>
#import "Protocol.h"

// configure ------

/* use command line arguments
 * -NSLoopback
 * -NSSendback
 * -NSSocket
 * -NSConnection
 * -NSClient
*/

BOOL loopback=NO;	// use a single socket for sending and receiving, otherwise compile as Zaurus-Server and Mac/Zaurus-Client
BOOL sendback=NO;	// send the received portmessage back
BOOL useSocket=NO;	// if YES, use NSSocketPort, if NO, use NSMessagePort
BOOL useConnectionForClient=NO;	// connect to the server through an NSConnection
#ifdef __mySTEP__
BOOL client=NO;		/* make Zaurus a Server by default */
#else
BOOL client=YES;	/* make Mac a Client by default */
#endif

// end configure ------

@interface MyClass : NSObject <MyServer>
@end

@implementation MyClass

- (NSString *) method;
{
	NSLog(@"client calls -[MyClass method]");
	return @"this is the instance of MyClass";
}

- (bycopy NSString *) method2:(byref NSString *) arg;
{
	NSLog(@"client calls -[MyClass method2:%@]", arg);
	NSLog(@"Arg class:%@", NSStringFromClass([arg class]));	// should be a distant object
	NSLog(@"Arg connection:%@", [(NSDistantObject *) arg connectionForProxy]);	// should be a distant object
	NSLog(@"All connections:%@", [NSConnection allConnections]);
	NSLog(@"Conversation:%@", [NSConnection currentConversation]);
	return @"result bycopy";
}

@end

@interface PortDelegate : NSObject
// - (void)handlePortMessage:(NSPortMessage *)portMessage;
@end

@implementation PortDelegate

- (void) invalidated:(NSNotification *) n
{
	NSLog(@"invalidated NSPort: %@", [n object]);
}

#if 0

- (void) handleMachMessage:(void *) msg;
{
	NSLog(@"received MachMessage: %@", [NSData dataWithBytes:msg length:64]);
}

#else

- (void) handlePortMessage:(NSPortMessage *)portMessage;
{
	static int cnt;
	BOOL b;
	NSSocketPort *s;
//	NSLog(@"runloop: %@", [NSRunLoop currentRunLoop]);
	NSLog(@"received NSPortMessage: %@", portMessage);
	NSLog(@"  runloop mode: %@", [[NSRunLoop currentRunLoop] currentMode]);
	NSLog(@"  msgid: %u", [portMessage msgid]);
	s=[portMessage sendPort];
	NSLog(@"  send: %p, %@, %@", s, s, NSStringFromClass([s class]));
		  // is the port we should send the response to - this is DIFFERENT from the port created in main()
	if([s respondsToSelector:@selector(address)])
		{
		NSLog(@"    address=%@", [s address]);
		NSLog(@"    protocol=%d", [s protocol]);
		NSLog(@"    protocolFamily=%d", [s protocolFamily]);
		NSLog(@"    socket=%d", [s socket]);
		NSLog(@"    socketType=%d", [s socketType]);
		}
	NSLog(@"    delegate=%@", [s delegate]);
	NSLog(@"    reservedSpaceLength=%d", [s reservedSpaceLength]);
	s=[portMessage receivePort];
	NSLog(@"  recv: %p, %@", s, s);	// is the originating port
	if([s respondsToSelector:@selector(address)])
		{
		NSLog(@"    address=%@", [s address]);
		NSLog(@"    protocol=%d", [s protocol]);
		NSLog(@"    protocolFamily=%d", [s protocolFamily]);
		NSLog(@"    socket=%d", [s socket]);
		NSLog(@"    socketType=%d", [s socketType]);
		}
	NSLog(@"    delegate=%@", [s delegate]);
	NSLog(@"    reservedSpaceLength=%d", [s reservedSpaceLength]);
	NSLog(@"  comp: %@", [portMessage components]);
	[(NSMutableArray *) [portMessage components] addObject:[@"3" dataUsingEncoding:NSUTF8StringEncoding]];	// mutable - yes
	if(sendback && cnt++ < 3)
		{
		b=[portMessage sendBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];	// send back
		if(!b)
			NSLog(@"send failed");
		}
	else
		NSLog(@"no more replys");
}

#endif

@end

int main(int argc, const char *argv[])
{
	NSAutoreleasePool *arp=[NSAutoreleasePool new];
	NSPort *send;
	NSPort *recv;
	NSLog(@"config=%@", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);
	loopback=[[NSUserDefaults standardUserDefaults] boolForKey:@"NSLoopback"];	// set by -NSLoopback
	sendback=[[NSUserDefaults standardUserDefaults] boolForKey:@"NSSendback"];	// send the received portmessage back
	useSocket=[[NSUserDefaults standardUserDefaults] boolForKey:@"NSSocket"];	// if YES, use NSSocketPort, if NO, use NSMessagePort
	useConnectionForClient=[[NSUserDefaults standardUserDefaults] boolForKey:@"NSConnection"];	// connect to the server through an NSConnection
	client=[[NSUserDefaults standardUserDefaults] boolForKey:@"NSClient"];	// server otherwise
#if 0
	{
		PortClass *p=[[PortClass new] autorelease];	// create new port
		NSConnection *c=[NSConnection connectionWithReceivePort:p sendPort:p];
		NSLog(@"started");
		NSLog(@"connection %@", c);
		[c setRootObject:[MyClass new]];	// vend some object
		NSLog(@"root object: %@", [c rootObject]);
		[c registerName:@"fexpserver" withNameServer:[NameServerClass sharedInstance]];
		NSLog(@"registered as fexpserver");
		NSLog(@"connections %@", [NSConnection allConnections]);
	}
#endif
	if(useSocket)
		{
		if(loopback)
			{
			recv=send=[[NSSocketPort alloc] initWithTCPPort:4550];	// connect to self
			}
		else if(client)
			{
			send=[[NSSocketPort alloc] initRemoteWithTCPPort:4550 host:@"192.168.129.201"];	// run as client connecting the server
			recv=[[NSSocketPort alloc] initWithTCPPort:4551];	// use a second port to receive from
			}
		else
			{ // server
			recv=send=[[NSSocketPort alloc] initWithTCPPort:4550];	// run as a server and publish
#ifdef __mySTEP__
			// [[NSPortNameServer systemDefaultPortNameServer] registerPort:recv name:@"port"];	// this blocks for 1.0 seconds for socketports (due to ZeroConf publishing)
#endif		
			}
		}
	else
		{
		if(loopback)
			{
			recv=send=[[NSMessagePort alloc] init];
			}
		else if(client)
			{
			send=[[NSMessagePortNameServer sharedInstance] portForName:@"port"];
			// recv=nil - > NSConnection would alloc a second port...
			recv=[[NSMessagePort alloc] init];	// use a second port to receive on
			}
		else
			{ // server
			recv=send=[[NSMessagePort alloc] init];
#ifdef __mySTEP__
			[[NSPortNameServer systemDefaultPortNameServer] registerPort:recv name:@"port"];	// this blocks for 1.0 seconds for socketports (due to ZeroConf publishing)
#endif
			}
		}
	if(!useConnectionForClient)
		{ // otherwise, the NSConnection should do all setups
		[send setDelegate:[PortDelegate new]];
		if(recv != send) [recv setDelegate:[send delegate]];	// make same
		[[NSNotificationCenter defaultCenter] addObserver:[send delegate] selector:@selector(invalidated:) name:NSPortDidBecomeInvalidNotification object:nil];
		}
	[recv scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];	// listen on otherport for incoming connection and data
	NSLog(@"send=%@ %p", send, send);
	if([send respondsToSelector:@selector(address)])
		{
		NSLog(@" address=%@", [send address]);
		NSLog(@" protocol=%d", [send protocol]);
		NSLog(@" protocolFamily=%d", [send protocolFamily]);
		NSLog(@" socket=%d", [send socket]);
		NSLog(@" socketType=%d", [send socketType]);
		}
	NSLog(@" delegate=%@", [send delegate]);
	NSLog(@" reservedSpaceLength=%d", [send reservedSpaceLength]);
	NSLog(@"recv=%@ %p", recv, recv);
	if([recv respondsToSelector:@selector(address)])
		{
		NSLog(@" address=%@", [recv address]);
		NSLog(@" protocol=%d", [recv protocol]);
		NSLog(@" protocolFamily=%d", [recv protocolFamily]);
		NSLog(@" socket=%d", [recv socket]);
		NSLog(@" socketType=%d", [recv socketType]);
		}
	NSLog(@" delegate=%@", [recv delegate]);
	NSLog(@" reservedSpaceLength=%d", [send reservedSpaceLength]);
#if 0	// to test how arguments are passed to NSInvocation
	NS_DURING
		[send method2:@"with arg"];	// an NSPort does not understand, so this calls forwardInvocation which raises an exception
	NS_HANDLER
	NS_ENDHANDLER
#endif
	if(useConnectionForClient)
		{ // connect through an NSConnection
		NSConnection *c=[NSConnection connectionWithReceivePort:recv sendPort:send];	// create a new connection
		if(client || loopback)
			{
			NSDistantObject <MyServer> *proxy;
			proxy=[c rootProxy];	// this should block until connected and response received
			NSLog(@"proxy=%@", proxy);
			[proxy setProtocolForProxy:@protocol(MyServer)];
			NSLog(@"calling proxy's method=%@", [proxy method]);
			NSLog(@"calling proxy's method2=%@", [proxy method2:@"some string"]);
			}
		else
			{ // server
			[c setRootObject:[MyClass new]];	// object to vend
			}
		}
	else
		{
		if(client || loopback)
			{
			BOOL b=[send sendBeforeDate:[NSDate dateWithTimeIntervalSinceNow:10.0]
								  msgid:12
							 components:[NSMutableArray arrayWithObjects:
								 [@"123" dataUsingEncoding:NSUTF8StringEncoding],
								 [NSData data],	// empty component
								 [@"987654321" dataUsingEncoding:NSUTF8StringEncoding],
								 //						send,
								 //						recv,
								 //	[[NSMessagePort alloc] init],	// sending a NSMessagePort through an NSSocketPort doesn't work on MacOS X
								 nil]
								   from:recv	// must not be nil or it fails!
							   reserved:100];
			if(!b)
				NSLog(@"send failed");
			else
				NSLog(@"did send");
			}
		}
	NSLog(@"running loop in default mode");
	[[NSRunLoop currentRunLoop] run];
	NSLog(@"loop done");
	[arp release];
	exit(0);
}
