//
//	MiscNibController.m -- an abstract superclass to load .nib files with
//			windows in them
//		Written by Mike Ferris (c) 1994 by Mike Ferris.
//		Modified from original MOKit "MOController" class by Don Yacktman.
//				Version 1.0.  All rights reserved.
//
//		This notice may not be removed from this source code.
//
//	This object is included in the MiscKit by permission from the author
//	and its use is governed by the MiscKit license, found in the file
//	"LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
//	for a list of all applicable permissions and restrictions.
//	


#define MISC_STRING_CLASS_NAME			"MiscString"
#define MISC_CLASSVARIABLE_CLASS_NAME	"MiscClassVariable"

#define CLASS_VERSION	0
#define CLASS_NAME		"MiscNibController"

#import <misckit/misckit.h>
//#import <misckit/MiscString.h>
//#import <misckit/MiscClassVariable.h>
//#import <misckit/MiscBundleLoader.h>
#import <objc/objc-runtime.h>

@implementation MiscNibController

// True class variable object
static MiscClassVariable *_MiscClassNibName;

// Looked up classes we use
static Class MiscStringClass;
static Class MiscClassVariableClass;

+ initialize
// Set the version.  Load classes, and init class variables.
{
	if (self == objc_lookUpClass(CLASS_NAME))  {
		[self setVersion:CLASS_VERSION];

		// Load supporting classes if necessary
		MiscStringClass = [self loadClassBundle:MISC_STRING_CLASS_NAME];
		MiscClassVariableClass = [self loadClassBundle:
					MISC_CLASSVARIABLE_CLASS_NAME];
		
		// Set up class variables
		_MiscClassNibName = [[MiscClassVariableClass allocFromZone:[self zone]] 
					initDoesFreeValues:YES];
	}
	return self;
}

+ startUnloading
// Free our class variables.
{
	[_MiscClassNibName free];
	return self;
}

+ setClassNib:(const char *)nibName
// Set the nib file which contains the window this controller class controls.
{
	id newVal;

	if (nibName) {
		newVal = [[MiscStringClass allocFromZone:[self zone]] 
				initStringValue:nibName];
	} else {
		newVal = nil;
	}
	[_MiscClassNibName setObject:newVal forClass:self];
	return self;
}

+ (const char *)classNib
// Returns the nib file name for this controller class.
{
	return [(MiscString *)[_MiscClassNibName getObjectForClass:self] stringValue];
}

- init
// Just calls the designated initializer.
{
	return [self initWithFrameName:NULL];
}

- initWithFrameName:(const char *)theFrameName
// Designated Initializer.
// Initialize some instance variables.
{
	[super init];
	nibIsLoaded = NO;
	window = nil;
	frameName = [[MiscStringClass allocFromZone:[self zone]] 
				initString:theFrameName];
	return self;
}

- free
// Free our window, and frameName.
{
	if (window)  [window free];
	[frameName free];
	return [super free];
}

- awake
// Sets up ivars we don't archive.
{
	window = nil;
	nibIsLoaded = NO;
	return self;
}

- read:(NXTypedStream *)strm
// This method is probably not useful, but here it is for completeness sake.
{
	int classVersion;

	[super read:strm];
	
	classVersion = NXTypedStreamClassVersion(strm, CLASS_NAME);
	
	switch (classVersion)  {
		case 0:		// First version.
			frameName = NXReadObject(strm);
			break;
		default:	// bail on anything else.
			NXLogError("[%s read:] class version %d cannot read "
						"instances archived with version %d", 
						CLASS_NAME, CLASS_VERSION, classVersion);
			frameName = [[MiscStringClass allocFromZone:[self zone]] init];
			break;
	}
	return self;
}

- write:(NXTypedStream *)strm
// This method is probably not useful, but here it is for completeness sake.
{
	[super write:strm];
	NXWriteObject(strm, frameName);
	return self;
}

- setFrameName:(const char *)theFrameName
// Sets the name to save the windows frame info under in the defaults 
// database.
{
	[frameName setStringValue:theFrameName];
	return self;
}

- (const char *)frameName
// Return the name to save the windows frame info under in the defaults 
// database.
{
	const char *fn = [frameName stringValue];
	if (!(fn) || !(*fn)) return NULL;
	return fn;
}

- window
// Return the window.  (Returns nil if the nib file is not loaded.)
{
	return [self window:NO];
}

- window:(BOOL)loadFlag
// Return the window.  (If loadFlag is NO and window is not loaded, 
// returns nil.  If loadFlag is YES, window is loaded if necessary.)
{
	if (loadFlag)  [self loadNibIfNeeded];
	return window;
}

- showWindow:sender
// Show the window (loading it first if necessary).
{
	[self loadNibIfNeeded];
	if (window)  [window makeKeyAndOrderFront:sender];
    return self;
}

- loadNibIfNeeded
// Loads the nib file if it hasn't already been loaded.  Also performs
// set up that must be deferred till the nib is loaded.
{
	const char *nibName = [[self class] classNib];
	const char *frmName = NULL;

	if (!(nibName) || !(*nibName)) { // try to default to the class' name
		nibName = [[self class] name];
	}
	if ((!nibIsLoaded) && (nibName) && (*nibName))  {
		[self nibWillLoad];
		if (![self loadNib:nibName withOwner:self fromBundle:nil]) return nil;
		nibIsLoaded = YES;

		// configure window
		frmName = [self frameName];
		if ((frmName) && (*frmName))  { // string exists and is len > 0
			[window setFrameAutosaveName:frmName];
			[window setFrameUsingName:frmName];
		}
		[self nibDidLoad];
	}
	return self;
}

- loadNib:(const char *)name withOwner:owner fromBundle:(NXBundle *)bundle
{	// This code actually loads a .nib file
	// name if NULL defaults to [self name], or [[self class] name] if
	//    self doesn't have a name
	// owner defaults to self if nil is passed in
	// bundle defaults to the bundle for [self class] if nil is passed in
    char path[MAXPATHLEN+1];
	const char *realName = (name ? name :
			([self name] ? [self name] : [[self class] name]));

	// find the path to the .nib file; check appropriate bundle, getting
	// the name as appropriate
	if (![(bundle ? bundle : [NXBundle bundleForClass:[self class]]) 
			getPath:path forResource:realName ofType:"nib"]) {
		NXLogError("[%s loadNib:withOwner:fromBundle:]: failed to "
				"find nib file %s.", 
				[[self class] name], realName);
		return nil;
	}
    if (![NXApp loadNibFile:path owner:(owner ? owner : self)
			withNames:NO fromZone:[self zone]]) {
		NXLogError("[%s loadNib:withOwner:fromBundle:]: failed to "
				"load nib file %s.", 
				[[self class] name], path);
		return nil;
	}
    return self;    
}

// cover methods:  (are any of these actually useful?)
- loadNib
{
	return [self loadNib:NULL	withOwner:nil	fromBundle:nil];
}

- loadNib:(const char *)name
{
	return [self loadNib:name	withOwner:nil	fromBundle:nil];
}

- loadNib:(const char *)name withOwner:owner
{
	return [self loadNib:name	withOwner:owner	fromBundle:nil];
}

- loadNib:(const char *)name fromBundle:bundle
{
	return [self loadNib:name	withOwner:nil	fromBundle:bundle];
}

- nibWillLoad
// This is called before our nib has been loaded.  You may override it to do
// whatever needs to be done, but call super's implementation first.
{
	return self;
}

- nibDidLoad
// This is called after our nib has been loaded.  You may override it to do
// whatever needs to be done, but call super's implementation first.
{
	return self;
}


@end
