/*---------------------------------------------------------------------------
SpeakToPh.m -- The SpeakToPh methods are not copyrighted.

SpeakToPh shows how to use invoke the Ph application using the Ph 
speaker/listener code.  It is intended for the programmer who wishes to
integrate Ph into his/her application.  This code is not intended for
production use.  Do not install SpeakToPh.  However, as with all speaker/
listener applications, the listener application (i.e., Ph) must be
installed in your ~/Apps or /LocalApps directory.

Rex Pruess <Rex-Pruess@uiowa.edu>
Weeg Computing Center
The University of Iowa
Iowa City, IA  52242
-----------------------------------------------------------------------------*/

/* Standard C header files */
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>

/* Application class header files */
#import "SpeakToPh.h"
#import "PhSpeaker.h"

@implementation SpeakToPh

/*---------------------------------------------------------------------------
This method will try to add a new server/site to the Ph Nameserver list.
It will fail if Ph has not completed its initialization process.  You
should check for this condition & retry until successful or your patience
runs out.  If it succeeds, the server/site will appear in the Ph list of
Nameservers.
-----------------------------------------------------------------------------*/
- addServer:sender
{
   int            flag;
   int            msgDelivered;
   char           server[128];
   char           site[128];

   if (![self createPort:self])
      return self;

   strcpy (server, [addServerForm stringValueAt:0]);
   strcpy (site, [addServerForm stringValueAt:1]);

   msgDelivered = [mySpeaker addServer:server site:site ok:&flag];

   if (msgDelivered != 0)
      fprintf (stderr, "Sorry, addServer message not delivered.\n");
   else
      if (!flag) {
	 fprintf (stderr, "\n*** addServer call failed.\n");
	 fprintf (stderr, "*** Perhaps Ph is still fetching the Nameserver sites from its default server.\n");
         fprintf (stderr, "*** Try again.\n");
      }
   
   free (mySpeaker);
   
   return self;
}

/*---------------------------------------------------------------------------
This method queries Ph for its Nameservers.  It will fail if Ph has not
completed its initialization process.  You should check for this condition &
retry until successful or your patience runs out.  The Server names are
returned in a tab-separated list; likewise for the site names.
-----------------------------------------------------------------------------*/
- getServers:sender
{
   char          *aPtr;
   char          *buf;
   int            flag;
   int            msgDelivered;
   char          *serverNames;
   char          *siteNames;

   [self outputStr:""];
      
   if (![self createPort:self])
      return self;

   msgDelivered = [mySpeaker getServers:&serverNames sites:&siteNames ok:&flag];

   if (msgDelivered != 0) {
      fprintf (stderr, "Sorry, getServers message not delivered.\n");
      free (mySpeaker);
      return self;
   }

   if (!flag)
      [self outputStr:"There is no server/site info yet.\nPerhaps Ph is still getting the info from its default server.\nTry again."];
   else {

      /*** This is a sloppy (generous) estimate of needed buffer space. */

      buf = malloc (strlen (serverNames) + strlen (siteNames) + 50);

      /*** Process the tab-separated list of server names. */

      strcpy (buf, "*** Servers ***\n");

      aPtr = strtok (serverNames, "\t");
      while (aPtr != 0) {
	 strcat (buf, aPtr);
	 strcat (buf, "\n");
	 aPtr = strtok (NULL, "\t");
      }

      /*** Process the tab-separated list of site names. */

      strcat (buf, "\n\n*** Sites ***\n");

      aPtr = strtok (siteNames, "\t");
      while (aPtr != 0) {
	 strcat (buf, aPtr);
	 strcat (buf, "\n");
	 aPtr = strtok (NULL, "\t");
      }

      [self outputStr:buf];
      free (buf);
   }
   
   free (mySpeaker);
   
   return self;
}

/*---------------------------------------------------------------------------
This is perhaps the most useful method.  It tells Ph to open a query window
with a specific CSO Nameserver.  If the server & site are not in the Ph server
list, they will be added to the list before Ph tries to establish a connection
with the server.  Thus, it is not necessary to issue the "addServer" method
first.
-----------------------------------------------------------------------------*/
- showServer:sender
{
   int            flag;
   int            msgDelivered;
   char           server[128];
   char           site[128];

   if (![self createPort:self])
      return self;

   strcpy (server, [showServerForm stringValueAt:0]);
   strcpy (site, [showServerForm stringValueAt:1]);

   msgDelivered = [mySpeaker showServer:server site:site ok:&flag];

   if (msgDelivered != 0) {
      fprintf (stderr, "Sorry, showServer message not delivered.\n");
   }
   else
      if (!flag) {
	 fprintf (stderr, "\n*** showServer call failed. ***\n");
	 fprintf (stderr, "*** Should only happen if 'server' is empty or too long.\n");
      }
   
   free (mySpeaker);
   
   return self;
}

/*---------------------------------------------------------------------------
Tell Ph to hide itself.
-----------------------------------------------------------------------------*/
- hide:sender
{
   int            msgDelivered;

   if (![self createPort:self])
      return self;
   
   msgDelivered = [mySpeaker hide];

   if (msgDelivered != 0)
      fprintf (stderr, "Sorry, hide message not delivered.\n");

   free (mySpeaker);
   
   return self;
}

/*---------------------------------------------------------------------------
Tell Ph to unhide itself.  If Ph is not running, this will wake it up.
-----------------------------------------------------------------------------*/
- unhide:sender
{
   int            msgDelivered;

   if (![self createPort:self])
      return self;
   
   msgDelivered = [mySpeaker unhide];

   if (msgDelivered != 0)
      fprintf (stderr, "Sorry, unhide message not delivered.\n");

   free (mySpeaker);
   
   return self;
}

/*---------------------------------------------------------------------------
This method will tell Ph to open a query window with a specific CSO
Nameserver and to perform a name/department query.  Note: The query
command must be a tab-separated ("\t") list of query commands.  Example:

       name=Rex Pruess\tdepartment=Weeg

If the server & site are not in the Ph server list, they will be added
to the list before Ph tries to establish a connection with the server.
Thus, it is not necessary to issue the "addServer" method first.
-----------------------------------------------------------------------------*/
- queryServer:sender
{
   int            flag;
   int            msgDelivered;

   char           command[1024];
   char           server[128];
   char           site[128];

   if (![self createPort:self])
      return self;

   strcpy (server, [showServerForm stringValueAt:0]);
   strcpy (site, [showServerForm stringValueAt:1]);

   /*** Build the tab-separated query command. */

   strcpy (command, "name=");
   strcat (command, [queryServerForm stringValueAt:0]);
   strcat (command, "\t");

   strcat (command, "department=");
   strcat (command, [queryServerForm stringValueAt:1]);

   /*** Send the command to the server. */

   msgDelivered = [mySpeaker queryServer:server site:site command:command ok:&flag];

   if (msgDelivered != 0)
      fprintf (stderr, "Sorry, queryServer message not delivered.\n");
   else
      if (!flag) {
	 fprintf (stderr, "\n*** queryServer call failed. ***\n");
	 fprintf (stderr, "*** Should only happen if 'server' is empty or too long.\n");   
      }
   
   free (mySpeaker);
   
   return self;
}

/*---------------------------------------------------------------------------
This internal support method sets the Speaker port and creates the PhSpeaker
object.
-----------------------------------------------------------------------------*/
- (BOOL) createPort:sender
{
   port_t         thePort;

   thePort = NXPortFromName ("Ph", NULL);
   
   if (thePort == PORT_NULL) {
      fprintf (stderr, "\n*** PhSpeaker port is NULL.\n");
      fprintf (stderr, "*** Perhaps Ph is not installed in a standard directory.\n");
      return NO;
   }

   mySpeaker = [[PhSpeaker alloc] init];

   [mySpeaker setSendPort:thePort];

   return YES;
}

/*---------------------------------------------------------------------------
This internal support method displays output in the scrollview.
-----------------------------------------------------------------------------*/
- outputStr:(const char *)aString
{
   id             outText;
   
   [stpWindow disableFlushWindow];

   outText = [scrollView docView];
   [outText setSel:0 :[outText textLength]];
   [outText replaceSel:aString];

   [[stpWindow reenableFlushWindow] flushWindow];

   return self;
}
      
@end
