// SourceDoc.m
//
// Free software created 1 Feb 1992
// by Paul Burchard <burchard@math.utah.edu>.

#import "Wais.h"
#import "WAISControl.h"
#import "SourceDoc.h"
#import "BrowserAssocTable.h"
#import "IconWellControl.h"
#import "IconWell.h"
#import "MailSpeaker.h"
#import "string.h"
#import <appkit/appkit.h>


@implementation SourceDoc

+ (const char *)fileType
{
    return "src";
}

+ (const char *)nibName
{
    return "SourceDoc.nib";
}

+ (const char *)miniIconName
{
    return "WaisSource.tiff";
}

+ (const char *)defaultFolder
{
    return [[WaisSource folderList] elementAt:0];
}

- updateFromWindow
{
    static char buf[READ_BUF_SIZE];
    
    [waisSource insertStringKey:":ip-name" value:[serverField stringValue]];
    [waisSource insertStringKey:":tcp-port" value:[serviceField stringValue]];
    [waisSource insertStringKey:":database-name"
    	value:[databaseField stringValue]];
    [waisSource	insertStringKey:":cost"	value:[costField stringValue]];
    [waisSource insertStringKey:":cost-unit" value:[unitsField stringValue]];
    [waisSource insertStringKey:":maintainer"
    	value:[maintainerField stringValue]];
    [[descriptionField docView] getSubstring:buf start:0 length:READ_BUF_SIZE];
    [waisSource insertStringKey:":description" value:buf];
    return self;
}

- updateToWindow
{
    if([waisSource valueForStringKey:":ip-name"])
	[serverField setStringValue:[waisSource valueForStringKey:":ip-name"]];
    else if([waisSource valueForStringKey:":ip-address"])
    	[serverField setStringValue:[waisSource 
    	    valueForStringKey:":ip-address"]];
    else [serverField setStringValue:""];
    if([waisSource valueForStringKey:":tcp-port"])
    	[serviceField setStringValue:[waisSource 
	    valueForStringKey:":tcp-port"]];
    else [serviceField setStringValue:""];
    if([waisSource valueForStringKey:":database-name"])
    	[databaseField setStringValue:[waisSource 
	    valueForStringKey:":database-name"]];
    else [databaseField setStringValue:""];
    if([waisSource valueForStringKey:":cost"])
    	[costField setStringValue:[waisSource valueForStringKey:":cost"]];
    else [costField setStringValue:""];
    if([waisSource valueForStringKey:":cost-unit"])
    	[unitsField setStringValue:[waisSource 
	    valueForStringKey:":cost-unit"]];
    else [unitsField setStringValue:""];
    if([waisSource valueForStringKey:":maintainer"])
    	[maintainerField setStringValue:[waisSource
	    valueForStringKey:":maintainer"]];
    else [maintainerField setStringValue:""];
    if([waisSource valueForStringKey:":description"])
    	[[descriptionField docView] setText:[waisSource 
	    valueForStringKey:":description"]];
    else [[descriptionField docView] setText:""];
    return self;
}

- init
{
    [super init];
    waisSource = [[WaisSource alloc] initKey:NULL];
    [waisSource insertStringKey:":tcp-port" value:"210"];
    [waisSource insertStringKey:":maintainer" value:current_user_name()];
    [waisSource insertStringKey:":cost" value:"0"];
    [waisSource insertStringKey:":cost-unit" value:":free"];
    [[descriptionField docView] setDelegate:self];
    isPublic = NO;
    [self updateToWindow];
    return self;
}

- free
{
    [waisSource free];
    [indexIWC free];
    [indexPanel free];
    return [super free];
}

- dump:sender
{
    if(!fileName) return nil;
    [self updateFromWindow];
    if(![waisSource writeWaisFile]) return nil;
    return self;
}

- load:sender
{
    if(!fileName) return nil;
    if(![waisSource readWaisFile]) return nil;
    [self updateToWindow];
    return self;
}

- setFileName:(const char *)aName
{
    id rtn = [super setFileName:aName];
    if(rtn) [waisSource setKey:fileName];
    return(rtn);
}

- (int)openFile:(const char *)name ok:(int *)flag
{
    return [[NXApp delegate] openFile:name ok:flag];
}

- bugReport:sender
{
    //!!! Note the "MailSendDemo" port is undocumented.
    id mailSpeaker = [[MailSpeaker alloc] init]; 
    port_t mailPort = NXPortFromName("MailSendDemo", NULL); 
    const char *maint;

    maint = [waisSource valueForStringKey:":maintainer"];
    if(!maint || !maint[0]) return nil;
    if(!mailSpeaker || mailPort==PORT_NULL)
    	{ [mailSpeaker free]; return nil; }
    [mailSpeaker setSendPort:mailPort];
    [mailSpeaker openSend];
    [mailSpeaker setTo:maint];
    if([waisSource valueForStringKey:":database-name"])
	[mailSpeaker setSubject:[waisSource 
	    valueForStringKey:":database-name"]];
    [mailSpeaker free];
    port_deallocate(task_self(), mailPort); 
    return self;
}

- indexPanel:sender
{
    int i, j, n, choice;
    id data, stringTable;
    const char *maint, *user;

    // Just pop up panel if it already exists.
    if(indexPanel)
	{ [indexPanel makeKeyAndOrderFront:self]; return indexPanel; }

    // Allow user to cancel if overwriting someone else's source.
    maint = [waisSource valueForStringKey:":maintainer"];
    user = current_user_name();
    stringTable = [[NXApp delegate] stringTable];
    if(maint && user && strcmp(maint, user)!=0)
    {
	if(stringTable) choice = NXRunAlertPanel(
	    [stringTable valueForStringKey:"Warning!"],
            [stringTable valueForStringKey:"This database is maintained by %s.  Are you sure you want to overwrite it?"],
	    [stringTable valueForStringKey:"Yes"],
	    [stringTable valueForStringKey:"Cancel"], NULL, maint);
	else
	{
	    fprintf(stderr,
	    	"Warning!  Overwriting WAIS source maintained by %s\n", maint);
	    fflush(stderr);
	    choice = NX_ALERTDEFAULT;
	}
	if(choice != NX_ALERTDEFAULT) return nil;
    }
    
    // Make user save new source first.
    if(!fileName)
    {
    	if(![[NXApp delegate] saveDoc:self as:YES]) return nil;
	if(!fileName) return nil;
    }
    {//!!!
    char *extn;
    char hostname[1024];
    //!!! messing with uniqued strings
    if(!(extn = strrchr(fileName, '/'))) extn = fileName;
    if(!(extn = strrchr(extn, '.'))) return nil;
    if(strcmp(extn+1, [[self class] fileType]) != 0) return nil;
    *extn = 0;
    [waisSource insertStringKey:":database-name" value:fileName];
    *extn = '.';
    gethostname(hostname, 1024);
    [waisSource insertStringKey:":ip-name" value:hostname];
    if(![waisSource writeWaisFile]) return nil;
    [self updateToWindow];
    }

    // Otherwise, load NIB and indexing information.
    [NXApp loadNibSection:"Indexer.nib" owner:self];
    [indexInputWell setDraggable:YES droppable:YES];
    [indexScriptWell setDraggable:YES droppable:NO];
    [[[indexFiles setEditable:YES] setAlphabetized:NO] setAbbreviated:NO];
    [indexFiles setTarget:self];
    [[indexFiles setAction:@selector(updateSelection:)]
    	setDoubleAction:@selector(updateSelection:)];
    n = [[waisSource dataFileList] count];
    for(i=0; i<n; i++)
    {
    	data = [[waisSource dataFileList] objectAt:i];
	j = [indexFiles indexAddEntry:[data valueForStringKey:"data-file"]];
	[indexFiles
	    setAssocStringValue:[data valueForStringKey:":index-mode"] at:j];
    }

    // Now pop up new panel.
    [indexPanel makeKeyAndOrderFront:self];
    if(!indexIWC) indexIWC = [[IconWellControl alloc] initWindow:indexPanel];
    return self;
}

- addToIndex:sender
{
    id theTarget;
    SEL theAction;

    theTarget = [indexFiles target];
    theAction = [indexFiles action];
    [indexFiles setTarget:nil];
    [indexFiles setAction:(SEL)0];
    [indexFiles setStringValue:[indexInputWell stringValue]];
    [indexFiles setTarget:theTarget];
    [indexFiles setAction:theAction];
    return [self applyIndexMode:self];
}

- applyIndexMode:sender
{
    int i, n;
    const char *mode = [[indexMode target] selectedItem];
    
    if(!mode) mode = ":text";
    n = [indexFiles count];
    for(i=0; i<n; i++) if([indexFiles isEntrySelectedAt:i])
    	[indexFiles setAssocStringValue:mode at:i];
    return self;
}

- makePrivate:sender
{
    isPublic = NO;
    return self;
}

- makePublic:sender
{
    //!!!----DISABLED-----
    NXRunAlertPanel("Publication",
    	"Sorry, Internet publication has been disabled in this version.",
	"OK", NULL, NULL);
    return nil;
    
    /*!!!
    int choice;
    id stringTable;

    // Let user cancel Internet publication.
    if(!(stringTable = [[NXApp delegate] stringTable])) return nil;
    choice = NXRunAlertPanel(
	[stringTable valueForStringKey:"Warning!"],
	[stringTable valueForStringKey:"Continuing will cause this source to be published on the Internet.  Make sure public access has been enabled!  Proceed?"],
	[stringTable valueForStringKey:"OK"],
	[stringTable valueForStringKey:"Cancel"], NULL);
    if(choice == NX_ALERTALTERNATE)
    	{ [indexPrivate performClick:self]; return nil; }
    isPublic = YES;
    return self;
    */
}

- updateSelection:sender
{
    id theTarget;
    SEL theAction;

    theTarget = [indexInputWell target];
    theAction = [indexInputWell action];
    [indexInputWell setTarget:nil];
    [indexInputWell setAction:(SEL)0];
    [indexInputWell setStringValue:[indexFiles stringValue]];
    [indexInputWell setTarget:theTarget];
    [indexInputWell setAction:theAction];
    return self;
}

- createIndex:sender
{
    int i, j, n, start_at, ok;
    char *extn;
    const char *mode;
    const char *systemFolder = NXGetDefaultValue("WAIStation","SystemFolder");
    id data;
    FILE *cmdfile;
    BOOL first;
    
    // Reset source's :index-list and save.
    //!!! waisindex overwrites this!!!
    if(!fileName) return nil;
    [waisSource clearDataFiles];
    n = [indexFiles count];
    for(i=0; i<n; i++)
    {
    	data = [[WaisDataFile alloc] initKey:NULL];
	[data insertStringKey:":data-file" value: [indexFiles entryAt:i]];
	[data insertStringKey:":index-mode"	
	    value: [indexFiles assocStringValueAt:i]];
	[waisSource addDataFile:data];
    }
    if(![self dump:self]) return nil;

    // Create shell script to index the data files.
    //!!! How 'bout some alert panels here??
    //!!! messing with uniqued strings
    if(!fileName) return nil;
    if(!(extn = strrchr(fileName, '/'))) extn = fileName;
    if(!(extn = strrchr(extn, '.'))) return nil;
    if(strcmp(extn+1, [[self class] fileType]) != 0) return nil;
    [Wais lockFileIO];
    strcpy(extn+1, "sh");
    if(!(cmdfile = fopen(fileName, "w")))
    	{ [Wais unlockFileIO]; return nil; }
    strcpy(extn+1, [[self class] fileType]);
    fchmod(fileno(cmdfile), 0755);
    
    // Write script header info.
    fprintf(cmdfile, "#!/bin/sh\n");
    fprintf(cmdfile, "# WAIS indexing script for %s\n", fileName);
    fprintf(cmdfile, "# Created %s by WAIStation.app %s\n",
    	printable_time(), WAISTATION_VERSION);
    
    // Group data files with same :mode into waisindex commands.
    //!!! Should keep command length below some limit.
    //!!! What about data file names with '\'' chars???
    n = [indexFiles count];
    for(first=YES, mode=0, start_at=0;
    	start_at>=0 && start_at<n; mode=0, first=NO)
	    for(i=start_at, start_at=(-1); i<n; i++)//!!!
    {
    	if(!mode)
	{
	    // Starting new :mode.  Flush old cmd line and start new one.
	    mode = [indexFiles assocStringValueAt:i];
	    *extn = 0;
	    fprintf(cmdfile, "\n%s/bin/waisindex -d %s %s -r -export ",
	    	(systemFolder ? systemFolder : ""),
		fileName, (first ? "" : "-a"));
	    *extn = '.';
	    if(isPublic) fprintf(cmdfile, "-register ");
	    if(!mode || !mode[0]) fprintf(cmdfile, "-t text ");	    
	    else fprintf(cmdfile, "-t %s ", mode+1);	    
	}
	else if(strcmp(mode, [indexFiles assocStringValueAt:i]) != 0)
	{
	    // Check if we saw this before.
	    for(j=0; j<i; j++)
	    	if(strcmp([indexFiles assocStringValueAt:i],
		    [indexFiles assocStringValueAt:j]) == 0)
		    	break;
	    if(j < i) continue;
	    
	    // Found new :mode.  Skip file and set pointer for next loop.
	    if(start_at < 0) start_at = i;
	    continue;
	}
	if(!mode || strlen(mode)==0) { [Wais unlockFileIO]; return nil; }
	
	// Compile data file into command line.
	fprintf(cmdfile, "\'%s\' ", [indexFiles entryAt:i]);
    }
    // Flush last cmd line.
    fprintf(cmdfile, "\n");
    
    // Have script refresh source in WAIStation by re-opening.
    //!!! Check if WS running first?
    fprintf(cmdfile, "open %s\n", fileName);
    fclose(cmdfile);
    [Wais unlockFileIO];
    
    // Now run script and present it to the user for re-indexing.
    ok = NO;
    strcpy(extn+1, "sh");
    [indexScriptWell setStringValue:fileName];
    [self openFile:fileName ok:&ok];
    strcpy(extn+1, [[self class] fileType]);
    if(!ok) return nil;
    return self;
}

@end


