#import "NetTalkClient.h"
#import "TalkController.h"
#import "NetTalkThinker.h"
#import <appkit/appkit.h>
#include <sys/types.h>
#include <ansi/ctype.h>
#include <bsd/pwd.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <sys/param.h>
#include <netdb.h>


@implementation NetTalkClient


- init
{
	int   retry;
	id    serverConnection;
	char  *serverName, serverList[8192];

	[super init];
	
	delegate = nil;
	sprintf(hostname, "%s", [self fullHostname]);
	realName = (char *)[self user];
	strncpy(talkName, NXUserName(), 127);
	uniqueName[0] = 0;
	serverStarter = NO;
	
	strcpy(serverList, NXGetDefaultValue(APPNAME, NTD_SERVERLIST));
	if (serverList[0] == 0) 
	{
		retry = 1;
		while (retry)
		{
			server = [NXConnection connectToName:NETTALK onHost:"*"];
			if (!server)
			{
				retry = [self thereIsNoServer];
				if (!retry)
				{
					[self free];
					return nil;
				}
			}
			else
			{
				fprintf(stderr, "NetTalk: Connected to local server.\n");
				retry = 0;
			}
		}
	}
	else
	{
		strcpy(serverList, NXGetDefaultValue(APPNAME, NTD_SERVERLIST));
		serverName = strtok(serverList, " ");
		while (serverName)
		{
			server = [NXConnection connectToName:NETTALK onHost:serverName];
			if (!server)
				serverName = strtok(NULL, " ");
			else
			{
				fprintf(stderr, "NetTalk: Connected to server %s.\n", serverName);
				strcpy(currentServerName, serverName);
				serverName = NULL;
			}
		}
		if (!server)
		{
			if (NXRunLocalizedAlertPanel(NULL
				, "Alert", "NoDemonOnServer-1-Name", "OK", "Terminate", NULL
				, serverName)
				== NX_ALERTALTERNATE)
			{
				[self free];
				return nil;
			}
		}
	}
	
	serverConnection = [server connectionForProxy];
	[server setProtocolForProxy:@protocol(NTKPserver)];

	[serverConnection registerForInvalidationNotification:self];
	[serverConnection runFromAppKit];
	[server addClient:self];
	
	return self;
}

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

- delegate
{
	return delegate;
}

/*****************************************************************/
/* - (id) startupServer                                (->self)  */
/*    Fork subprocess and make it the server.                    */
/*****************************************************************/
- (id) startupServer
{
	char    servername[MAXPATHLEN+1];
	int     childPid;
	
	[[NXBundle mainBundle]
		getPath:servername forResource:"nettalkd" ofType:NULL];
	switch (childPid = vfork())
	{
/* error */
	case -1:
		perror("vfork (child)");
		break;
/* child */
	case 0:
		execl(servername, 0);
		perror("vfork (child)");		/* should never gets here tho */
		exit(1);
/* me */
	default:
		fprintf(stderr, "Started server <%d>.\n", childPid);
		sleep(2);
	}
	return self;
}

/*****************************************************************/
/* - (int) thereIsNoServer                                       */
/*    Informs the user that there is no server.                  */
/*    Asks weather to retry searching                       (1)  */
/*    or to terminate the application.                      (0)  */
/*****************************************************************/
- (int) thereIsNoServer
{
	switch(NXRunLocalizedAlertPanel(NULL
		, "Alert"
		, "NoServer"
		, "Retry", "Start server", "Terminate"))
	{
		case NX_ALERTDEFAULT:
			return 1;
			break;
		case NX_ALERTALTERNATE:
			[self startupServer];
			return 1;
			break;
		case NX_ALERTOTHER:
			return 0;
	}
	return 0;
}

- (id) server
{
	return server;
}

- (const char *)serverName
{
	return currentServerName;
}

/*****************************************************************/
/* - (const char *) user                               */
/*    Returns the real user name, taken from NetInfo.  */
/*                                                     */
/*    Thanks to                                        */
/*    droege@informatik.uni-koblenz.de for the code.   */
/*******************************************************/
- (const char *)user
{
	static char        *username = (char *)0;
	struct passwd      *pwdentry;
	static char         ubuf[128];
	char               *pc;

	pwdentry = getpwnam(NXUserName());
	username = ((pwdentry) ? pwdentry->pw_gecos : "");

	/*
	 * extended GECOS field handling, Detlev Droege,
	 * droege@informatik.uni-koblenz.de, December 1991 
	 */
	if (pc = index(username, ','))
		*pc = 0;										/* strip ','-part of gecos field, clobbers
																 * pw_gecos !! */
	if (index(username, '&'))
	{
		/* do '&' expansion */
		pc = ubuf;
		while (*username)
		{
			if (*username == '&')
			{
				strcpy(pc, pwdentry->pw_name);
				*pc = toupper(*pc);
				while (*pc)
					pc++;									/* ==  pc += strlen(..->pw_name) */
			}
			else
				*pc++ = *username;
			username++;
		}
		*pc = 0;
		username = ubuf;
	}
	return (const char *)username;
}

- (const char*)fullHostname
{
	static char host[MAXHOSTNAMELEN];
	int status, hlen, dlen;

	/* Get the host name */
	status = gethostname(host, MAXHOSTNAMELEN);
	if (status < 0) return(NULL);

	hlen = strlen(host);

	/* res_init() will look up the domain name */ 
	res_init();

	/* If there's no domain name set, _res.defdname is zero length */
	dlen = strlen(_res.defdname);
	if (dlen == 0) return(host);

	/* Append the domain name to the host name if necessary */
	if ((hlen < dlen) || strcmp(host + (hlen - dlen), _res.defdname))
	{
		strcat(host, ".");
		strcat(host, _res.defdname);
	}

	return(host);
}

- free
{
	[super free];
	[[server connectionForProxy] free];
	return self;
}


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


- (int)registrationNr
{
	return 0;
}


/*****************************************************************/
/* Methods the delegate and other interested folks should invoke */
/*****************************************************************/

- (id)
	sendRefuseTo:(const char *)uName
		forCode:(const char *)aCode
{
	[server refuseConnection:self to:uName forCode:aCode];
	return self;
}

- (id)
	sendConnectTo:(const char *)uName
		via:aDocument
		withCode:(const char *)aCode
		topicIs:(const char *)aTopic
{
	[server connect:self to:uName via:aDocument withCode:aCode topicIs:aTopic];
	return self;
}


/*****************************************************************/
/*  Methods from the NetTalkClient protocol                      */
/*****************************************************************/


- (const char *)clientName
{
	static char cName[MAXPATHLEN];
	
	sprintf(cName, "%s @ %s", realName, hostname);
	return cName;
}


- (const char *)userName
{
	return talkName;
}

- (const char *)realUserName
{
	return realName;
}


- (const char *)uniqueName
{
	int    pid;
	
	if (uniqueName[0] == 0)
	{
		unix_pid(task_self(), &pid);
		sprintf(uniqueName, "%s@%s|%i", talkName, hostname, pid);
	}
	return uniqueName;
}

- (BOOL) isServerStarter
{
	return serverStarter;
}

- (oneway void) setServerStarter:(BOOL)aFlag
{
	serverStarter = aFlag;
}

- sendDisconnectTo:(const char *)uName
{
	[server
		disconnect:self
		from:uName
		forCode:NULL];
	return self;
}

- sendDisconnectTo:(const char *)uName
	forCode:(const char *)aCode
{
	[server disconnect:self from:uName forCode:aCode];
	return self;
}

- getClientList
{
	return [server clientList];
}


- (oneway void)newClientNamed:(const char *)uName
{
	if ([delegate respondsTo:@selector(updateClientData)])
		[delegate updateClientData];
	free((char *)uName);
}


/*****************************************************************/
/* - client:sendsVia:forCode:topicIs:                            */
/*****************************************************************/
- (oneway void)
		client:(in const char *)uName 
		sendsVia:aDocument
		forCode:(const char *)aCode
		topicIs:(in const char *)aTopic;
{
	if (!aCode) aCode = "";
	if (!aTopic) aTopic = "";
	if ([delegate respondsTo:@selector(handleConnectFrom:via:withCode:topicIs:)])
		[delegate
			handleConnectFrom:uName
			via:aDocument
			withCode:aCode
			topicIs:aTopic];
	free((char *)aCode);
	free((char *)aTopic);
}


- (oneway void)
		client:(in const char *)uName
		refusesConnectionForCode:(const char *)aCode
{
	if (!aCode) aCode = "";
	if ([delegate respondsTo:@selector(handleRefuseFrom:forCode:)])
		[delegate
			handleRefuseFrom:uName
			forCode:(const char *)aCode];
	free((char *)uName);
	free((char *)aCode);
}


- (oneway void)
		client:(in const char *)uName
		wantsToDisconnectForCode:(const char *)aCode
{	
	if (!aCode) aCode = "";
	if ([delegate respondsTo:@selector(handleDisconnectFrom:forCode:)])
		[delegate
			handleDisconnectFrom:uName
			forCode:aCode];
	free((char *)uName);
	free((char *)aCode);
}



/*****************************************************************/
/*  From the NXSenderIsInvalid protocol                          */
/*                                                               */
/* - senderIsInvalid:sender                            (->self)  */
/*****************************************************************/

- senderIsInvalid:sender
{
	id     serverConnection;
	int    retry = 1;
	int    tries = 0;
	char  *serverName, serverList[8192];
	
	if (strcmp(NXGetDefaultValue(APPNAME, NTD_USESELECTED), NTD_YES) == 0)
	{
		strcpy(serverList, NXGetDefaultValue(APPNAME, NTD_SERVERLIST));
		serverName = strtok(serverList, " ");
		while (serverName)
		{
			server = [NXConnection connectToName:NETTALK onHost:serverName];
			if (!server)
				serverName = strtok(NULL, " ");
			else
			{
				fprintf(stderr, "NetTalk: Now connected to server %s.\n", serverName);
				strcpy(currentServerName, serverName);
				serverName = NULL;
			}
		}
		if (!server)
		{
			switch(NXRunAlertPanel(NULL
				, "Alert"
				, "Lost connection to server!"
				, "Continue", "Terminate", NULL))
			{
				case NX_ALERTDEFAULT:
					break;
				case NX_ALERTALTERNATE:
					[NXApp terminate:nil];
					break;
			}
			server = nil;
			return 0;
		}
	}
	else
	{
		if (serverStarter)
		{
			[self startupServer];
			[self setServerStarter:NO];
		}
	
		while (retry)
		{
			server = [NXConnection connectToName:NETTALK onHost:"*"];
			if (!server)
			{
				tries++;
				if (tries > 2)
				{
					retry = [self thereIsNoServer];
					tries = 0;
				}
				if (!retry)
					[NXApp terminate:self];
			}
			else
			{
				fprintf(stderr, "NetTalk: Connected to local server.\n");
				retry = 0;
			}
		}
	}
	
	serverConnection = [server connectionForProxy];
	[server setProtocolForProxy:@protocol(NTKPserver)];

	[serverConnection registerForInvalidationNotification:self];
	[serverConnection runFromAppKit];
	[server addClient:self];
	return self;
}

@end

