/* Implementation of Application class
 *
 * Copyright (C)  1993  The Board of Trustees of  
 * The Leland Stanford Junior University.  All Rights Reserved.
 *
 * Authors: Scott Francis, Fred Harris, Paul Kunz, Tom Pavel, 
 *	    Imran Qureshi, and Libing Wang (SLAC)
 *	    Mike L. Kienenberger (Alaska)
 *
 * This file is part of an Objective-C class library for a Window system
 *
 * Application.m,v 1.91 1995/12/13 22:32:51 fedor Exp
 */



#include "Application.h"

/* Interface for display specific methods */
@interface Application(ToolKit)
- (int)_runModalFor:theWindow;
- _displayInitArgc:(int)ac argv:(char**)av;

@end

/* Required for implementation: */
#include <defaults/defaults.h>
#include "Menu.h"
#include "NXBundle.h"
#include "PrintInfo.h"
#include "Window.h"
#include "stdmacros.h"
#include <objc/List.h>
#include <stdlib.h>             /* for free() */
#include <string.h>
#ifdef	sgi
#include <malloc.h>	/* although you might need to edit malloc.h */
#endif	/* sgi */
#include <pwd.h>   /* required for NXHomeDirectory ... */
#include <unistd.h>
#include <objc/hashtable.h>

/* The following supporting functions are implmented by different
 * window systems
 */

extern appMainLoop( void *);
extern void *createApplicationContext( void );
extern destroyApplicationContext( void *);
extern
void *openDisplay( void *app_conn, char *appname, int ac, char **av);
 
/* The following dummy function is used to force the loading of all the
 * classes so that objects can be read from an archive file
 */
extern void baseSymbols(void);	

/* Realize these externals: */
id 	NXApp;
pid_t   NXProcessID;
char    **NSArgv;
int     NSArgc;

#define NXArgv NSArgv
#define NXArgc NSArgc

/* For naming conventions	*/
typedef struct data_Type {
	id	object;
	id	owner;
	char   *name;
}*dataPtr;

typedef struct node_type {
	dataPtr	data;
	struct node_type *next;
} *node;


static node head = NULL;


/* This is so in loading xmib files, File's Owner is set properly */
id	nibOwner;

char *NXHomeDirectory(void)
{
   /* needed to make friendly path to filename on n-tuples */
   static struct passwd *current_pwd = NULL;
   static int uid = -1;
   int newuid;

   if ((newuid=getuid()) != uid || current_pwd == NULL) {
       uid = newuid;
       current_pwd = getpwuid(uid);
   }
   return current_pwd->pw_dir;

}

char *NXUserName(void)
{
   /* simply returns user name */
   static struct passwd *current_pwd = NULL;
   static int uid = -1;
   int newuid;

   if ((newuid=getuid()) != uid || current_pwd == NULL) {
       uid = newuid;
       current_pwd = getpwuid(uid);
   }
   return current_pwd->pw_name;

}

@implementation Application

// + (id <NXWorkspaceRequestProtocol>)workspace;
// + initialize;
// + new;
// + allocFromZone:(NSZone *)zone;
// + alloc;

- free
{
    [windowList freeObjects];
    [windowList free];
    free(appName);
    /* FIXME: destroy the DPSContext */
    destroyApplicationContext( app_con );
    [_mainMenu free];
    return [super free];
}

- setDelegate:anObject
{
    delegate = anObject;
    return self;
}

- delegate
{
    return delegate;
}

- (DPSContext)context
{
    return _context;
}

// - hide:sender;
// - unhide:sender;
// - unhideWithoutActivation:sender;
// - findWindow:(int)windowNum;
// - focusView;

- mainWindow
{
//    return [mainWindowList lastObject];
    return mainWindow;
}


- keyWindow;
{
  /* temporary */
    return nil;
}

// - (port_t)replyPort;

- (const char*)appName
{
    return appName;
}

- (int)activateSelf:(BOOL)flag
{
  /* should do something other than just setting the flag */
    appFlags.active = flag ? 1 : 0;
    return 0;
}

- run
{   
    [self appDidInit:self];
    [windowList makeObjectsPerform:@selector(_realize)];
    appMainLoop( app_con );
    return self;
}

- (int)runModalFor:theWindow
{
    return [self _runModalFor:theWindow];
}

- stopModal
{
    _modalReturnCode = X_RUNSTOPPED;
    return self;
}

- stopModal:(int)returnCode
{
    _modalReturnCode = returnCode;
    return self;
}

- setAutoupdate:(BOOL)flag
{
    appFlags.autoupdate = flag;
    return self;
}

- terminate:sender
{
    destroyApplicationContext(app_con);
    exit(0);
}

- makeWindowsPerform:(SEL)aSelector inOrder:(BOOL)flag
{
    [windowList makeObjectsPerform:aSelector];
    return self;
}

- appIcon
{
    return _appIcon;
}

- windowList
{
    return windowList;
}

- updateWindows
{
    Window	*win;
    int		i;
    
    if ( delegate && [delegate respondsTo:@selector(appWillUpdate:)] ) {
	[delegate appWillUpdate:self];
    }
    i = [windowList count];
    while ( i-- ) {
	win = [windowList objectAt:i];
	if ( [win isVisible] ) {
	    [win update];
	}
    }
    if ( delegate && [delegate respondsTo:@selector(appDidUpdate:)] ) {
	[delegate appDidUpdate:self];
    }
    return self;
}

- (BOOL)sendAction:(SEL)theAction to:theTarget from:sender
{
    id		altTarget;
    
    if ( theTarget ) {
        altTarget = theTarget;
    } else {
	altTarget = [self calcTargetForAction:theAction];
    }
    if ( altTarget ) {
        [altTarget perform:theAction with:sender];
	return YES;
    }
    return NO;
}

- calcTargetForAction:(SEL)theAction
{
    Responder	*r;
    Window	*w;
    id		d;
    
    w = [self keyWindow];
    if ( w ) {
        r = [w firstResponder];
	while ( r ) {
	    if ( [r respondsTo:theAction] ) {
	    	return r;
	    }
	    r = [r nextResponder];
	}
    }
    d = [w delegate];
    if ( d  && [d respondsTo:theAction] ) {
	return d;
    }
    if ( w != [self mainWindow] ) {
        w = [self mainWindow];
	if ( w ) {
	    r = [w firstResponder];
	    while ( r ) {
		if ( [r respondsTo:theAction] ) {
		    return r;
		}
		r = [r nextResponder];
	    }
	}
	d = [w delegate];
	if ( d  && [d respondsTo:theAction] ) {
	    return d;
	}
    }
    if ( [self respondsTo:theAction] ) {
        return self;
    }
    if ( delegate && [delegate respondsTo:theAction] ) {
        return delegate;
    }
    return nil;
}

- printInfo
{
    if ( !_printInfo ) {
	_printInfo = [[PrintInfo alloc] init];
    }
    return _printInfo;
}
	/* Handling requests for nib files */

- setPrintInfo:info
{
    PrintInfo	*oldInfo;
    
    oldInfo = _printInfo;
    _printInfo = info;
    return oldInfo;
}

- setMainMenu:aMenu
{
    _mainMenu = aMenu;
    return self;
}

- mainMenu
{
    return _mainMenu;
}

- delayedFree:anObject
{
    return self;
}

- getScreenSize:(NXSize *)theSize;
{
    return self;
}

- init
{   
    [super init];
    if ( !self ) {
    	baseSymbols();	/* want the reference, but don't call it. */
    }
    instancename = "Application";
    
    /* Init globals: */
    NXApp = self;
    NXProcessID = getpid();

    app_con = createApplicationContext();
    windowList = [[List alloc] init];
//    mainWindowList = [[List alloc] init];

    if (! appName) [self setAppName:"App"];
    return self;
}

- initargc:(int)ac argv:(char**)av
{
    [self appWillInit:self];
    NSArgc = ac;
    NSArgv = av;
    
    [self init];
    if (![self _displayInitArgc:NSArgc argv:NSArgv]) {
	[self free];
	return nil;
    }
    return self;	
}

 /* appWillInit and appDidInit are implemented  so that sub
  * class of Application can handle them if delegate doesn't
  */
- appWillInit:sender
{
    if (delegate && [delegate respondsTo:@selector(appWillInit:)]) {
    	[delegate appWillInit:sender];
    }
    return self;
}

- appDidInit:sender
{
    if (delegate && [delegate respondsTo:@selector(appDidInit:)])
    	[delegate appDidInit:sender];
    return self;
}

- setAppName:(const char*)aString
{
    appName = NXCopyStringBuffer(aString);
    return self;
}

- (void*) _X_display
{
    return _X_display;
}

- setAppIcon:anIcon
{
    if (_appIcon) 
	[_appIcon free];
    _appIcon = anIcon;
    return self;
}

- loadNibSection:(const char *)sectionName owner:anOwner
{
    return [self loadNibSection:sectionName
		 owner:anOwner
		 withNames:YES
                 fromHeader:NULL
		 fromZone:NSDefaultMallocZone() ];
}

- loadNibSection:(const char *)sectionName owner:anOwner withNames:(BOOL)aFlag
{

    return [self loadNibSection:sectionName
                 owner:anOwner
                 withNames:aFlag
                 fromHeader:NULL
                 fromZone:NSDefaultMallocZone() ];

}

- loadNibSection:(const char *)sectionName owner:anOwner withNames:(BOOL)aFlag
      fromHeader:(const struct mach_header *)header
{
    return [self loadNibSection:sectionName
                 owner:anOwner
                 withNames:aFlag
                 fromHeader:header
                 fromZone:NSDefaultMallocZone() ];
}

- loadNibSection:(const char *)sectionName owner:anOwner withNames:(BOOL)aFlag
        fromZone:(NSZone *)zone
{
    return [self loadNibSection:sectionName
                 owner:anOwner
                 withNames:aFlag
                 fromHeader:NULL
                 fromZone:zone ];
}

- loadNibSection:(const char *)sectionName owner:anOwner withNames:(BOOL)aFlag
        fromHeader:(const struct mach_header *)header fromZone:(NSZone *)zone
{
/* ignore zone and header for now */
    NXBundle	*bundle;
    char	path[MAXPATHLEN+1];
	
    if ( !sectionName || (*sectionName == '\0') ) 
	return nil;

    bundle = [NXBundle mainBundle];
    if ( !([bundle getPath:path forSection:sectionName]) )
	return nil;

    return [self loadNibFile:path owner:anOwner withNames:aFlag];
}

- loadNibFile:(const char *)fileName owner:anOwner
{
    TypedStream		*typedStream;
    id                  list_of_objects;
    id                  list_of_connections;
    id			previousOwner;
    id			obj;
    int			i, count;

    typedStream = objc_open_typed_stream_for_file(fileName,
						  OBJC_READONLY);
     if (!typedStream) {
	fprintf(stderr, "loadNibFile: could not open %s for reading\n",
		fileName);
	return nil;
    }
 /*
  * set the global variable so CustomObjects can correctly set themselves if
  * they're the File's Owner.   Don't lose the previous owner as this method
  * could be called recusively 
  */
    previousOwner = nibOwner;
    nibOwner = anOwner;

 /* read the list object that contains all the objects */
    objc_read_object(typedStream, &list_of_objects);

 /* read the list object that contains all the connections */
    list_of_connections = [list_of_objects removeLastObject];

    objc_close_typed_stream(typedStream);

    [list_of_connections makeObjectsPerform:@selector(establishConnection)];
    count = [list_of_objects count];
    for ( i = 0; i < count; i++ ) {
        obj = [list_of_objects objectAt:i];
	if ( [obj respondsTo:@selector(awakeFromNib)] ) {
	    [obj awakeFromNib];
	}
    }
    nibOwner = previousOwner;
    return [list_of_objects objectAt:0];
}

- loadNibFile:(const char *)fileName owner:anOwner withNames:(BOOL)flag
{
    return [self loadNibFile:fileName owner:anOwner];
}

- loadNibFile:(const char *)fileName owner:anOwner withNames:(BOOL)flag 
     fromZone:(NSZone *)aZone
{
    return [self loadNibFile:fileName owner:anOwner];
}



- (void *)_toplevel
{
    return [[windowList objectAt:0] _shell];
}

@end

@implementation Application(Private)
- _willBecomeMain:(Window *)aWindow
{
//    Window	*curMain;
    
    if ( ![aWindow canBecomeMainWindow] ) {
        return self;
    }
//    curMain = [mainWindowList lastObject];
//    if (nil != curMain)  [curMain resignMainWindow];
    if (nil != mainWindow)  [mainWindow resignMainWindow];
    [aWindow update];
    [aWindow becomeMainWindow];
//    [mainWindowList removeObject:aWindow];
//    [mainWindowList addObject:aWindow];
    mainWindow = aWindow;
    return self;
}

- _addWindow:aWindow
{
    [windowList addObject:aWindow];
    return self;
}

- _removeWindow:aWindow
{
    [windowList removeObject:aWindow];
    if (mainWindow == aWindow)  mainWindow = nil;
    
    return self;
}

@end


id
objc_get_named_object(const char *name, id owner)
{
	node temp;
	
	temp = head->next;
	while (temp)
	{
	    if ((temp->data->owner == owner) && 
	    	(strcmp(temp->data->name, name) == 0))
	    {
	    	return temp->data->object;
	    }
	    temp = temp->next;
	}
	return nil;
}

const char *
objc_get_object_name(id theObject)
{
	node temp;
	char *name;
	
	temp = head->next;
	while (temp)
	{
	    if (temp->data->object == theObject) {
		name = (char *)malloc(strlen(temp->data->name) + 1);
		strcpy(name, temp->data->name);
		return (name);
	    }
	}
	return NULL;
}

/*
	If head is null, initialize a head node.  The head node is never
	freed.  Other nodes are inserted between it and the list as they
	are added.  After checking the head node, memory is allocated,
	and the data fields set.
 */
int
objc_name_object(const char *name, id theObject, id owner)
{
	node newNode;
	
	if (!head) {
	    head = (node)malloc(sizeof(struct node_type));
	    head->data = NULL;
	    head->next = NULL;
	}
	
	newNode = (node)malloc(sizeof(struct node_type));
	newNode->data = (dataPtr)malloc(sizeof(struct data_Type));
	newNode->data->object = theObject;
	newNode->data->owner = owner;
	newNode->data->name = (char *)malloc(strlen(name)+1);
	strcpy(newNode->data->name, name);
	
	newNode->next = head->next;
	head->next = newNode;
	return (1);
}

int
objc_unname_object(const char *name, id owner)
{
	node *temp;
	node smallertemp;
	
	while (*temp)
	{
	    if ((strcmp((*temp)->data->name, name) == 0) &&
	    	((*temp)->data->owner == owner))
	    {
	    	smallertemp = *temp;
		*temp = (*temp)->next;
		free(smallertemp->data->name);
		free(smallertemp->data);
		free(smallertemp);
	    	return 1;
	    }
	}
	return 0;
}

int
objc_free_names()
{
	node temp;
	node temp2;
	
	temp = head->next;
	while (temp)
	{
	    temp2 = temp;
	    temp = temp->next;
	    free(temp2->data->name);
	    free(temp2->data);
	    free(temp2);
	}
	free(head);
	return 1;
}
