
//======================================================================
//
//	Portions written by FreemanSoft Inc.
//
//	FreemanSoft disclaims any warranty of any kind, expressed or implied,
//	as to this source code's fitness for any particular use.
//
//	For more information, use the following electronic mail addresses:
//	 
//		info@FreemanSoft.com	general questions
//		support@FreemanSoft.com	technical questions
//
//======================================================================


/* Written by
 *	Joe Freeman	jfreeman@next.com	
 *	JumpBackControl
 *
 *	This code has no warranty.  
 *	It is provided so that the consumer may maintain and modify it
 *	at their own risk.  Use this code at your own risk.
 */

#import "JumpBackControl.h"
#import "RdistControl.h"
#import "CDROMIndexControl.h"
#import "TarControl.h"
#import "PreferencesPanel.h"
#import "common.h"
#import "pathutil.h"
#import <sys/vnode.h>		/* AAACHH!  unix dweebie stuff !! */

@implementation JumpBackControl

/*================================================================
 *
 *================================================================*/

    
/*================================================================
 *
 *================================================================*/

- init
{
    [super init];
    
    dragBackList = [[List alloc] init];
    scriptBackList = [[List alloc] init];
    return self;
}

- free
{
    [dragBackList free];
    [scriptBackList free];
    return self;
}

- appDidInit:sender
{
    /* YOW!  even the default keys and values are localized */
    const NXDefaultsVector JumpBackDefaults = {
    	{info_default, 	yes_str},
	{show_drag,	yes_str},
	{path_default, 	rdist_relative},
	{dup_default, 	dup_update},
	{find_no_write,	yes_str},
	{debug,		"0"},
	{NULL}
    };
    char buf[MAXPATHLEN+1];    

    NXRegisterDefaults([NXApp appName], JumpBackDefaults);
    
    /* make our custom lib dir first -- so other modules don't have to worry */
    /* ignore return  because we don't care if the dir is already there */
    strcpy(buf, NXHomeDirectory());
    strcat(buf,"/");
    strcat(buf,lib_dir);
    mkdir(buf, VREAD | VWRITE | VEXEC );

    /* build our predefined rdist's menu */
    [self loadPredefined:self];
    
    prefPanel = [PreferencesPanel new];
    
    /* load the info panel if novice user */
    if (!strcmp(NXGetDefaultValue([NXApp appName],info_default), yes_str))
	[self showInfoPanel:self];

    /* show a drag style (standard) JumpBack window if user wants it */
    if (!strcmp(NXGetDefaultValue([NXApp appName], show_drag), yes_str))
	[self newDragController:self];

    return self;
}

- appWillTerminate:sender
{
    int i;
    int lastDragBack ;
    
    for ( i = 0 ; i < [dragBackList count]; i++)
    {
    	if ([[dragBackList objectAt:i] state] != NOTHING_RUNNING)
	{
       		if (	NXRunAlertPanel([NXApp appName],
			something_running,
			dont_close, close_anyway,NULL) == NX_ALERTDEFAULT )
			return nil;
		else
			break;
	}
    }
    
    lastDragBack= [dragBackList count]-1;
    
    for ( i = lastDragBack  ; i >= 0  ; i--)
    {
    	/* no way to detect window wasn't closed */
        [[dragBackList objectAt:i] abortProcess:self];
        [[[dragBackList objectAt:i] window] performClose:self];
    }
    return self;
}

/*================================================================
 *
 *================================================================*/

- loadPredefined:sender
{
    NXStream *	stream;
    char *	lineStart;		/* start of current line in data */
    char *	tab1;			/* tab that is end of menu */
    char *	tab2;			/* tab that is end of src */
    
    char *	data;			/* stream buffer */
    int 	length;
    int 	maxLength;
    
    int 	i;
    MenuCell *	newCell;		/* while we are building the menu */
    
    stream = [self mapResource:pre_def_file];
    
    /* get the buffer , and count the number of lines */
    NXGetMemoryBuffer(stream, &data, &length, &maxLength);
    if (length == 0){
    	NXClose(stream);
	return nil;
    }
    
    /* figure out how many lines there are */
    pairNum = 0;
    lineStart = data;
    tab1 = tab2 = NULL;
    /* see if the first line is a comment line */
    for ( i = 0 ; i <= length; i++){
    	if (pairNum >= JBC_MAX_PAIR)
		break;
	
	if ((i < length) && data[i] == '\t')
	{
		if (!tab1 )
			tab1 = &data[i];
		else if (!tab2 )
			tab2 = &data[i];
		else
		{
			if (debug_level >= debug_file_parse)
				fprintf(stderr,"Found extra tab char\n");
		}
	} else if (data[i] == '\n' || 
			(	(i == length) && 
				(data[i-1] != '\n') && 
				(data[i-1] != '\0') ) )
	{
	    	/* if new line or on last line which had no \n, then at EOL*/
		if (debug_level >= debug_file_parse)
			fprintf(stderr,"Found end of line, char=#%d char=%c\n", 
						i,*lineStart);
						
		/* this is a limited check for bogus lines */
		if ( (*lineStart != '#') && tab1 && tab2 && *tab1 && *tab2 ) 
		{
			pairMenu[pairNum] = lineStart;
			
			/* setup src side of pair and replace tab char */
			if (tab1 < &data[i])
			{
				*tab1 = '\0';
				pairSrc[pairNum] = &tab1[1];
			}

			/* setup dst side of pair and replace tab char */
			if (tab2 < &data[i])
			{
				*tab2 = '\0';
				pairDest[pairNum] = &tab2[1];
			}
			
    			/* wipe out the end of line */
			if (data[i] == '\n')
				data[i] = '\0';
			
			if (debug_level >= debug_file_parse)
				fprintf(stderr,
						"Found line "
						"\tMenu= %s"
						"\tSrc= %s"
						"\tDest= %s\n\n", 
						pairMenu[pairNum],
						pairSrc[pairNum],
						pairDest[pairNum]
					);
			pairNum++;
		} else {
			/* some problem, probably not enough columns */
			if (debug_level >= debug_file_parse)
				fprintf(stderr,"Not enough columns.\n");
		}
		/* yes, could go past end, but we drop out before problem */
		lineStart = &data[i+1];
		tab1 = tab2 = NULL;
	} else {
		/* some normal old character */
	}
    }
    if (debug_level >= debug_file_parse)
		fprintf(stderr,"Found %d lines.\n", pairNum);
    
    
    /* map the buffer to the menu */
    for ( i = 0 ; i < pairNum; i++){
    	newCell = [[pairingsMenu target] 
			addItem:pairMenu[i]
			action:@selector(selectPredefined:)
			keyEquivalent:0];
	[newCell  setTarget:self];
	[newCell  setTag:i];
    }

    /* could have been torn off when app quit  ... */
    if ([[pairingsMenu target] isVisible])
    	[[pairingsMenu target] display];
    
    /* and close it - keeping the buffer */
///    NXClose(stream);
    NXCloseMemory(stream, NX_SAVEBUFFER);
    
    return self;
}

- selectPredefined:sender
{
    [dragBackList addObject: 
    	[[RdistControl alloc] initSourcePath:pairSrc[[sender selectedTag]]
			  destinationPath:pairDest[[sender selectedTag]] ] ];

    return self;
}

/*================================================================
 *
 *================================================================*/

- newDragController:sender
{
    [dragBackList addObject: [[RdistControl alloc] init]];
    return self;
}

/* catch all */
- newUnrelatedTool:sender
{
    if ([sender selectedTag] == 0)
    {
	[dragBackList addObject: [[CDROMIndexControl alloc] init]];
    }
    else if ([sender selectedTag] == 1)
    {
	[dragBackList addObject: [[TarControl alloc] init]];
    }
    
    return self;
}

/*================================================================
 *
 *================================================================*/

- preferencesChanged:sender
{
    int i;
    for ( i = 0 ; i < [dragBackList count]; i++){
		[[dragBackList objectAt:i] updateTextFields:self];
    }
    for ( i = 0 ; i < [scriptBackList count]; i++){
		[[scriptBackList objectAt:i] updateTextFields:self];
    }
    return self;
}

- dropDragJumpBackController:aController
{
    [dragBackList removeObject:aController];
    return self;
}

- (NXStream *)mapResource:(const char *)resourceName
{
    NXStream *	aStream;
    id		bundle;
    char 	buf[MAXPATHLEN];
	/* see if the user has a Patterns file */
	strcpy(buf, NXHomeDirectory());
	strcat(buf,"/");
	strcat(buf,lib_dir);
	strcat(buf,"/");
	strcat(buf, resourceName);
	aStream = NXMapFile(buf, NX_READONLY);
	/* if not then use the default one we have in the app wrapper */
	if (!aStream){
		bundle = [NXBundle bundleForClass:[self class]];
		[bundle getPath:buf forResource:resourceName ofType:template];
		aStream = NXMapFile(buf, NX_READONLY);
		
		/* since no file was in our Library, lets put it there */
		NXSaveToFile(aStream, buf);
		NXSeek(aStream, 0 , NX_FROMSTART);
	}
       
    return aStream;
}

/*================================================================
 *
 *================================================================*/

- showInfoPanel:sender
{
    if (!infoPanel)
    {
    	[NXApp loadNibSection:"InfoPanel.nib" owner:self];
	[versionField setStringValue: __DATE__ ];
    	[infoPanel setFrameUsingName:info_frame];
	[infoPanel setFrameAutosaveName:info_frame];
    }
    [infoPanel makeKeyAndOrderFront:self];
    return self;
}

   	
- showPrefPanel:sender
{
    [prefPanel makeKeyAndOrderFront:sender];
	
    return self;
}

@end
