//  Thinspector.m
//
//  contains more routines for the Thinker class, mostly related to
//  BackSpace's inspectors.
//
//  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 "Thinker.h"
#import "BackWindow.h"
#import "BackView.h"
#import "SpaceView.h"
#import "MySlider.h"
#import "Password.h"
#import "psfuncts.h"
#import "ModuleList.h"


#import <appkit/appkit.h>
#import <objc/NXBundle.h>
#import <sys/dir.h>				/* for opendir(), etc. */


@implementation Thinker(inspector)

- commonImageInspector
{
	return commonImageInspector;
}

- nullInspector
{
	return nullInspector;
}

- spaceInspector
{
	return spaceInspector;
}

- boinkInspector
{
	return boinkInspector;
}

- revertToDefaultImage:sender
{
	[self setImageFromName: "defaultImage"];
	NXRemoveDefault([NXApp appName], "imageFile");
	return self;
}

- (BOOL)browser:sender columnIsValid:(int)column
{
	return browserValid;
}

- addCellWithString:(const char *)str at:(int)row toMatrix:matrix
{
	id theCell;
	
	[matrix insertRowAt:row];
	theCell = [matrix cellAt:row :0];
	[theCell setStringValue:str];
	[theCell setLoaded:YES];
	[theCell setLeaf:YES];
	return self;
}

- (int)browser:sender fillMatrix:matrix inColumn:(int)column
{
	const char *ptr;
	int i;
	
	// this shouldn't happen...
//	if (browserValid) return [matrix cellCount];

	[self addCellWithString:NXLocalString("All", 0, 0)
		at:0 toMatrix:matrix];
	
	for (i = 0; i < [moduleList count]; i++)
		[self addCellWithString:NXLocalString([moduleList nameAt: i], 0, 0)
			at:(i+1) toMatrix:matrix];
			
	realViewIndex = 0;

	if(modeType == SAVERMODE) 
	{
		ptr = NXGetDefaultValue([NXApp appName], "viewType");
	}
	else // (modeType == BACKMODE)
	{
		ptr = NXGetDefaultValue([NXApp appName], "backViewType");
	}

	if (ptr)
	{
	    for (i = 0; i < [moduleList count]; i++)
		if (strcmp(ptr, [moduleList nameAt: i]) == 0)
		{
		    realViewIndex = i+1;
		    break;
		}
	}
	
	browserValid = YES;
	return [matrix cellCount];
}


//  Dynamically load all object files found in the specified directory
//	if we find a module in several places, we save the additional paths
//	in case they point to modules for different architectures

- loadViewsFrom: (const char *) dirname
{
    DIR *dir;
    struct direct *de;
    char path[MAXPATHLEN];
    char name[60];
	char *iptr;
	ModuleInfo *m;
	BOOL validName, filePackage;


    dir = opendir(dirname);
    if (dir == NULL) {
		//perror(dirname);
		return self;
    }

    while ((de = readdir(dir)) != NULL)
	{
		int i, numstrings;
	
		// Ignore '.'-files (not really necessary, I guess)
		if (de->d_name[0] == '.')
			continue;

		filePackage = validName = NO;
		if (de->d_namlen > 10 && 
				!strcmp(&de->d_name[de->d_namlen-10], "View.BackO"))
		{
			validName = YES;
			filePackage = NO;
		}
		else if (de->d_namlen > 15 && 
				!strcmp(&de->d_name[de->d_namlen-15], "View.BackModule"))
		{
			validName = YES;
			filePackage = YES;
		}

		if (!validName) continue;


		// refuse to load if the name matches a module already loaded
		numstrings = [moduleList count];
		strcpy(name, de->d_name);

		// Smash out the 'V' in "FooView.BackO"
		if (iptr = rindex(name, 'V'))
			*iptr = '\0';

		for (i=0; i< numstrings; i++)
		{
			if (!strcmp(name, [moduleList nameAt:i]))
			{
				// we already have a module with this name, but will save the path anyway
				validName = NO;
				if (filePackage) sprintf(path,"%s/%sView.BackModule",dirname,name);
				else strcpy(path, dirname);
				[[moduleList objectAt:i] appendPath:path];
				break;
			}
		}
		if (!validName) continue;
		
		// I used to load the class at this time; this got horribly
		// inefficient.  I now wait until I'm about to instantiate
		// a view before doing this (thanx bbum!)

		if (filePackage) sprintf(path,"%s/%sView.BackModule",dirname,name);
		else strcpy(path, dirname);
		
		m = [[ModuleInfo alloc] 
			initWithView:NULL name:name path:path];
	    [moduleList addObject: m];
    }

    closedir(dir);

    return self;
}

- (BOOL)appAcceptsAnotherFile:sender
{
    // return openAnother;
	// if I just return openAnother, I fail to open the module if
	// I get launched to do it.  Punt until I think of a better solution.
    return YES;
}

- (int)app:sender openFile:(const char *)filename type:(const char *)aType
{
	// Here we have caught BackSpace off guard; Speaker says open a file,
	// but BackSpace is already locked on a view because it doesn't unlock
	// focus or bail out of the event loop unless there's an event.
	// This is good for performance but it means it handles openfile badly;
	// it must post an event telling it to bail out of the event loop and
	// open the file later; multi file opens are therefore broken, and
	// the return value here is bogus...
	
    char path[MAXPATHLEN];
	NXEvent anEvent;
	char *iptr;

	if (fileToOpen)
	{	free(fileToOpen);
		fileToOpen = NULL;
	}
	
	strcpy(path,filename);

	if (!strcmp(aType,"BackModule"))
	{
		iptr = rindex(filename, '/');
		strcat(path,iptr);			// /xxx/fooView.BackModule/fooView.BackModule
		iptr = rindex(path, 'M');
		strcat(iptr,"O");			// /xxx/fooView.BackModule/fooView.BackO
	}

	fileToOpen = NXCopyStringBuffer(path);
	openAnother = NO;
		
	anEvent.type = NX_APPDEFINED;
	anEvent.data.compound.subtype = BSOPENFILE;
	anEvent.ctxt = [NXApp context];
	DPSPostEvent(&anEvent,0);
	return YES;
}

- doDelayedOpenFile
{
	int i, numstrings;
	int ret = YES;
    char path[MAXPATHLEN];
	id theMatrix;
	char *name;
	char *iptr;
	ModuleInfo *m;

	numstrings = [moduleList count];
	strcpy(path, fileToOpen);

	if ((name = rindex(path, '/')) != NULL)
		*name = '\0';
	name++;

	// Smash out the 'V' in "FooView.BackO"
	if ((iptr = rindex(name, 'V')) != NULL)
		*iptr = '\0';

	for (i=0; i< numstrings; i++)
	{
		if (!strcmp(name, [moduleList nameAt:i]))
		{	ret = NO;
			break;
		}
	}
		
	if (ret)
	{
		m = [[ModuleInfo alloc] 
			initWithView:NULL name:name path:path];
	    [moduleList addObject: m];

		browserValid = NO;
		[moduleList sort];
	
		[viewSelectionBrowser loadColumnZero];
	}
	
	theMatrix = [viewSelectionBrowser matrixInColumn:0];
	numstrings = [moduleList count];
	for (i=0; i< numstrings; i++)
	{
		if (!strcmp(name, [moduleList nameAt:i]))
			break;
	}
	realViewIndex = i+1;
		
	[theMatrix selectCellAt:realViewIndex :0];
	[theMatrix scrollCellToVisible:realViewIndex :0];
	[viewSelectionBrowser display];

	[self setVirtualViewIndexAndIncrement:NO];

	free(fileToOpen);
	fileToOpen = NULL;
	openAnother = YES;
	
	return self;
}


extern const char *appDirectory();
- (const char *) appDirectory
{
	return appDirectory();
}

- (const char *) moduleDirectory:(const char *)name
{
	int i, numstrings;
	
	numstrings = [moduleList count];
	for (i=0; i< numstrings; i++)
	{
		if (!strcmp(name, [moduleList nameAt:i]))
			return [[moduleList objectAt:i] path];
	}
	return NULL;
}

- (struct mach_header *) headerForModule:(const char *)name
{
	int i, numstrings;
	
	numstrings = [moduleList count];
	for (i=0; i< numstrings; i++)
	{
		if (!strcmp(name, [moduleList nameAt:i]))
			return [[moduleList objectAt:i] header];
	}
	return NULL;
}




static char *launchDir = NULL;	/* if this is NULL, it will be calculated */

/**
 **	which-dir -- find the path for the named exectuable file
 ** Returns malloc'd string if it finds it, or NULL if it can't
 **
 **	borrowed from Lennart, munged by sam
 **/

const char *which_dir(const char *file)
{
    char buf[MAXPATHLEN];
    char *paths, *p, *q;
	char *ret;
    int dirlen;

    paths = getenv("PATH");
    if (paths == NULL) return NULL;

    for (p = q = paths; *q != '\0'; p = q + 1)
	{
		q = index(p, ':');
		if (q == NULL) q = p + strlen(p);

		dirlen = q - p;

		sprintf(buf, "%.*s/%s", dirlen, p, file);
		if (access(buf, X_OK) != 0) continue;

		buf[dirlen] = '\0';
		ret = malloc((dirlen+1)*sizeof(char));
		strcpy(ret,buf);
		return ret;
    }
	return NULL;
}

const char *appDirectory()
{
    char buf[MAXPATHLEN];
    char *suffix, *path;

	if (launchDir) return launchDir;
	
    strcpy (buf,NXArgv[0]);
    suffix = rindex(buf,'/');
    if (suffix != NULL)			// explicit path
	{
		*suffix = '\0'; 		// remove executable name
	}
	else
	{
		path = (char *)which_dir([NXApp appName]);
		if (path != NULL)
		{
			launchDir = path;
			return launchDir;
		}
		else
		{
			// Don't know where we came from -- arbitrarily
			// presume "." and continue (it's better than breaking)
			strcpy(buf, ".");
		}
    }

	launchDir = malloc((strlen(buf)+1)*sizeof(char));
	strcpy(launchDir,buf);
	return launchDir;
}

@end

