//
//  determineDOConfig.m
//  mySTEP
//
//  Created by Dr. H. Nikolaus Schaller on Fri Oct 10 2003.
//  Copyright (c) 2003 DSITRI. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <Foundation/NSObjCRuntime.h>
#import <Foundation/NSConnection.h>
#import <Foundation/NSProxy.h>
#import <objc/Protocol.h>

#define dump(args, X) \
{ \
	int i; \
	NSLog(@"%@ dumping %@ - sig=%s", self, NSStringFromSelector(_cmd), [[self methodSignatureForSelector:_cmd] methodType]); \
	NSLog(@"&self=%08x &_cmd=%08x", &self, &_cmd); \
	for(i=0; i<32; i++) \
		{ \
		NSString *note=@""; \
		if(((void **)args)[i] == self) note=(@"self"); \
		if(((void **)args)[i] == _cmd) note=(@"_cmd"); \
		if(((void **)args)[i] == args) note=(@"args"); \
		if(((void **)args)[0] == &((void **)args)[i]) note=(@"<-args"); \
		X; \
		NSLog(@"arg[%2d]:%08x %08x %@", i, &(((void **)args)[i]), ((void **)args)[i], note); \
		} \
}

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
// #include <dlfcn.h>
void hexdump(void *a, int cnt)
{
	char *addr=(char *) a;
	int i=0;
	while(cnt>0)
		{
		fprintf(stderr, "%08x", (unsigned int) addr);
		while(i<16 && cnt>0)
			{
			fprintf(stderr, " %02x", (*addr++)&0xff), cnt--, i++;
			}
		fprintf(stderr, "\n");
		i=0;
		}
}

int
foo(int x, int y)
{
	fprintf(stderr, "foo(%d,%d)\n", x, y);
	return 0;
}

int
docall(int x, int y)
{
	void *args, *result;
	void *f;
	id self;
	SEL _cmd;
	args = __builtin_apply_args();
	f = (void*)foo;
	fprintf(stderr, "&x=%08x, &y=%08x\n", (unsigned) &x, (unsigned) &y);
	hexdump(args, 128);
	self=nil, _cmd=0;
#ifdef Linux_ARM
//	dump(args, ;);
#endif
	result = __builtin_apply((void(*)()) f, args, 6*sizeof(int));
	__builtin_return(result);
//	assert(0);
}

#ifdef Linux_ARM
#include <objc/objc.h>	// for arglist_t
#include "mframe.h"

typedef struct { char x, y; } short_struct;

@interface Proxy : NSObject
- (oneway void) message;
- (void) messageWithChar:(char) x;
- (void) messageWithChar:(char) x andChar:(char) y;
- (void) messageWithCharPtr:(char *) str;
- (void) messageWithInt:(int) x;
- (void) messageWithFloat:(float) x;
- (void) messageWithDouble:(double) x;
- (void) messageWithObject:(bycopy id) o;
- (void) messageWithInt:(int) x andObject:(byref id) o;
- (void) messageWithObject:(id) o andInt:(int) x;
- (void) messageWithInt:(int) x andInt:(int) y;
- (void) messageWithInt:(int) x andInt:(int) y andInt:(int) z;
- (void) messageWithPoint:(NSPoint) x;
- (void) messageWithPoint:(NSPoint) x andPoint:(NSPoint) y;
- (void) messageWithPoint:(NSPoint) x andPoint:(NSPoint) y andPoint:(NSPoint) z;
- (id) messageGettingObject:(int) x;
- (int) messageGettingInt:(int) x;
- (char) messageGettingChar:(int) x;
- (char *) messageGettingCharPtr:(int) x;
- (NSPoint) messageGettingPoint:(int) x;
- (short_struct) messageGettingShortStruct:(int) x andInt:(int) y;
- (NSPoint) movePoint:(NSPoint) p by:(double) amount;
- (NSPoint) movePoint2:(NSPoint) p by:(double) amount;
@end

@implementation Proxy

- (NSPoint) movePoint:(NSPoint) p by:(double) amount;
{
//	void *args = __builtin_apply_args();
	NSPoint q=p;
//	dump(args, if(*(double *)&(((void **)args)[i]) == amount) note=(@"amount"))
	q.x += amount;
	q.y -= amount;
	NSLog(@"movePoint:by: returns %@", NSStringFromPoint(q));
	return q;
}

// This method needs to be implemented to actually do anything.
- (void) forwardInvocation:(NSInvocation*)anInvocation
{
	int i;
	NSMethodSignature *m=[anInvocation methodSignature];
//	NSLog(@"Proxy forwardInvocation:%@", anInvocation);
	NSLog(@"target=%@ selector=%@", [anInvocation target], NSStringFromSelector([anInvocation selector]));
	for(i=0; i<[m numberOfArguments]; i++)
		{
		const char *type=[m getArgumentTypeAtIndex:i];
		char *bfr=objc_malloc([m methodInfo][i+1].size);
		[anInvocation getArgument:bfr atIndex:i];
		switch(*type)
			{
			case _C_ID:			NSLog(@"%d: %@", i, *(id*) bfr); break;
			case _C_SEL:		NSLog(@"%d: %@", i, NSStringFromSelector(*(SEL*) bfr)); break;
			case _C_UCHR:
			case _C_CHR:		NSLog(@"%d: %02x", i, *(char*) bfr); break;
			case _C_CHARPTR:	NSLog(@"%d: %s", i, *(char **) bfr); break;
			case _C_INT:		NSLog(@"%d: %d", i, *(int*) bfr); break;
			case _C_FLT:		NSLog(@"%d: %f", i, *(float*) bfr); break;
			case _C_DBL:		NSLog(@"%d: %lf", i, *(double*) bfr); break;
			case _C_STRUCT_B:	NSLog(@"%d: %@", i, NSStringFromPoint(*(NSPoint*) bfr)); break;
			default:			NSLog(@"%d: type=%s val=%08x", i, type, *(long*) bfr);
			}
		objc_free(bfr);
		}
	if([NSStringFromSelector([anInvocation selector]) isEqual:@"messageGettingPoint:"])
		{
		int iii;
		NSPoint p;
		[anInvocation getArgument:&iii atIndex:2];
		p=NSMakePoint(iii+1.5, iii+2.5);
		NSLog(@"messageGettingPoint setting return value %@", NSStringFromPoint(p));
		[anInvocation setReturnValue:&p];
		}
	else if([NSStringFromSelector([anInvocation selector]) isEqualToString:@"messageGettingInt:"])
		{
		int iii;
		[anInvocation getArgument:&iii atIndex:2];
		iii++;	// increment
		NSLog(@"messageGettingInt setting return value %d", iii);
		[anInvocation setReturnValue:&iii];
		}
	else if([NSStringFromSelector([anInvocation selector]) isEqualToString:@"messageGettingChar:"])
		{
		int iii;
		char ccc;
		[anInvocation getArgument:&iii atIndex:2];
		iii++;	// increment
		ccc=iii;
		NSLog(@"messageGettingChar setting return value %d", ccc);
		[anInvocation setReturnValue:&ccc];
		}
	else if([NSStringFromSelector([anInvocation selector]) isEqualToString:@"messageGettingCharPtr:"])
		{
		int iii;
		static char ccc[50];	// must be static as we pass the address of this buffer and only the address
					            // and not the string is copied!
		char *cccp=ccc;			// initalize a pointer to the first character - we will pass that pointer
		[anInvocation getArgument:&iii atIndex:2];
		iii++;	// increment
		sprintf(ccc, "this is a string: %d", iii);
		NSLog(@"messageGettingCharPtr setting return value %s", ccc);
		[anInvocation setReturnValue:&cccp];	// we must pass the address of buffer!
		}
	else if([NSStringFromSelector([anInvocation selector]) isEqualToString:@"messageGettingObject:"])
		{
		id o;
		int iii;
		[anInvocation getArgument:&iii atIndex:2];
		o=[NSNumber numberWithDouble:iii+3.14159265358979];
		NSLog(@"messageGettingObject setting return value %@", o);
		[anInvocation setReturnValue:&o];
		}
	else if([NSStringFromSelector([anInvocation selector]) isEqualToString:@"messageGettingShortStruct:andInt:"])
		{
		short_struct s;
		int x, y;
		[anInvocation getArgument:&x atIndex:2];
		[anInvocation getArgument:&y atIndex:3];
		s.x=x;
		s.y=y;
		NSLog(@"messageGettingShortStruct setting return value");
		[anInvocation setReturnValue:&s];
		}
}

@end
#endif

#ifdef Linux_ARM

@protocol myProtocol
// check return types
- (oneway void) onewayvmessage;
- (void) vmessage;
- (char) cmessage;
- (char *) smessage;
- (int) imessage;
- (float) fmessage;
- (double) dmessage;
- (NSPoint) pmessage;
- (id) omessage;
- (byref id) byrefomessage;
- (bycopy id) bycopyomessage;
// check argument passing
// check byref/bycopy
// check in, out, inout
@end

@interface myServer : NSObject <myProtocol>
@end

@implementation myServer
- (oneway void) onewayvmessage; { NSLog(@"[myServer onewayvmessage] called!"); return; }
- (void) vmessage; { NSLog(@"[myServer vmessage] called!"); return; }
- (char) cmessage; { NSLog(@"[myServer cmessage] called!"); return '*'; }
- (char *) smessage; { NSLog(@"[myServer smessage] called!"); return "string from server"; }
- (int) imessage; { NSLog(@"[myServer imessage] called!"); return 17; }
- (float) fmessage; { NSLog(@"[myServer fmessage] called!"); return 3.1415926535; }
- (double) dmessage; { NSLog(@"[myServer dmessage] called!"); return 3.1415926535; }
- (NSPoint) pmessage; { NSLog(@"[myServer pmessage] called!"); return NSMakePoint(17.5, 33.2); }
- (id) onilmessage; { NSLog(@"[myServer onilmessage] called!"); return nil; }
- (id) omessage; { NSLog(@"[myServer omessage] called!"); return [NSArray array]; }
- (byref id) byrefomessage; { NSLog(@"[myServer byrefomessage] called!"); return [NSArray array]; }
- (bycopy id) bycopyomessage; { NSLog(@"[myServer bycopyomessage] called!"); return [NSArray array]; }
- (void) exceptionmessage; { NSLog(@"[myServer exceptionmessage] exceptionmessage!"); [[NSArray array] objectAtIndex:1]; }
@end
#endif

void testConnection(void)
{
#ifdef Linux_ARM
	id theProxy;
	// set up a myProxy object to be distantly accessed - e.g. the server object
	id serverObject=[[[myServer alloc] init] autorelease];
	NSLog(@"serverObject=%@", serverObject);
	[[NSConnection defaultConnection] setRootObject:serverObject];
	if([[NSConnection defaultConnection] registerName:@"server"] == NO)
		{
		NSLog(@"error registering name");
		return;
		}
	// establish a connection to the server

	theProxy = [NSConnection rootProxyForConnectionWithRegisteredName:@"server" host:nil];
//	[theProxy retain];	// recommended by Apple documentation
	NSLog(@"theProxy=%@", theProxy);
//	[theProxy setProtocolForProxy:@protocol(myProtocol)];

	// send messages
	[theProxy onewayvmessage];	// should run asnchronously
	[theProxy vmessage];		// wait for completion
	NSLog(@"cmessage sent - returns %02x", [theProxy cmessage]);
	NSLog(@"smessage sent - returns \"%s\"", [theProxy smessage]);
	NSLog(@"imessage sent - returns %d", [theProxy imessage]);
	NSLog(@"fmessage sent - returns %f", [theProxy fmessage]);
	NSLog(@"dmessage sent - returns %lf", [theProxy dmessage]);
	NSLog(@"pmessage sent - returns %@", NSStringFromPoint([theProxy pmessage]));
	NSLog(@"onilmessage sent - returns %@", [theProxy onilmessage]);
	NSLog(@"omessage sent - returns %@", [theProxy omessage]);
	NSLog(@"byrefomessage sent - returns %@", [theProxy byrefomessage]);
	NSLog(@"bycopyomessage sent - returns %@", [theProxy bycopyomessage]);
	// finally raise an exception
	NS_DURING
		[theProxy exceptionmessage];
	NS_HANDLER
		NSLog(@"received remote excepion: \"%@\", reason \"%@\"", [localException name], [localException reason]);
	NS_ENDHANDLER
	// run the run loop to handle received messages
//	[[NSRunLoop currentRunLoop] run];
	NSLog(@"suceeded.");
#endif
}

int main(int argc, const char **argv)
{
#ifdef Linux_ARM
	NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
	NSInvocation *i;
	NSMethodSignature *sig;
	Proxy *p;
	NSPoint pnt;
	int ii;
	id o;
	short_struct s;
	char *str;

	int rv;
	int a=7, b=13;
	fprintf(stderr, "Test if __builtin_apply() works.\nHere, it should print twice the same:\nfoo(%d,%d)\n", a, b);
	rv = docall(a, b);
	
	NSLog(@"&arg_ptr = %08x", &(((arglist_t) 0)->arg_ptr));
	NSLog(@"&arg_regs = %08x", &(((arglist_t) 0)->arg_regs));
	
	NSLog(@"main() located at %08x", (unsigned) &main);
	NSLog(@"[NSAutoreleasePool] object located at %08x", (unsigned) pool);
	p=[Proxy alloc];
	NSLog(@"[Proxy] object located at %08x", (unsigned) p);
	[p init];
	NSLog(@"[Proxy forward::] located at %08x", (unsigned) [p methodForSelector:@selector(forward::)]);
	[p message];
	[p messageWithChar:'x'];
	[p messageWithChar:'x' andChar:'y'];	// BUG: x=78 gets passed, y not yet!!!
	[p messageWithCharPtr:"this is a string"];
	[p messageWithInt:4];
	[p messageWithInt:5];
	[p messageWithFloat:(float) 5.4];
	[p messageWithDouble:(double) 5.5];
	[p messageWithObject:p];
	[p messageWithObject:pool];
	[p messageWithObject:p andInt:6];
	[p messageWithInt:7 andObject:pool];
	[p messageWithInt:8 andInt:9];
	[p messageWithInt:10 andInt:11 andInt:12];
	[p messageWithPoint:NSMakePoint(1.0, 2.0)];	// pass struct
	[p messageWithPoint:NSMakePoint(1.0, 2.0) andPoint:NSMakePoint(3.0, 4.0)];	// pass 2 structs
	[p messageWithPoint:NSMakePoint(1.0, 2.0) andPoint:NSMakePoint(3.0, 4.0) andPoint:NSMakePoint(5.0, 6.0)];	// pass 3 structs
	ii=[p messageGettingInt:8];
	NSLog(@"messageGettingInt returns %d (%d)", ii, 8+1);
	ii=[p messageGettingChar:17+256];
	NSLog(@"messageGettingChar returns %d (%d)", ii, (char) (17+256+1));
	str=[p messageGettingCharPtr:17+256];
	NSLog(@"messageGettingCharPtr returns str=%08x", str);
	NSLog(@"                                 =%s", str);
	o=[p messageGettingObject:8];
	NSLog(@"messageGettingObject returns %@ (8)", o);
	pnt=[p messageGettingPoint:8];
	NSLog(@"messageGettingPoint returns %@", NSStringFromPoint(pnt));
	pnt=[p movePoint:NSMakePoint(5.0, 6.0) by:3.45];
	NSLog(@"movePoint returns %@", NSStringFromPoint(pnt));
	// 	pnt=[p movePoint2:NSMakePoint(5.0, 6.0) by:3.45];	// this one is not even handled by forwardInvocation:
//	NSLog(@"movePoint2 returns %@", NSStringFromPoint(pnt));
	NSLog(@"now sending messageGettingShortStruct...");
	NSLog(@"p=%@", p);
	s=[p messageGettingShortStruct:9 andInt:10];
	NSLog(@"messageGettingShortStruct returns x=%d y=%d", s.x, s.y);
	NSLog(@"p=%@", p);
	s=[p messageGettingShortStruct:9+256 andInt:10-256];
	NSLog(@"messageGettingCharStruct returns x=%d y=%d", s.x, s.y);
	NSLog(@"p=%@", p);

	NSLog(@"now creating a fresh NSInvocation");

	// create invocation
//	sig=[Proxy instanceMethodSignatureForSelector:@selector(messageWithInt:andObject:)];	// returns nil because the method is not implemented!
//	sig=[Proxy instanceMethodSignatureForSelector:@selector(movePoint2:by:)];
	sig=[p methodSignatureForSelector:@selector(movePoint:by:)];
	NSLog(@"sig=%@", sig);
	i=[NSInvocation invocationWithMethodSignature:sig];
	[i setTarget:p];
	[i setSelector:@selector(movePoint:by:)];
	for(ii=5; ii<=7; ii++)
		{
		NSPoint p0=NSMakePoint(1.5, 2.5), p1;
		double n=ii;
		[i setArgument:&p0 atIndex:2];
		[i setArgument:&n atIndex:3];
		[i invoke];
		[i getReturnValue:&p1];
		NSLog(@"p0=%@ : %lf -> %@", NSStringFromPoint(p0), n, NSStringFromPoint(p1));
		}
	NSLog(@"done.");

	testConnection();
	
	[pool release];
#else
#endif
	return 0;
}

#if OTHER

struct small1
{
	char byte;
};

struct small4
{
	char byte[4];
};

struct small8
{
	char byte[8];
};

struct small16
{
	char byte[16];
};

struct large
{
	char byte[256];
};

struct large returnlarge(void)
{
	int get_mframe_stack_struct(void);
	void *s=alloca(4);	// get stack space
	struct large x;
	printf("&get_mframe_stack_struct=%08x\n", (unsigned int) &get_mframe_stack_struct);
	printf("&returnlarge=%08x\n", (unsigned int) &returnlarge);
	printf("&s=%08x\n", (unsigned int) &s);
	printf("s=%08x\n", (unsigned int) s);
	printf("&x=%08x\n", (unsigned int) &x);
	hexdump(s, sizeof(x)+40);	// print stack starting at x
	x.byte[5]=123;
	printf("");
	return x;
}

// try to find out what values we need for DOconfig.h

int get_mframe_stack_struct(void)
{
	struct large r;
	r=returnlarge();
	return 0;
}

int main(int argc, const char **argv)
{

	printf("not completed\n");
	exit(1);
	
	printf("#define MFRAME_STACK_STRUCT %d\n\
	This should be defined to 1 if functions return structures by value\n\
	using the method where the caller places a pointer on the stack.\n\
	Define this to 0 otherwise (eg. when the pointer to the structure is\n\
	passed in a register).\n\n",
		get_mframe_stack_struct());

	printf("#define MFRAME_SMALL_STRUCT %d\n\
	This is the size of the largest structure returned by value on\n\
	the stack.  Normally gcc returns structures of up to 8 bytes on\n\
	the stack.  If your system never returns structures on the stack,\n\
	set this to zero rather than 8.\n\
	NB. If __builtin_apply_args() always returns an argframe for\n\
	structure return via pointer, you should also define this to zero.\n\n",
		0);

	printf("#define MFRAME_STRUCT_BYREF %d\n\
	This should be defined to 1 if structure arguments are passed in\n\
	the stack frame by reference, 0 otherwise.\n\n",
		0);

	printf("#define MFRAME_ARGS_SIZE %d\n\
	This must be set to the value computed by the apply_args_size()\n\
	function in expr.c in the gcc source.  It is the size of the\n\
	area of memory allocated in which to pass arguments to a function.\n\
	If you can't figure out how to determine this (hack expr.c to print\n\
	the result of the function the first time it's called) - try using a\n\
	value like 128 - which will probably be far larger than required\n\
	(and therefore somewhat inefficient) but will most likely work.\n\n",
		128);

	printf("#define MFRAME_RESULT_SIZE %d\n\
	This must be set to the value computed by the apply_result_size()\n\
	function in expr.c in the gcc source.  It is the size of the area\n\
	of memory allocated in which to return a value from a function.\n\
	If you can't figure out how to determine this (hack expr.c to print\n\
	the result of the function the first time it's called) - try using a\n\
	value like 128 - which will probably be far larger than required\n\
	(and therefore somewhat inefficient) but will most likely work.\n\n",
		128);

	printf("%s#define MFRAME_FLT_IN_FRAME_AS_DBL\n\
	This should be defined as 1 if float parameters to functions and\n\
	objective-c methods are passed on the stack as double values.\n\
	Otherwise it should not be defined.\n\n",
		0?"// ":"");

	printf("MFRAME_STRUCT_ADDR(ARGFRAME,TYPES)\n\
	If a function returns a structure by copying it into a location\n\
	whose address is set by the caller, this macro must return that\n\
	address within the argframe.\n\
	Otherwise the macro must return zero.\n\n");

	printf("MFRAME_ARGS\n\
	This macro should define a data type to be used for recording\n\
	information about the arguments list of a method.\n\
	See 'CUMULATIVE_ARGS' in the configuration file for your system\n\
	in gcc for a parallel example.\n\n");

	printf("MFRAME_INIT_ARGS(CUM, RTYPE)\n\
	This macro is used to initialise a variable (CUM) of the type\n\
	defined by MFRAME_ARGS.  The RTYPE value is the type encoding for the\n\
	method return type, it is needed so that CUM can take int account any\n\
	invisible first argument used for returning structures by value.\n\
	See 'INIT_CUMULATIVE_ARGS' in the configuration file for your system\n\
	in gcc for a parallel example.\n\n");

	printf("MFRAME_ARG_ENCODING(CUM,TYPES,STACK,DEST)\n\
	This macro is used to to determine the encoding of arguments.\n\
	You will have to write this macro for your system by examining the\n\
	gcc source code to determine how the compiler does this on your\n\
	system - look for the usage of CUMULATIVE_ARGS an INIT_CUMULATIVE_ARGS\n\
	in the configuration files for your hardware and operating system in\n\
	the gcc (or egcs) source, and make your macro mirror it's operation.\n\
\n\
	Before initial entry,\n\
	CUM should have been initialised using the MFRAME_INIT_ARGS() macro,\n\
	TYPES should be a (const char*) variable initialised to a\n\
	type-encoding string listing the arguments of a function/method,\n\
	STACK should be an integer variable of value 0 in which the size of\n\
	the stack arguments will be accumulated,\n\
	DEST should be a (char*) variable initialised to a pointer to a\n\
	buffer into which the full type encoding will be written.\n\
	After each use of the macro, TYPES is advanced to point to the next\n\
	argument, and DEST is advanced to point after the encoding of the\n\
	previous argument.\n\
	Of course, you must ensure that the buffer pointed to by DEST is\n\
	large enough so that it does not overflow!\n\
	You will be safe if your buffer is at least ten times as big as\n\
	the type-encoding string you start from.\n\n");

	return 0;
}

#endif