/* ClassAdditions.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.
 */

/* stupid comment */
#import "ClassAdditions.h"
#import "StorageAdditions.h"  // category that adds addElementIfAbsent:
#import <objc/objc-class.h>
#import <objc/objc-runtime.h>
#import <objc/List.h>
#import <objc/Storage.h>
#import <string.h>

BOOL inheritsFrom(struct objc_class *candidate, struct objc_class *root)
{
	struct objc_class *t;

	for(t=candidate;(t && t != root);t=t->super_class);
	return (t==root);
}

BOOL isPoser(struct objc_class *candidate)
{
	return (candidate->name[0] == '%');
}

struct objc_class *realSuperclass(struct objc_class *candidate)
{
	struct objc_class *o;
	// Superclass that isn't a poser
	for(o=candidate->super_class;(o && isPoser(o));o = o->super_class);
	return o;
}

int subclasses(	struct objc_class *of,
				id list,
				BOOL posers,
				CAObjectFilterFunc filterFunc )
{
	NXHashTable *p;
	NXHashState state;
	void *value;
	int count = 0;
	struct objc_class *candidate;

	p = objc_getClasses();
	state = NXInitHashState(p);

	while (NXNextHashState(p,&state,&value)) {
		candidate = (struct objc_class *)value;
		if (!inheritsFrom(candidate,[Object class])) continue;
		if (((!posers &&
				((realSuperclass(candidate)==of)&&
				(!isPoser(candidate))) ) ||
				((posers) && (candidate->super_class == of))) && 
				(!filterFunc || filterFunc(candidate,of))) {
					count++;
					if (list)
						[list addObject:(id)candidate];
		}
	}
	return count;
}

int methodSelectors(	struct objc_class *of,
						id storage,
						BOOL ancestors,
						CAElementFilterFunc filterFunc )
{
	struct objc_method_list *methods;
	SEL selector;
	BOOL c;
	int i;
	int total = 0;
	struct objc_class *s;
	
	/* inane observation */
	[storage empty];
	for(s=of,c=YES;(c && s);s = s->super_class,c=ancestors) {
		for(methods = s->methods;methods;methods=methods->method_next) {
			for(i=0;(i<methods->method_count);i++) {
				selector = (SEL)(methods->method_list[i].method_name);
				if (filterFunc(of, selector)) {
					[storage addElementIfAbsent:&selector];
					total++;
				}
			}
		}
	}
	return total;
}

@implementation Object (ClassAdditions)

+ (int)subclasses:(id)list allowPosers:(BOOL)yn
						filterWith:(CAObjectFilterFunc)aFunc
{
	return subclasses(self,list,yn,aFunc);
}

+ (int)methodSelectors:(id)storage includeAncestors:(BOOL)yn
						filterWith:(CAElementFilterFunc)aFunc
{
	return methodSelectors(self,storage,yn,aFunc);
}

@end
