// Sat Oct 12 11:14:48 MET 1996


/* toDo:	- do some guesses for type on new connection (via isKindOf:)
 */

#import "DefaultController.h"

#define DEFCTL_VERSION 1

@interface Object(__WarningKiller)
- target;
-(SEL) action;
@end

// so that all gadgets can notify themselves to us on window-close or app-terminate
@implementation Object(_ListActionPerform)
- _sendAction
{	SEL action=0;
	id	target=nil;
	if([self respondsTo:@selector(target)]) target=[self target];
	if([self respondsTo:@selector(action)]) action=[self action];
	if(target && action) [NXApp sendAction:action to:target from:self];
	return self;
}
@end

@implementation DefaultController:Object

+ initialize
{	if (self == [DefaultController class]) 
	{	/*
		 * **** Archiving: READ ME **** After bumping the _VERSION, it is
		 * considered common practice to add a comment line indicating the new
		 * version number, date, and modifier. Optionally, the reason for the
		 * change. There is no need to modify the setVersion message.
		 */
		// version 1: initial.  (db)
		[[DefaultController class] setVersion:DEFCTL_VERSION];
    }
	return self;
}


- init
{	names=[[Storage alloc] initCount:1 elementSize:sizeof(char*) description:"*"];
	types=[[Storage alloc] initCount:1 elementSize:sizeof(int  ) description:"i"];
	gadgets =[[List alloc] init];
	return self;
}

-(List*) gadgets { return gadgets; }
-(unsigned)indexOf:anObject
{	return [gadgets indexOf:anObject];
}
- removeObjectAt:(unsigned)index
{	id currObjc=[gadgets objectAt:index];
	if(currObjc)
	{	[names removeElementAt:index];
		[types removeElementAt:index];
	} return [gadgets removeObjectAt:index];
}
- objectAt:(unsigned)index
{	return [gadgets objectAt:index];
}
- addObjectIfAbsent:anObject
{	return [self registerName:"SomeDefName" andType:DCT_string for:anObject];
}
-(unsigned) count
{	return [gadgets count];
}

-(const char*) getDefaultOfName:(const char*) defaultName
{	const char *ptr;
	if(!defaultName || !strlen(defaultName)) return "";
	ptr=NXGetDefaultValue([NXApp appName], defaultName);
	return ptr? ptr:"";
}
- loadValueFor:currObjc
{	const char 				*aName;
	DefaultControllerType_t  aType;
	if([self getName:&aName andType:&aType for:currObjc])
	{	switch(aType)
		{	case DCT_string:
				[currObjc setStringValue:[self getDefaultOfName:aName]];
			break;
			case DCT_int:
				[currObjc setIntValue:atoi([self getDefaultOfName:aName])];
			break;
			case DCT_float: case DCT_double:
				[currObjc setFloatValue:atof([self getDefaultOfName:aName])];
			break;
			case DCT_bool:
				if(!strcmp([self getDefaultOfName:aName],"YES"))
						[currObjc setState:1];
				else [currObjc setState:0];
			break;
			case DCT_tag:
				[currObjc selectCellWithTag:atoi([self getDefaultOfName:aName])];
			break;
			case DCT_color:
			{	float r,g,b,a;
				sscanf([self getDefaultOfName:aName],"%f %f %f %f",&r,&g,&b,&a);
				[currObjc setColor:NXConvertRGBAToColor(r,g,b,a)];
			} break;
			default: NXLogError("DefaultController: Undefined type: %d\n",aType);
	}	} return self;
}

- doSetupConnections
{	int	  					 i;
	id	  					 currObjc;

	for(i=0; currObjc=[gadgets objectAt:i];i++)
	{	if ([currObjc respondsTo:@selector(setTarget:)])
			[currObjc setTarget:self];
		if ([currObjc respondsTo:@selector(setAction:)])
			[currObjc setAction:@selector(uiDataChanged:)];
		[self loadValueFor:currObjc];
	} return self;
}

- registerName:(const char*) aName andType:(DefaultControllerType_t) aType for:theGadget
{	char **ptr=malloc(sizeof(char*));
	unsigned pos;
	if(!aName || !strlen(aName) || !theGadget) return nil;
	if((pos=[gadgets indexOf:theGadget]) == NX_NOT_IN_LIST)
	{	[gadgets addObject:theGadget];
		[names addElement:((*ptr)=NXCopyStringBuffer(aName),ptr)];
		[types addElement:&aType];
	} else
	{	[names replaceElementAt:pos with:((*ptr)=NXCopyStringBuffer(aName),ptr)];
		[types replaceElementAt:pos with:&aType];
	} return self;
}
- getName:(const char**) aName andType:(DefaultControllerType_t*) aType for:theGadget
{	unsigned pos;
	if(!theGadget) return nil;
	if((pos=[gadgets indexOf:theGadget]) != NX_NOT_IN_LIST)
	{	if(aName) *aName=*(char**)[names elementAt:pos];	// some crazy indirection
		if(aType) *aType=*(int  *)[types elementAt:pos];
		return self;
	} if([theGadget isKindOf:[Matrix class]])
		return [self getName:aName andType:aType for:[theGadget selectedCell]];
	return nil;
}

- (int)setDefault:(const char *)defName to:(const char *)defValue
{	return NXWriteDefault( [NXApp appName], defName, defValue );
}
- (int)setDefault:(const char *)defName toBool:(BOOL)defValue
{	return [self setDefault:defName to:(defValue ? "YES" : "NO")];
}
- (int)setDefault:(const char *)defName toInt:(int)defValue
{	char intBuffer[80];
	sprintf(intBuffer, "%i", defValue);
	return [self setDefault:defName to:intBuffer];
}
- (int)setDefault:(const char *)defName toDouble:(double)defValue
{	char buf[80];
	sprintf(buf,"%f",defValue);
	return [self setDefault:defName to:buf];
}

- uiDataChanged:sender
{	const char 				*aName,*ptr;
	DefaultControllerType_t  aType;
	int i;

	if([self getName:&aName andType:&aType for:sender])
	{	switch(aType)
		{	case DCT_string:
				[self setDefault:aName to:[sender stringValue]];
			break;
			case DCT_int:
				[self setDefault:aName toInt:[sender intValue]];
			break;
			case DCT_float: case DCT_double:
				[self setDefault:aName toDouble:[sender doubleValue]];
			break;
			case DCT_bool:
				[self setDefault:aName toBool:[sender intValue]];
			break;
			case DCT_tag:
				[self setDefault:aName toInt:[[sender selectedCell] tag]];
			break;
			case DCT_color:
			{	char buf[128];
				float r,g,b,a;
				NXConvertColorToRGBA([sender color],&r,&g,&b,&a);
				sprintf(buf,"%f %f %f %f",r,g,b,a);
				[self setDefault:aName to:buf];
			} break;

			default: NXLogError("DefaultController: Undefined type: %d\n",aType);
		}
	} else NXLogError("DefaultController: Unregistered(unconnected) sender: %s\n",
						[[sender class] name]);
// now do some EOF-like magic¼
	for(i=0;[gadgets objectAt:i] && aName && (ptr=*(char**)[names elementAt:i]) ;i++)
	{	if(!strcmp(aName,ptr)) [self loadValueFor:[gadgets objectAt:i]];
	} return self;
}

-  windowWillClose:sender
{	[gadgets makeObjectsPerform:@selector(_sendAction)];	// before we close collect all data
	return self;
}

- appWillTerminate:sender
{	[gadgets makeObjectsPerform:@selector(_sendAction)];	// before we close collect all data
	return self;
}

- awakeFromNib
{	[self doSetupConnections];
	return self;
}

#if 0
- copyFromZone:(NXZone*) theZone
{	id copy=[super copyFromZone:theZone];
//
	return copy;
}
#endif

// archiving
- read: (NXTypedStream*)stream
{	int version;
	[super read:stream];
	switch (version = NXTypedStreamClassVersion(stream, "DefaultController"))
	{	case 0:	// unimplemented
		break;
		case DEFCTL_VERSION:
			gadgets= NXReadObject(stream);
			names=NXReadObject(stream);
			types=NXReadObject(stream);
		break;
		default:
			NXLogError("[%s %s] - unknown version of %s in typed stream",
				[[self class] name], sel_getName(_cmd), 
				[[DefaultController class] name]);
		break;

	} return self;
}
- write: (NXTypedStream*)stream
{	[super write:stream];

	NXWriteObject(stream,gadgets);
	NXWriteObject(stream,names);
	NXWriteObject(stream,types);

	return self;
}


- free
{	[gadgets free];	// do not free the objects!
	[names free];
	[types free];
	return [super free];
}

@end