#import <Foundation/Foundation.h>
#include <objc/objc-api.h>

/**
 * Not actually needed, but it gets rid of a warning when we call -foo since it
 * is not ever used as a method.
 */
@protocol foo
- (void) foo;
@end 

/**
 * Basic implementation of prototypes in Objective-C
 */
@interface Prototype : NSObject {
	BOOL isPrototype;
	NSMapTable * dtable;
}
/**
 * Adds the specified method to this instance.
 */
- (void) setMethod:(IMP)aMethod forSelector:(SEL)aSelector;
@end
@implementation Prototype
- (id) init
{
	if(nil == (self = [super init]))
	{
		return nil;
	}
	//Note: This is a hack; selectors with the same name return the same
	//C string from the runtime, so we can do a pointer comparison on them
	dtable = NSCreateMapTable(
			NSNonOwnedPointerMapKeyCallBacks,
			NSNonOwnedPointerMapValueCallBacks,
			5);
	return self;
}
/**
 * Set this class as having a custom method lookup mechanism.
 */
+ (void) initialize
{
	
	CLS_SETOBJECTMESSAGEDISPATCH((Class)self);
	[super initialize];
}
/**
 * Message lookup function.  Looks for method in the dtable ivar.
 */
+ (IMP) messageLookupForObject:(id)anObject selector:(SEL)aSelector
{
	struct Prototype_struct { @defs(Prototype) } * ivars = 
		(struct Prototype_struct*)anObject;
	/* Avoid map lookup if there are no methods added yet. */
	if(ivars->isPrototype)
	{
		return NSMapGet(ivars->dtable, sel_get_name(aSelector));
	}
	return NULL;
}
/**
 * Add a new method.
 */
- (void) setMethod:(IMP)aMethod forSelector:(SEL)aSelector
{
	isPrototype = YES;
	NSMapInsert(dtable, sel_get_name(aSelector), (void*)aMethod);
}
@end

#define DEFMETHOD(name, ...) id name(id self, SEL cmd, ## __VA_ARGS__)

int main(void)
{
	[NSAutoreleasePool new];
	id proto = [Prototype new];
	int i = 12;
	DEFMETHOD(foo)
	{
		NSLog(@"Called with object %@, in lexical scope where i=%d", self, i);
		return nil;
	}
	[proto setMethod:(IMP)foo forSelector:@selector(foo)];
	[proto foo];
	return 0;
}
