/* SelPairAgent.m
 * Written By:  Thomas Burkholder
 *
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 */

#import "../Utilities.subproj/ClassAdditions.h"
#import "SelectorBrowsingFunctions.h"
#import "SelPairAgent.h"
#import "TBinderListConnectInspector.h"
#import <objc/objc-class.h>
#import <apps/InterfaceBuilder.h>

#define T_UNKNOWN 0
#define T_ORDINAL 1
#define T_STRING 2
#define T_OBJECT 3
#define T_SELECTOR 4

int abstractType(char c)
{
	if ((c==*@encode(char)) || (c==*@encode(int)) ||
		(c==*@encode(short)) || (c==*@encode(long)) ||
		(c==*@encode(unsigned char)) || (c==*@encode(unsigned int)) ||
		(c==*@encode(unsigned short)) || (c==*@encode(unsigned long)) ||
		(c==*@encode(float)) || (c==*@encode(double)))
		return T_ORDINAL;
	if (c==*@encode(char *)) return T_STRING;
	if (c==*@encode(id)) return T_OBJECT;
	if (c==*@encode(SEL)) return T_SELECTOR;
	return T_UNKNOWN;
}

void filterForReturnTypes(id storage, id srcClass, SEL srcSelector,id destClass)
{
	// Icky, nasty.  storage is assumed to contain SEL's.  srcClass and
	// srcSelector give us the templateArgs - the arg types of the
	// srcSelector method in the srcClass class.  destClass we need, in
	// order to get the arg types of each selector in the storage.
	int i,tp1,tp2;

	// we get the arg types from class and aSelector:
	struct objc_method *m = class_getInstanceMethod(srcClass,srcSelector);
	const char *candidateArgs, *templateArgs = m->method_types;

	// eliminate all selectors whose return values don't match.

	for(i=0;(i<[storage count]);) {
		m = class_getInstanceMethod(destClass,*(SEL *)[storage elementAt:i]);
		candidateArgs = m->method_types;
		// Some primitive type-matching, taking advantage of type
		// coersion...
		tp1 = abstractType(candidateArgs[0]);
		tp2 = abstractType(templateArgs[0]);
		if ((candidateArgs[0]==templateArgs[0])||(tp1&&tp2&&(tp1==tp2)))
			i++;
		else
			[storage removeElementAt:i];
	}
}

@implementation SelPairAgent

- initFrom:anObject
{
	// A bit crude; we use initialStorage to determine leafdom.
	[super init];
	initialStorage = anObject;
	secondaryStorage = [[SortedStorage alloc] init];
	[secondaryStorage setAgent:self];
	return self;
}

- free
{
	[secondaryStorage free];
	return [super free];
}

- subdirectoryFor:(void *)anElement sender:sender
{
	if (sender != initialStorage || ![NXApp isConnecting])
		return nil;
	[[[NXApp connectDestination] class] methodSelectors:secondaryStorage
							includeAncestors:YES
							filterWith:isAccessor];
	// match the return type of the selected SEL.
	filterForReturnTypes(secondaryStorage,
				[[getDSConnectorForSource([NXApp connectSource]) destination] class],
				*(SEL *)anElement,
				[[NXApp connectDestination] class]);
	return secondaryStorage;
}

- (BOOL)isLeaf:(void *)anElement sender:sender
{
	return (![NXApp isConnecting] || sender != initialStorage);
}

- (const char *)displayStringFor:(void *)anElement sender:sender
{
	return sel_getName(*(SEL *)anElement);
}

- (int)compare:(void *)first with:(void *)second sender:sender
{
	return strcmp(sel_getName(*(SEL *)first), sel_getName(*(SEL *)second));
}

- (const char *)titleOfColumn:(int)col
{
	return (col)?"Interface":"DataSource";
}

@end