/****************************************************************************
   fancydemo.c - A fancy rexx host that can send and receive messages.

   Author - Gary Samad & Bill Hawes

   Revisions:
      7-Mar-88   Original version.
     16-Mar-88   Added result string return (WSH)

   This is truly Public Domain!!
****************************************************************************/

#include "storage.h"
#include "rxslib.h"
#include <exec/ports.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>

#define YES      1
#define NO      0

#define OK      0
#define NOTOK      1

#define EOS      '\0'

#define NO_REXX_MSG   "Rexx is not active.  Please run 'rexxmast' from another CLI.\n"
#define STARTUP_MSG   "Type commands to rexx.  Type EOF (^\\) to end.\n"
#define CLOSING_MSG   "Ok, we're closing (after all rexx messages have returned).\n"

#define WINDOW_SPEC   "CON:0/10/600/60/Fancy Demo Input Window/c"
#define HOST_PORT_NAME   "FancyDemo"
#define REXX_EXTENSION   "rexx"

#define BUFFLEN      100

/* THIS NAME MUST BE RexxSysBase FOR THE GLUE ROUTINES */
struct RxsLib *RexxSysBase = NULL;   /* this is the rexx library base */

int outstanding_rexx_commands = 0;

BPTR window_file_handle = NULL;
struct MsgPort *dos_reply_port = NULL;
struct StandardPacket *dos_message = NULL;
struct MsgPort *rexx_port = NULL;

main()
{
   struct Message *GetMsg();
   BPTR open_window();
   struct MsgPort *setup_dos_reply_port();
   struct MsgPort *setup_rexx_port();
   struct StandardPacket *setup_dos_message();
   void send_read_packet();
   int send_rexx_command();
   void execute_command();
   void reply_rexx_command();
   void free_rexx_command();

   int packet_out = NO;      /* whether a READ is outstanding */
   char buff[BUFFLEN+1];      /* used for reading user input */
   struct RexxMsg *rexxmessage;   /* incoming rexx messages */
   int close_down = NO;      /* set when the user hits EOF */

   /* open a window to talk to the user through */
   if ((window_file_handle = open_window()) == NULL)
   {
     printf("sorry, couldn't open a CON: window\n");
     close_up_shop(10);
   }

   /* set up a port for dos replys */
   if ((dos_reply_port = setup_dos_reply_port()) == NULL)
   {
     printf("sorry, couldn't set up a dos_reply_port\n");
     close_up_shop(10);
   }

   /* set up a public port for rexx to talk to us later */
   if ((rexx_port = setup_rexx_port()) == NULL)
   {
     printf("sorry, couldn't set up our public rexx port\n");
     close_up_shop(10);
   }

   /* set up a dos packet for the asynchronous read from the window */
   if ((dos_message = setup_dos_message()) == NULL)
   {
     printf("sorry, not enough memory for a dos packet\n");
     close_up_shop(10);
   }

   Write(window_file_handle,STARTUP_MSG,(long)sizeof(STARTUP_MSG));

   /* loop until quit and no messages outstanding */
   while (!close_down || outstanding_rexx_commands)
   {
     /* if the packet (for user input) has not been sent out, send it */
     if (!packet_out && !close_down)
     {
       /* send a packet to dos asking for user keyboard input */
       send_read_packet(dos_message,window_file_handle,dos_reply_port,buff);
       packet_out = YES;
     }
       
     /* now wait for something to come from the user or from rexx */
     Wait((1L<<dos_reply_port->mp_SigBit) | (1L<<rexx_port->mp_SigBit));

     /* got something!! */
     /* is it a command from the user? */
     if (GetMsg(dos_reply_port))
     {
       /* not out any more */
       packet_out = NO;

       /* if EOF (either the close gadget was hit or ^\) */
       if (dos_message->sp_Pkt.dp_Res1 == 0)
       {
         close_down = YES;
         Write(window_file_handle,CLOSING_MSG,(long)sizeof(CLOSING_MSG));
       }
       else
       {
         /* NULL terminate the string (thanks again DOS!) */
         buff[dos_message->sp_Pkt.dp_Res1-1] = EOS;

         /* send the command directly to rexx */
         if (send_rexx_command(buff) != OK)
         {
           Write(window_file_handle,NO_REXX_MSG,(long)sizeof(NO_REXX_MSG));
         }
       }
     }

     /* did we get something from rexx? */
     while(rexxmessage = (struct RexxMsg *)GetMsg(rexx_port))
     {
       /* is this a reply to a previous message? */
       if (rexxmessage->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
       {
printf("the command '%s' has terminated with code %ld, %ld\n",
  rexxmessage->rm_Args[0],rexxmessage->rm_Result1,rexxmessage->rm_Result2);
         free_rexx_command(rexxmessage);
       }
       else
       {
         /* a rexx macro has sent us a command, deal with it */
         /* THE MESSAGE WILL HAVE BEEN REPLIED INSIDE OF execute_command */
         execute_command(rexxmessage);
       }
     }
   }

   /* clean up */
   close_up_shop(0);
}

close_up_shop(value)
int value;
{
   if (window_file_handle)
     close_window(window_file_handle);

   if (dos_reply_port)
     shutdown_dos_reply_port(dos_reply_port);

   if (rexx_port)
     shutdown_rexx_port(rexx_port);

   if (dos_message)
     free_dos_message(dos_message);

   exit(value);
}

/******** These are dos functions for getting and displaying user input *******/
BPTR open_window()
{
   BPTR Open();

   return(Open(WINDOW_SPEC,MODE_NEWFILE));
}

close_window(file_handle)
BPTR file_handle;
{
   Close(file_handle);
}

struct MsgPort *setup_dos_reply_port()
{
   struct MsgPort *CreatePort();

   return(CreatePort(NULL,0L));
}

shutdown_dos_reply_port(dos_reply_port)
struct MsgPort *dos_reply_port;
{
   DeletePort(dos_reply_port);
}

struct StandardPacket *setup_dos_message()
{
   struct StandardPacket *malloc();

   struct StandardPacket *new_packet;

   /* get a packet */
   if (new_packet = malloc(sizeof(struct StandardPacket)))
   {
     /* required AmigaDOS Kludge */
     new_packet->sp_Msg.mn_Node.ln_Name = (char *) &(new_packet->sp_Pkt);
     new_packet->sp_Pkt.dp_Link = &(new_packet->sp_Msg);
   }

   return(new_packet);
}

free_dos_message(dos_message)
struct StandardPacket *dos_message;
{
   free(dos_message);
}

void send_read_packet(dos_message,window_file_handle,dos_reply_port,buff)
struct StandardPacket *dos_message;
BPTR window_file_handle;
struct MsgPort *dos_reply_port;
char *buff;
{
   struct FileHandle *file_handle;

   /* change a BPTR to a REAL pointer */
   file_handle = (struct FileHandle *)(window_file_handle << 2);

   /* setup the packet for reading */
   dos_message->sp_Pkt.dp_Arg1 = file_handle->fh_Arg1;
   dos_message->sp_Pkt.dp_Arg2 = (long) buff;
   dos_message->sp_Pkt.dp_Arg3 = BUFFLEN;
   dos_message->sp_Pkt.dp_Type = ACTION_READ;
   dos_message->sp_Pkt.dp_Port = dos_reply_port;
   dos_message->sp_Msg.mn_ReplyPort = dos_reply_port;

   /* now send it */
   PutMsg(file_handle->fh_Type,dos_message);
}

/******** This is the REXX stuff ********/
struct MsgPort *setup_rexx_port()
{
   struct MsgPort *CreatePort();
   struct MsgPort *FindPort();

   struct MsgPort *the_port;

   Forbid();

   /* look for someone else that looks just like us! */
   if (FindPort(HOST_PORT_NAME))
   {
     Permit();
     printf("A public port called '%s' already exists!\n",HOST_PORT_NAME);
     return(NULL);
   }

   /* allocate the port */
   the_port = CreatePort(HOST_PORT_NAME,0L);

   Permit();

   return(the_port);
}

shutdown_rexx_port(rexx_port)
struct MsgPort *rexx_port;
{
   DeletePort(rexx_port);
}

int send_rexx_command(buff)
char *buff;
{
   struct MsgPort *FindPort();
   struct RsxLib *OpenLibrary();
   struct RexxMsg *CreateRexxMsg();
   STRPTR CreateArgstring();

   struct MsgPort *rexxport;      /* this will be rexx's port */
   struct RexxMsg *rexx_command_message;   /* this is the message */

   /* lock things temporarily */
   Forbid();

   /* if rexx is not active, just return NOTOK */
   if ((rexxport = FindPort(RXSDIR)) == NULL)
   {
     Permit();
     return(NOTOK);
   }

   /* now open the library, THIS SHOULD NEVER FAIL BECAUSE REXX IS ACTIVE*/
   if (outstanding_rexx_commands == 0)
     if ((RexxSysBase = OpenLibrary(RXSNAME,0L)) == NULL)
     {
       Permit();
       return(NOTOK);
     }

   /* allocate a message packet for our command */
   /* note that this is a very important call.  Much flexibility is */
   /* available to you here by using multiple host port names, etc. */
   if ((rexx_command_message = CreateRexxMsg(rexx_port,
                    REXX_EXTENSION,
                    rexx_port->mp_Node.ln_Name))
         /* the last parameter could have been HOST_PORT_NAME */
      == NULL)
   {
     if (outstanding_rexx_commands == 0)
     {
       CloseLibrary(RexxSysBase);
       RexxSysBase = NULL;
     }
     Permit();
     return(NOTOK);
   }

   /* create an argument string and install it in the message */
   if ((rexx_command_message->rm_Args[0] =
      CreateArgstring(buff,strlen(buff))) == NULL)
   {
     DeleteRexxMsg(rexx_command_message);
     if (outstanding_rexx_commands == 0)
     {
       CloseLibrary(RexxSysBase);
       RexxSysBase = NULL;
     }
     Permit();
     return(NOTOK);
   }

   /* tell rexx that this is a COMMAND, not a FUNCTION, etc. */
   rexx_command_message->rm_Action = RXCOMM;

   /* and now the EASY part! */
   PutMsg(rexxport,rexx_command_message);

   /* keep a count of outstanding messages for graceful cleanup */
   outstanding_rexx_commands++;

   /* we're done hogging */
   Permit();

   /* successful, finally... */
   return(OK);
}

void free_rexx_command(rexxmessage)
struct RexxMsg *rexxmessage;
{
   /* delete the argument that we originally sent */
   DeleteArgstring(rexxmessage->rm_Args[0]);

   /* delete the extended message */
   DeleteRexxMsg(rexxmessage);

   /* decrement the count of outstanding messages */
   outstanding_rexx_commands--;
   if (outstanding_rexx_commands == 0)
   {
     CloseLibrary(RexxSysBase);
     RexxSysBase = NULL;
   }
}

void execute_command(rexxmessage)
struct RexxMsg *rexxmessage;
{
   long primary=0,secondary=0;

printf("got '%s' from rexx\n",rexxmessage->rm_Args[0]);

   if (strcmp(rexxmessage->rm_Args[0],"BAD") == 0L) primary = 10L;

   reply_rexx_command(rexxmessage,primary,secondary,"A Test");
}

/* Replies a REXX message, filling in the appropriate codes.  If the macro
 * program has requested a result string, the return argstring is allocated
 * and installed in the rm_Result2 slot.
 *
 * A result is returned ONLY IF REQUESTED AND THE PRIMARY RESULT == 0.
 */

void reply_rexx_command(rexxmessage,primary,secondary,result)
struct RexxMsg *rexxmessage;
long           primary,secondary;
char           *result;
{
   /* set an error code */
   if (primary == 0 && (rexxmessage->rm_Action & 1L<<RXFB_RESULT)) {
      secondary = result ? (long) CreateArgstring(result,strlen(result))
                         : (long) NULL;
      }
   rexxmessage->rm_Result1 = primary;
   rexxmessage->rm_Result2 = secondary;
   ReplyMsg(rexxmessage);
}
