// Formalities
	//	Copyright (C) 1994 Guus C. Bloemsma
	//
	//	COWSObjectLibrary.m
	//	Version 1.0			Guus C. Bloemsma
	
	#import "COWSObjectLibrary.h"


@interface	Object(findClass)
// A hack to declare the findClass: method that is apparently nowhere documented
// but can be found in AppInspector, the most amazing tool ever made (right after
// Interface Builder ;-)

-	(Class) findClass: (const char *) className;

@end



@implementation COWSStringNode(ObjectLibrary)

- idVal {
	if (SN_CHECK([self value_state], COWSSTRINGNODE_STORED_STRING))
		return NXGetNamedObject(string, NXApp);
	else
		return (id)(long)double_value;
	};

- setIdVal: object {
	return [self setDoubleVal:(long)object];
	};

- classVal {
	if (SN_CHECK([self value_state], COWSSTRINGNODE_STORED_STRING)) 
		/* ignore warning */
		return [self findClass:string];
	else
		return (id)(long)double_value;
	};

- setClassVal: object {
	return [self setDoubleVal:(long)object];
	};

- (SEL) selVal {
	if (SN_CHECK([self value_state], COWSSTRINGNODE_STORED_STRING)) 
		return sel_getUid(string);
	else
		return (SEL)(long)double_value;
	};

- setSelVal: (SEL)selector {
	return [self setDoubleVal:(long)selector];
	};

@end



@implementation COWSObjectLibrary

id		objc_msgSendv(id theReceiver, SEL theSelector, unsigned int argSize, marg_list argFrame);

char	*ObjectMakeStack (marg_list stack, unsigned long maxStackSize, Method theMethod,
			id parameters, unsigned int *stackSize) {
	// Need a lot more error checking and optimization here
	
	unsigned int size;
	unsigned int count;
	unsigned int index;
	
	size = method_getSizeOfArguments(theMethod);
	count = method_getNumberOfArguments(theMethod);
	
	if (size > maxStackSize)
		return "Too many arguments.\n";
	
	for (index = 2; index < count; index++) {
		const char *type;
		int offset;
		id	param = [parameters next];
		method_getArgumentInfo(theMethod, index, &type, &offset);
		//printf("Passing type \"%s\" at offset %d\n", type, offset); 
		switch (*type) {
			case 'c':	//	A char
				marg_setValue(stack, offset, char, [param intVal]);
				break;
				
			case 'i':	//	An int
				marg_setValue(stack, offset, int, [param intVal]);
				break;
				
			case 's':	//	A short
				marg_setValue(stack, offset, short, [param intVal]);
				break;
				
			case 'l':	//	A long
				marg_setValue(stack, offset, long, [param intVal]);
				break;
				
			case 'C':	//	An unsigned char
				marg_setValue(stack, offset, unsigned char, [param intVal]);
				break;
				
			case 'I':	//	An unsigned int
				marg_setValue(stack, offset, unsigned int, [param intVal]);
				break;
				
			case 'S':	//	An unsigned short
				marg_setValue(stack, offset, unsigned short, [param intVal]);
				break;
				
			case 'L':	//	An unsigned long
				marg_setValue(stack, offset, unsigned long, [param intVal]);
				break;
				
			case 'f':	//	A float
				marg_setValue(stack, offset, float, [param floatVal]);
				break;
				
			case 'd':	//	A double
				marg_setValue(stack, offset, double, [param floatVal]);
				break;
				
			case 'v':	//	A void
				break;
				
			case '*':	//	A character string (char *)
				// ¿¿ make this an NXAtom first?
				marg_setValue(stack, offset, const char *, [param string]);
				break;
				
			case '@':	//	An object (whether statically typed or typed id)
				marg_setValue(stack, offset, id, [param idVal]);
				break;
				
			case '#':	//	A class object (Class)
				// ¿¿ Take class of object ?
				marg_setValue(stack, offset, Class, [param classVal]);
				break;
				
			case ':':	//	A method selector (SEL)
				marg_setValue(stack, offset, SEL, [param selVal]);
				break;
				
			case '[':	//...]	An array
			case '{':	//...}	A structure
			case '(':	//...)	A union
			case 'b':	//num	A bitfield of num bits
			case '^':	//type	A pointer to type
			case '?':	//	An unknown type
			default:
				return "Method expects composite type."; 
			};
		};
	
	if (stackSize)
		*stackSize = size;
	return NULL;
	};


- object_to: arg_list {
	id		result=[[COWSStringNode alloc] init];
	id		arg = [arg_list first];
	const	char	*messageName;
	SEL		selector;
	char	myStack[1024];
	id		object;
	unsigned	int	stackSize;
	Method		theMethod;
	
	arg = [arg_list first];
	if ((!arg) || !((object = [arg idVal]) || (object = [arg classVal])))
		return [[result setError: YES] setString: "\"to\" needs an object or class"];
	
	arg = [arg_list next];
	if ((!arg) || !(messageName = [arg string]))
		return [[result setError: YES] setString: "\"to\" needs a message name"];
	
	selector = sel_getUid(messageName);
	if (!selector)
		return [[result setError: YES] setString: "this is not a selector"];
	
	/* ignore warning */
	theMethod = class_getInstanceMethod(object->isa, selector);
	if (!theMethod)
		return [[result setError: YES] setString: "the object does not respond to this selector"];
	
	ObjectMakeStack (myStack, 1024, theMethod, arg_list, &stackSize);
	
	switch (*theMethod->method_types) {
		case 'c':	//	A char
			[result setBooleanVal: ((char(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case 'i':	//	An int
			[result setIntVal: ((int(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case 's':	//	A short
			[result setIntVal: ((short(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case 'l':	//	A long
			[result setIntVal: ((long(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case 'C':	//	An unsigned char
			[result setBooleanVal: ((unsigned char(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case 'I':	//	An unsigned int
			[result setIntVal: ((unsigned int(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case 'S':	//	An unsigned short
			[result setIntVal: ((unsigned short(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case 'L':	//	An unsigned long
			[result setIntVal: ((unsigned long(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case 'f':	//	A float
			[result setFloatVal: ((float(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case 'd':	//	A double
			[result setDoubleVal: ((double(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case 'v':	//	A void
			objc_msgSendv(object, selector, stackSize, myStack);
			break;
			
		case '*':	//	A character string (char *)
			[result setString: ((char*(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case '@':	//	An object (whether statically typed or typed id)
			[result setIdVal: objc_msgSendv(object, selector, stackSize, myStack)];
			break;
			
		case '#':	//	A class object (Class)
			[result setClassVal: objc_msgSendv(object, selector, stackSize, myStack)];
			break;
			
		case ':':	//	A method selector (SEL)
			[result setSelVal: ((SEL(*)(id, SEL, unsigned int, marg_list)) objc_msgSendv) (object, selector, stackSize, myStack)];
			break;
			
		case '[':	//...]	An array
		case '{':	//...}	A structure
		case '(':	//...)	A union
		case 'b':	//num	A bitfield of num bits
		case '^':	//type	A pointer to type
		case '?':	//	An unknown type
		default:
			[[result setError: YES] setString: "cannot return this type"]; 
		};
	
	return result;
	}




- loadLibrary:sender {
    id result = [super loadLibrary:sender];
	
    if (![sender conformsTo:@protocol(LibraryControl)]) {
        puts ("ObjectLibrary error: "
              "Interpreter doesn't conform to LibraryControl!");
        return NULL;
    }

    [sender addLibraryFunction: "to"
                        selector:@selector(object_to:)
                        target:self];
   return result;
}

@end
