#import <Foundation/Foundation.h>

@interface InvokedObject : NSObject

- (void) noarg;
- (void) chararg:(char) arg;
- (void) shortarg:(short) arg;
- (void) intarg:(int) arg;
- (void) doublearg:(double) arg;
- (void) idarg:(id) arg;
- (void) idarg:(id) arg idarg:(id) arg2;
- (void) indirect;

@end

@implementation InvokedObject

- (void) noarg;
{
	NSLog(@"noarg");
}

- (void) intarg:(int) arg;
{
	NSLog(@"intarg:%d", arg);
}

- (void) chararg:(char) arg;
{
	NSLog(@"chararg:%d", arg);
}

- (void) shortarg:(short) arg;
{
	NSLog(@"shortarg:%d", arg);
}

- (void) doublearg:(double) arg;
{
	NSLog(@"doublearg:%lf", arg);
}

- (void) idarg:(id) arg;
{
	NSLog(@"idarg:%@", arg);
}

- (void) idarg:(id) arg idarg:(id) arg2;
{
	int i;
	NSLog(@"&self=%p", &self);
	NSLog(@"&_cmd=%p", &_cmd);
	NSLog(@"&arg=%p", &arg);
	NSLog(@"&arg2=%p", &arg2);
	for(i=-24; i<48; i++)
		{
		NSString *note=@"";
		void **addr=((void **)&arg);	// array of pointers to something
		if(addr[i] == self) note=(@"self");
		if(addr[i] == _cmd) note=(@"_cmd");
		if(addr[i] == arg) note=(@"arg");
		if(addr[i] == arg2) note=(@"arg2");
		NSLog(@"stack[%2d]:%08x %08x %@", i, &(addr[i]), addr[i], note);
		}
	NSLog(@"idarg:%@ :%@", arg, arg2);
}

- (void) idarg:(id) arg idarg:(id) arg2 idarg:(id) arg3;
{
	NSLog(@"&idarg1:%p", &arg);
	NSLog(@"idarg1:%@", arg);
	NSLog(@"idarg2:%@", arg2);
	NSLog(@"&idarg3:%p", &arg3);
	NSLog(@"idarg3:%p", arg3);
	NSLog(@"idarg3:%@", arg3);
}

- (oneway void) sameSignature:(int) pid
							   name:(bycopy NSString *) name		// name
							   path:(bycopy NSString *) path		// full path
							  NSApp:(byref NSString *) app;			// creates NSDistantObject to remotely access
{
	NSLog(@"dummy to provide method signature");
}

- (NSMethodSignature *) methodSignatureForSelector:(SEL)aSelector
{
	NSLog(@"methodSignatureForSelector %@", NSStringFromSelector(aSelector));
//	NSLog(@"methodSignatureForSelector %s", sel_getName(aSelector));
#if __mySTEP__
	if(strcmp(sel_get_name(aSelector), "forward:name:path:NSApp:") == 0)
#else
	if(strcmp(sel_getName(aSelector), "forward:name:path:NSApp:") == 0)
#endif
		{
		NSMethodSignature *sig;
		NSLog(@"found forward:name:path:NSApp:");
		sig=[super methodSignatureForSelector:@selector(sameSignature:name:path:NSApp:)];
		NSLog(@" replaced by %@ returning %s", sig, [sig methodReturnType]);
		return sig;
		}
	return [super methodSignatureForSelector:aSelector];
}

- (void) forwardInvocation:(NSInvocation *)anInvocation
{
	unsigned n;
	NSMethodSignature *ms;
	NSLog(@"forwardInvocation: %p", anInvocation);
	ms=[anInvocation methodSignature];
	NSLog(@"target=%@", [anInvocation target]);
	NSLog(@"selector=%@", NSStringFromSelector([anInvocation selector]));
	NSLog(@"isOneway=%u", [ms isOneway]);
	NSLog(@"frameLength=%u", [ms frameLength]);
	NSLog(@"methodReturnLength=%u", [ms methodReturnLength]);
	NSLog(@"numberOfArguments=%u", [ms numberOfArguments]);
	NSLog(@"methodReturnType=%s", [ms methodReturnType]);
	for(n=0; n<[ms numberOfArguments]; n++)
		NSLog(@"type[%u]=%s", n, [ms getArgumentTypeAtIndex:n]);
	[anInvocation setSelector:@selector(registerApplication:name:path:NSApp:)];	// redirect
	NSLog(@"redirected=%@", NSStringFromSelector([anInvocation selector]));
	[anInvocation invoke];	// and call
}

- (oneway void) registerApplication:(int) pid
							   name:(bycopy NSString *) name		// name
							   path:(bycopy NSString *) path		// full path
							  NSApp:(byref NSString *) app;			// creates NSDistantObject to remotely access
{
	int i;
	long *frame=(long *)__builtin_apply_args();
	NSLog(@"registerApplication: __builtin_apply_args returns frame=%p", frame);
	for(i=0; i<24; i++)
		{
		NSLog(@"frame[%2d]:%p %08x", i, &frame[i], frame[i]);
		}
	for(i=-8; i<48; i++)
		{
		NSString *note=@"";
		void **addr=((void **)&pid)-8;	// array of pointers to something
		if(addr[i] == self) note=(@"self");
		if(addr[i] == _cmd) note=(@"_cmd");
		if(addr[i] == (void *) pid) note=(@"pid");
		if(addr[i] == name) note=(@"name");
		if(addr[i] == path) note=(@"path");
		if(addr[i] == app) note=(@"app");
		NSLog(@"stack[%2d]:%08x %08x %@", i, &(addr[i]), addr[i], note);
		}
	// addresses on stack
	NSLog(@"&self=%p", &self);
	NSLog(@"&_cmd=%p", &_cmd);
	NSLog(@"&pid=%p", &pid);
	NSLog(@"&name=%p", &name);
	NSLog(@"&path=%p", &path);
	NSLog(@"&app=%p", &app);
	// values
	NSLog(@"self=%p", self);
	NSLog(@"_cmd=%p", _cmd);
	NSLog(@"pid=%x", pid);
	NSLog(@"name=%p", name);
	NSLog(@"path=%p", path);
	NSLog(@"app=%p", app);
	// objects (typed values)
	NSLog(@"self=%@", self);
	NSLog(@"_cmd=%@", NSStringFromSelector(_cmd));
	NSLog(@"pid=%d", pid);
	NSLog(@"name=%@", name);
	NSLog(@"path=%@", path);
	NSLog(@"app=%@", app);
}

- (void) indirect
{
//	direct();
	void *args[50];
	void *frame[6];	// build our own stack frame manually
	NSLog(@"indirect preparing frame=%p args=%p", frame, args);
	frame[0]=args;
	frame[1]=self;		// self
	frame[2]=@"frame[2]";	// this one is ignored!
	frame[3]=@selector(registerApplication:name:path:NSApp:);
	frame[4]=(void *) 4567;	// pid
	frame[5]=@"directname";
	args[0]=@"directpath";	// this are the args that are NOT passed in registers
	args[1]=@"directapp";
	NSLog(@"indirect apply");
	__builtin_apply((void *) [self methodForSelector:(SEL) frame[3]], (void *) frame, 2*sizeof(args[0]));
	NSLog(@"indirect done");
}

@end

InvokedObject *target;

NSInvocation *invocation(SEL sel)
{ // create invocation
	NSMethodSignature *ms=[target methodSignatureForSelector:sel];
	NSInvocation *i=ms?[NSInvocation invocationWithMethodSignature:ms]:nil;
	unsigned n;
	NSLog(@"selector=%@", NSStringFromSelector(sel));
	NSLog(@"isOneway=%u", [ms isOneway]);
	NSLog(@"frameLength=%u", [ms frameLength]);
	NSLog(@"methodReturnLength=%u", [ms methodReturnLength]);
	NSLog(@"numberOfArguments=%u", [ms numberOfArguments]);
	NSLog(@"methodReturnType=%s", [ms methodReturnType]);
	for(n=0; n<[ms numberOfArguments]; n++)
		NSLog(@"type[%u]=%s", n, [ms getArgumentTypeAtIndex:n]);
	[i setTarget:target];
	[i setSelector:sel];
	return i;
}

void function(int arg1, int arg2, int arg3)
{
	NSLog(@"arg1=%d", arg1);
	NSLog(@"arg2=%d", arg2);
	NSLog(@"arg3=%d", arg3);
}

// this also works!!!

void other()
{
	typedef struct args { void *a[16]; } args;
	int iargs[]={ target, @selector(idarg:idarg:idarg:), @"String 1", @"String 2", @"String 3" };
	void (*fnp)(args arg)=[target methodForSelector:(SEL)iargs[1]];
	(*fnp)(*(args *) iargs);	// this should copy the struct onto the stack like directly supplied arguments
//	abort();
}

void direct()
{
	void *args[50];
	void *frame[6];	// build our own stack frame manually
	NSLog(@"preparing frame=%p args=%p", frame, args);
	frame[0]=args;
	frame[1]=target;		// self
	frame[2]=@"frame[2]";	// this one is ignored!
	frame[3]=@selector(registerApplication:name:path:NSApp:);
	frame[4]=(void *) 4567;	// pid
	frame[5]=@"directname";
	args[0]=@"directpath";	// this are the args that are NOT passed in registers
	args[1]=@"directapp";
	NSLog(@"apply");
	__builtin_apply((void *) [target methodForSelector:(SEL) frame[3]], (void *) frame, 2*sizeof(args[0]));
	NSLog(@"done");
}

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 	NSInvocation *i;
	char cval='@';
	short sval=1234;
	int ival=4321;
	double dval=3.1415;
	id oval=@"constant string";
	id oval2=@"second constant string";
	target=[InvokedObject new];
#if 0
	direct();
	other();
	i=invocation(@selector(noarg));
	[i invoke];
	i=invocation(@selector(intarg:));
	[i setArgument:&ival atIndex:2];
	[i invoke];
	i=invocation(@selector(chararg:));
	[i setArgument:&cval atIndex:2];
	[i invoke];
	i=invocation(@selector(shortarg:));
	[i setArgument:&sval atIndex:2];
	[i invoke];
	i=invocation(@selector(doublearg:));
	[i setArgument:&dval atIndex:2];
	[i invoke];
	i=invocation(@selector(idarg:));
	[i setArgument:&oval atIndex:2];
	[i invoke];
	i=invocation(@selector(idarg:idarg:));
	[i setArgument:&oval atIndex:2];
	[i setArgument:&oval2 atIndex:3];
	[i invoke];
	i=invocation(@selector(idarg:idarg:idarg:));
	oval=@"arg1";
	[i setArgument:&oval atIndex:2];
	oval=@"arg2";
	[i setArgument:&oval atIndex:3];
	oval=@"arg3";
	[i setArgument:&oval atIndex:4];
	[i invoke];
#endif
//	[target registerApplication:123 name:@"name" path:@"/usr/path" NSApp:@"NSApp"];	// call directly
	[target forward:123 name:@"name" path:@"/usr/path" NSApp:@"NSApp"];	// should issue a forwardInvocation
	// now call by constructed NSInvocation
//	direct();
	i=invocation(@selector(registerApplication:name:path:NSApp:));
	[i setArgument:&ival atIndex:2];
	oval=@"name";
	[i setArgument:&oval atIndex:3];
	oval=@"/usr/bin/path";
	[i setArgument:&oval atIndex:4];
	oval=@"NSApp";
	[i setArgument:&oval atIndex:5];
//	direct();
//	[target indirect];
	[i invoke];
    [pool release];
    return 0;
}
