//*****************************************************************************
//
//	DLDelegate.m.  
//
//		Document List controller and NXBrowser delegate
//
//			by	Felipe A. Rodriguez		
//
//	This code is supplied "as is" the author makes no warranty as to its 
//	suitability for any purpose.  This code is free and may be distributed 
//	in accordance with the terms of the:
//		
//			GNU GENERAL PUBLIC LICENSE
//			Version 2, June 1991
//			copyright (C) 1989, 1991 Free Software Foundation, Inc.
// 			675 Mass Ave, Cambridge, MA 02139, USA
//
//*****************************************************************************

#import <sys/types.h>
#import <sys/dir.h>  /* POSIX applications #include <dirent.h> */
#import <appkit/appkit.h>
#import <dbkit/DBImageView.h>

#import "GKdefs.h"
#import "Coordinator.h"
#import "DLDelegate.h"
#import "DLRecord.h"
#import "AlphaDBImageView.h"
#import "GateDocEditor.h"





@implementation DLDelegate

//*****************************************************************************
//
//		sent to us by controller from appDidInit 
//
//*****************************************************************************

- awake 
{										
    [super awake];
	[self testForDataDirectory];		// test existence of Gate doc directory
	[[SDImageView setEditable:NO] setStyle:DB_ImageNoFrame];

	if(![[NXBundle mainBundle]getPath:oPath forResource:"options" ofType:NULL])
		oPath[0] = '\0';
	[self browser:nil fillMatrix:nil inColumn:0];				// load db
	[self initDList];		

    return self;
}
//*****************************************************************************
//
// 		initialize the doc directory and reinit if path to it is changed 
//
//*****************************************************************************

- initDList 
{
char *sC;
				//	cell selection in doc dir should persist between sessions
				//	so recall previous selection
	sC = NXCopyStringBuffer(NXGetDefaultValue([NXApp appName], SELCELL));		
	selCell = atoi(sC);
	free(sC);

	if([dirList count] > 0)			// if doc dir path is valid
		{
		if(!(selCell < [dirList count]))
			selCell = 0;
		if(!(image = [[dirList objectAt:selCell] gateIcon]))	
			image = [NXImage findImageNamed: "Blank"];	// else use blank
		}													
	else
		image = [NXImage findImageNamed: "Blank"];		// else use blank
	[SDImageView setImage:image];
	[ISPField setStringValue:[self extractName:
									[[dirList objectAt:selCell] gateDocName]]];

	return self;
}
//*****************************************************************************
//
// 		return dir List object 
//
//*****************************************************************************

- DList 
{
	return dirList;
}
//*****************************************************************************
//
// 		provide to listBrowser(NXBrowser) it's data to be displayed
//			do this because we are its delegate  
//
//*****************************************************************************

- (int)browser:b fillMatrix:m inColumn:(int)c
{
int	i = 0, strSize;
id t, obj = nil;
char *ptr;
NXHashState state;
const void  *key; 
void  *value; 
struct direct *dirp;
static DIR *dp = NULL, *dpOLD = NULL;

	if(dp)
		dpOLD = dp;
	if(dirList)
		[dirList empty];					// empty list
	else
		dirList = [[List alloc] init];		// produce an array of objects
    if ((dp = opendir(NXGetDefaultValue([NXApp appName], LIBPATH))) != NULL)
		{
		if(!namHashTable)				// alloc hashtable w/str keys, obj vals
			namHashTable = [[HashTable alloc] initKeyDesc:"*" valueDesc:"@"];
		else
			{
			tmpHashTable = [namHashTable copyFromZone:[self zone]];
			[namHashTable empty];
			}
		while (dirp = readdir(dp)) 				// read dir and find all
			{									// entries with ext .Gate
//			if(dirp->d_namlen > 5)		// future use
			if((strSize = strlen(dirp->d_name)) > 5)
				{
				ptr = dirp->d_name + strSize;		// point to end of name str
				ptr -= 5;						
				if(strcmp(ptr, ".Gate") == 0)		// if extension is .Gate
					{								// if str not in hash table
					if ([tmpHashTable isKey:dirp->d_name] == NO) 	
						{							// create a doc dir record
						obj = [[DLRecord alloc] init];	
						[obj setGateDocName:dirp->d_name];
						}							// if record exists in hash
					else							// use existing record
						obj = [tmpHashTable valueForKey:dirp->d_name];
					[namHashTable insertKey:[obj gateDocName] value:obj];
					}
				}
			};
		[tmpHashTable free];
		if(dpOLD)
			closedir(dpOLD);
									// sort and display dir in NXBrowser
		for(i = 0; i < [namHashTable count]; i++) 
			{						// Return NXHashState structure required 
									// when iterating through the HashTable
			state = [namHashTable initState]; 
			while ([namHashTable nextState:&state key:&key value:&value]) 
				{					// iterate thru the hashtable and perform
				if ([dirList indexOf:value] == NX_NOT_IN_LIST)
					{
					if(!obj)
						obj = value;					// an ascending sort
					if(strcasecmp(key, [obj gateDocName]) < 0)		
						obj = [namHashTable valueForKey:key];
					}			
				};
			if(m)
				{
				[m addRow];							// add row to doc dir matrx
				t = [m cellAt:i :0];				// ret cell in NXBrowser
				[t setStringValueNoCopy:[obj gateDocName]];
				[t setLoaded:YES];
				[t setLeaf:YES];
				}
			[dirList addObject:obj];
			obj = nil;
			}
		}
	else											// doc dir dir is invalid	
		[[NXApp delegate] showAlert:"Error opening document directory."];

    return i; 
}
//*****************************************************************************
//
//  target/action for a single click in the browser. 
//
//*****************************************************************************

- browserHit:sender
{   
  	selCell = [[sender matrixInColumn:0] selectedRow];
	[self setDispPerCellAt:selCell];			// display traits of sel'd cell

    return self;
}
//*****************************************************************************
//
//  target/action method for a double click in the browser.  
//
//*****************************************************************************

- browserDoubleClick:sender
{   
  	selCell = [[sender matrixInColumn:0] selectedRow];
	[self setDispPerCellAt:selCell];			// display traits of sel'd cell
	if([dirList count] >= 1)					// open sel'td doc dir file
		[[NXApp delegate] app:self openFile:
						[[dirList objectAt:selCell] gateDocPath] type:NULL];
	else						
		[[NXApp delegate] showAlert:"noSelectedDoc"];

    return self;
}
//*****************************************************************************
//
// 	target/action for a single click in the preferences browser. 
//
//*****************************************************************************

- pBrowserHit:sender
{   
  	selCell = [[sender matrixInColumn:0] selectedRow];
//	[mat selectCellAt:selCell :0];
	[self setDispPerCellAt:selCell];			// display traits of sel'd cell
//	[mat scrollCellToVisible:selCell :0];

    return self;
}
//*****************************************************************************
//
// 	target/action method for a double click in the preferences browser.  
//
//*****************************************************************************

- pBrowserDoubleClick:sender
{   
  	selCell = [[sender matrixInColumn:0] selectedRow];
//	[mat selectCellAt:selCell :0];
	[self setDispPerCellAt:selCell];			// display traits of sel'd cell
//	[mat scrollCellToVisible:selCell :0];
	if([dirList count] >= 1)					// open sel'td doc dir file
		[[NXApp delegate] app:self openFile:
						[[dirList objectAt:selCell] gateDocPath] type:NULL];
	else						
		[[NXApp delegate] showAlert:"noSelectedDoc"];

    return self;
}
//*****************************************************************************
//
// 		go to next gate doc in doc dir 
//
//*****************************************************************************

- nextItem:sender 
{
	[sender setState:0];
	if(selCell < ([dirList count] - 1))
		selCell++;
	else
		selCell = 0;
//	[mat selectCellAt:selCell :0];
	[self setDispPerCellAt:selCell];			// display traits of sel'd cell
//	[mat scrollCellToVisible:selCell :0];

	return self;
}
//*****************************************************************************
//
// 		go to previous gate doc in doc dir 
//
//*****************************************************************************

- prevItem:sender 
{
	[sender setState:0];
	if(selCell > 0)
		selCell--;
	else
		selCell = [dirList count] - 1;
//	[mat selectCellAt:selCell :0];
	[self setDispPerCellAt:selCell];			// display traits of sel'd cell
//	[mat scrollCellToVisible:selCell :0];

	return self;
}
//*****************************************************************************
//
// 		set views per selected cell in doc directory 
//
//*****************************************************************************

- setDispPerCellAt:(int)selectedCell 
{
	[ISPField setStringValue:[self extractName:
									[[dirList objectAt:selCell] gateDocName]]];
	if(!(image = [[dirList objectAt:selectedCell] gateIcon]))	// if imge
		image = [NXImage findImageNamed: "Blank"];		// else use blank
	[SDImageView setImage:image];
	if(toolBarView)
		[toolBarView setImage:image];

	return self;
}
//*****************************************************************************
//
// 		link based on selected document  
//
//*****************************************************************************

- link 
{
	if([dirList count] >= 1)					// open sel'td doc dir file
		{
		[[NXApp delegate]linkWithFile:[[dirList objectAt:selCell]gateDocPath]];
//		[ISPField setStringValue:[self extractName:
//									[[dirList objectAt:selCell] gateDocName]]];
		}
	else						
		[[NXApp delegate] showAlert:"noSelectedDoc"];

	return self;
}
//*****************************************************************************
//
// 		forward anything in Greek to someone who might understand 
//
//*****************************************************************************

- forward:(SEL)aSelector :(marg_list)argFrame
{
//    if ( [toolWin respondsTo:aSelector] )
//        return [toolWin performv:aSelector :argFrame];

    [self doesNotRecognize:aSelector];					// error abort process

	return self;
}
//*****************************************************************************
//
// 		app will terminate so save selected cell, browser size
//
//*****************************************************************************

- appWillTerminate
{
char tmpStr[16];

	sprintf(tmpStr, "%d", selCell);
								// write an entry to defaults database
	if(!NXWriteDefault([NXApp appName], SELCELL, tmpStr))
		[[NXApp delegate] showAlert:"ddbWriteError"];

								// save current doc path to ddb
	if(!NXWriteDefault([NXApp appName], CDOC, 
									 [[dirList objectAt:selCell] gateDocPath]))
		[[NXApp delegate] showAlert:"ddbWriteError"];

	return self;
}
//*****************************************************************************
//
// 		free gets rid of everything we created
//
//*****************************************************************************

- free
{
	if(dirList)
		{
		[dirList freeObjects];							// free list's objects
		[dirList free];									// free list object
		}		
	if(namHashTable)
		[namHashTable free];									

    return [super free];
}
//*****************************************************************************
//
// 		send images back to this object 
//
//*****************************************************************************

- setToolBar:aView 
{
	toolBarView = aView;
	[toolBarView setImage:image];

	return self;
}
//*****************************************************************************
//
// 		return the selectedCell 
//
//*****************************************************************************

- (int)hLSelectedCell 
{
	return selCell;
}
//*****************************************************************************
//
// 		return the path to the selected gate docs options file 
//
//*****************************************************************************

- (const char *)selGateDocOptionsPath 
{
	if([dirList count] >= 1)							// path of selected doc
		{
		strncpy(oPath,[[dirList objectAt:selCell] gateDocPath], MAXPATHLEN);			
		strncat(oPath, OPTION, MAXPATHLEN - strlen(oPath));

		return oPath;
		}
	else						
		[[NXApp delegate] showAlert:"noSelectedDoc"];

	return NULL;
}
//*****************************************************************************
//
// 		test for existence of library directory, create if necessary
//
//*****************************************************************************

- testForDataDirectory
{
static char *libSearch[] = {NULL, "~/Library/", "~/LocalLibrary/", "/Library/", 
													"/LocalLibrary/", NULL};
int i = 0;
static BOOL init = YES;
int ret = -1;

	libSearch[0] = 							// chk preference setting first
			NXCopyStringBuffer(NXGetDefaultValue([NXApp appName], LIBPATH));

	while((ret == -1) && libSearch[i] != NULL)			// search for dir 
		{
		if(*libSearch[i] == '~')						// if rel path
			{											// prepend home dir
			strncpy(Path, NXHomeDirectory(), MAXPATHLEN);	
			if(Path[0] == '/')							// if home is root dir
				strncpy(Path, libSearch[i++], MAXPATHLEN);
			else
				strncat(Path, libSearch[i++], MAXPATHLEN - strlen(Path));
			}
		else											// else absolute path
			strncpy(Path, libSearch[i++], MAXPATHLEN - strlen(Path));
		if(i > 1)				// append app name to def lib search entries
			strncat(Path, [NXApp appName], MAXPATHLEN - strlen(Path));	
     	if((ret = access(Path, F_OK)) != -1)	// test for existence of file
			{									// save doc dir path to ddb	
			if(!NXWriteDefault([NXApp appName], LIBPATH, Path))
				[[NXApp delegate] showAlert:"ddbWriteError"];
			}	
		if(!init)								// only first invocation
			while(libSearch[i++] != NULL);		// searchs all paths
		}
	if(ret == -1)								
		{										// not found, so create doc dir
		strncpy(Path, NXHomeDirectory(), MAXPATHLEN);
		if(Path[0] == '/')						// if home is root dir
			strncpy(Path, libSearch[3], MAXPATHLEN);
		else
			strncat(Path, libSearch[3], MAXPATHLEN - strlen(Path));
		strncat(Path, [NXApp appName], MAXPATHLEN - strlen(Path));	

    	mkdir(Path, (_S_IRUSR | _S_IWUSR | _S_IXUSR));	  	// owner rws
     	if((ret = access(Path, F_OK)) != -1)	// test for existence of file
			{									// save doc dir path to ddb
			if(!NXWriteDefault([NXApp appName], LIBPATH, Path))
				[[NXApp delegate] showAlert:"ddbWriteError"];
			}
		else
			[[NXApp delegate] showAlert:"Error accessing library directory."];				
		chown(Path, getuid(), getgid());	// set owner to real user
											// create example doc in ddir
		strncat(Path, "/MyISP.Gate", MAXPATHLEN - strlen(Path));
     	if(mkdir(Path, (_S_IRUSR | _S_IWUSR | _S_IXUSR)) == -1)	  
			[[NXApp delegate] showAlert:"Error creating Gate doc."];
		else
			{
			chown(Path, getuid(), getgid());
			[theGateDocEditor addDefaultFiles:Path];
			[theGateDocEditor editGateDoc:Path];
			}
		}
	free(libSearch[0]);	
	init = NO;					// subsequent invocations will only search
								// preference set path
    return self;
}
//*****************************************************************************
//
//		extracts the Gate doc name from its path 
//
//*****************************************************************************

- (const char *)extractName:(const char *)aPath
{
static char pathBuf[MAXPATHLEN];
char *ptr;

	strcpy(pathBuf, aPath);
	ptr = pathBuf + strlen(pathBuf) - 1;		
	while(*ptr != '.' && *ptr)
		ptr--;
	*ptr-- = '\0';
	while(*ptr != '/' && *ptr)
		ptr--;
	++ptr;

    return ptr;
}
//******************* status Window delegate methods **************************
//*****************************************************************************
//
// 		called whenever the user closes our status window.
//
//*****************************************************************************

- windowWillClose:sender  
{
	if(!NXWriteDefault([NXApp appName], DISPLAYS, "NO"))
		[[NXApp delegate] showAlert:"ddbWriteError"];

    return self;
}

@end
