/***************************************************************************
*                                                                          *
* Chatserver.m                                                             *
* Copyright 1992 by Nik A Gervae                                           *
*                                                                          *
* Part of an example using the Objective-C classes (SktSocketManager,      *
* SktSocket, and SktSocketUser) which implement a convenient interface to  *
* Berkeley stream sockets under NeXTSTEP(r).  See the accompanying class   *
* specifications (files with a .rtf or .spec suffix) and the sources for   *
* further information.                                                     *
*                                                                          *
* NeXTSTEP is a registered trademark of NeXT Computer, Inc.                *
*                                                                          *
****************************************************************************
*                                                                          *
* LICENSE                                                                  *
*                                                                          *
* This program is free software; you can redistribute it and/or modify     *
* it under the terms of the GNU General Public License as published by     *
* the Free Software Foundation.                                            *
*                                                                          *
* The program and this makefile are distributed in the hope that it will   *
* be useful, but are provided "AS IS" AND WITHOUT ANY WARRANTY; without    *
* any express or implied warranty of MERCHANTABILITY or FITNESS FOR A      *
* PARTICULAR PURPOSE. See the GNU General Public License for more details. *
* Any use or distribution of the program and documentation must include    *
* appropriate copyrights to acknowledge Nik A. Gervae and the Free         *
* Software Foundation, Inc.                                                *
*                                                                          *
* You should have received a copy of the GNU General Public License        *
* along with this program; if not, write to the Free Software              *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                *
*                                                                          *
****************************************************************************
*                                                                          *
* VERSION HISTORY                                                          *
*                                                                          *
* Version numbers are simply dates in the form YYYYMMDD.  These represent  *
* the date that version was finished.  Only significantly changed versions *
* are reported here, or those versions requiring explanation of changes.   *
* There may be many interim stages between dated versions.                 *
*                                                                          *
* DateVersion Primary Author  Notes                                        *
* ----------- --------------- -------------------------------------------- *
* 19920327    Nik A Gervae    First released version                       *
*                                                                          *
***************************************************************************/

#import <stdio.h>
#import <string.h>

#import <objc/Object.h>
#import <objc/List.h>

#import "SktSocketManager.h"
#import "SktSocket.h"
#import "Chatserver.h"
#import "Guest.h"

/***************************************************************************
*                                                                          *
* These are the constant strings used.  Feel free to translate them into   *
* your favorite language.  Do be sure to keep all the % directives in      *
* place, or change the code that accesses these strings.                   *
*                                                                          *
***************************************************************************/
#define STR_RunningOnAddresses  "Running on %s at Internet addresses:"
#define STR_AnAddress           " %s"
#define STR_InetServicePort     "Internet service port number: %d\n"
#define STR_EndOfAddresses      "\n"



@implementation Chatserver

/***************************************************************************
*                                                                          *
* initWithInetPort:                                                        *
*                                                                          *
* Creates a SocketMgr object and initializes the guestList. Also logs      *
* the host, address and port number the server is running on.              *
*                                                                          *
***************************************************************************/
- initWithInetPort:(int)inetPort
{
  char **addresses;
  int    i;

 /*
  * Get a manager, bomb if you can't.
  */
  socketMgr = [[SktSocketManager alloc] initPort:inetPort logfile:stderr
               fdCapacity:10 userClass:[Guest class]];
  if (!socketMgr) return [self free];

 /*
  * Try to print out all the internet addresses for this machine.
  */
  if (NULL != (addresses = [socketMgr getInetAddresses])) {
    [self log:STR_RunningOnAddresses, [socketMgr hostname]];
    for (i = 0; NULL != addresses[i]; i++) {
      [self log:STR_AnAddress, addresses[i]];
    }
    [self log:STR_EndOfAddresses];
    free(addresses);
  }

 /*
  * Print out the port number.
  */
  [self log:STR_InetServicePort, [socketMgr servicePort]];

 /*
  * A new guest list, please.
  */
  guestList = [[List alloc] init];

  return self;
}

/***************************************************************************
*                                                                          *
* run                                                                      *
*                                                                          *
* Run forever. The -stop message never gets sent.  Excercise for the       *
* reader.                                                                  *
*                                                                          *
***************************************************************************/
- run
{
  running = YES;
  while(running) {
    usleep(250000);
    [self update];
  }
  return self;
}

/***************************************************************************
*                                                                          *
* update                                                                   *
*                                                                          *
* socketMgr needs this to work. Tell socketMgr to update, and do the same  *
* for each guest.                                                          *
*                                                                          *
***************************************************************************/
- update
{
  [socketMgr update];
  [guestList makeObjectsPerform:@selector(update)];
  return self;
}

/***************************************************************************
*                                                                          *
* guestDidInit:                                                            *
*                                                                          *
* Any newly created Guest sends this. This just saves it in the guest list *
* for further reference.                                                   *
*                                                                          *
***************************************************************************/
- guestDidInit:aGuest
{
  [guestList addObject:aGuest];
  return self;
}

/***************************************************************************
*                                                                          *
* guestWillFree:                                                           *
*                                                                          *
* A guest send this as its about to die.  This just removes it from the    *
* guest list                                                               *
***************************************************************************/
- guestWillFree:aGuest
{
  [guestList removeObject:aGuest];
  return self;
}

/***************************************************************************
*                                                                          *
* log:                                                                     *
*                                                                          *
* Write out a printf() style argument list to stderr.                      *
*                                                                          *
***************************************************************************/
- log:(const char *)format, ...
{
  va_list param_list;

  va_start(param_list, format);
  vfprintf(stderr, format, param_list);
  fflush(stderr);
  va_end(param_list);
  return self;
}

/***************************************************************************
*                                                                          *
* announce:                                                                *
*                                                                          *
* Tell something to every guest.                                           *
*                                                                          *
***************************************************************************/
- announce:(const char *)announcement
{
  int numGuests;
  int i;

  numGuests = [guestList count];
  for (i = 0; i < numGuests; i++) {
    [[guestList objectAt:i] queueOutputString:announcement];
  }

  return self;
}

/***************************************************************************
*                                                                          *
* announce:except:                                                         *
*                                                                          *
* Tell something to every guest except the one mentioned.                  *
*                                                                          *
***************************************************************************/
- announce:(const char *)announcement except:notMe
{
  int numGuests;
  int i;
  id  currentGuest;

  numGuests = [guestList count];
  for (i = 0; i < numGuests; i++) {
    currentGuest = [guestList objectAt:i];
    if (currentGuest != notMe) [currentGuest queueOutputString:announcement];
  }

  return self;
}

/***************************************************************************
*                                                                          *
* stop                                                                     *
*                                                                          *
* Should be reasonably clear.  Except that it never gets sent!  :-)        *
*                                                                          *
***************************************************************************/
- stop
{
  running = NO;
  return self;
}

/***************************************************************************
*                                                                          *
* shutdown                                                                 *
*                                                                          *
* If there were more complex structures/objects, you might want to save    *
* them to disk or free them all here.                                      *
*                                                                          *
***************************************************************************/
- shutdown
{
  return [self free];
}


@end /*implementation Chatserver*/

/***************************************************************************
***************************************************************************/
