/*************************************************************************
Talk Client written by Jeffrey A. Litz for Envoy Talk Service

litz@cs.uwp.edu  -or-  Jeff_Litz@EDTNG.Kenosha.WI.US

Copyright ©1994 JL Productions.
*************************************************************************/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/semaphores.h>
#include <envoy/nipc.h>
#include <envoy/services.h>
#include <pragmas/exec_pragmas.h>
#include <pragmas/nipc_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/services_pragmas.h>

#include <utility/tagitem.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/nipc_protos.h>
#include <clib/services_protos.h>
#include <clib/alib_protos.h>
#include <clib/alib_stdio_protos.h>

#include <string.h>

#define geta4 __builtin_geta4
void geta4(void);

#define TKCMD_TALK  1
#define TKCMD_ABORT 2
#define TKCMD_DATA  3

struct Library *SysBase,*DOSBase,*NIPCBase,*ServicesBase;

static UBYTE version[32] = {"$VER: Talk Client v1.2a"};

void _main(void)
{
    struct RDArgs *lpargs;
    void *entity, *dest, *a_entity;
    struct Transaction *trans,*a_trans;
    ULONG sigbit=0,sigbit2=0,waitmask,signal,howmany;
    ULONG args[1];
    BPTR input_w,output_w;
    ULONG done=0;
    UBYTE work_space[512],host_name[256],userinput[256];
    struct MsgPort *port,*replyport;
    struct StandardPacket *packet;
    struct TagItem cetags[4],cbtags[2],ttags[3];
    
    geta4();
    SysBase = *((struct Library **)4L);

    /* Initialize a bunch of stuff for setting up transactions */

    cetags[0].ti_Tag=ENT_Name;
/* Assigned later */
//  cetags[0].ti_Data=NULL;
    cetags[1].ti_Tag=ENT_Public;
    cetags[1].ti_Data=NULL;
    cetags[2].ti_Tag=ENT_AllocSignal;
    cetags[2].ti_Data=(ULONG) &sigbit;
    cetags[3].ti_Tag=TAG_END;
    cetags[3].ti_Data=0;
 
    cbtags[0].ti_Tag=ENT_AllocSignal;
    cbtags[0].ti_Data=(ULONG) &sigbit2;
    cbtags[1].ti_Tag=TAG_END;
    cbtags[1].ti_Data=0;
 
    ttags[0].ti_Tag=TRN_AllocReqBuffer;
    ttags[0].ti_Data=516;
    ttags[1].ti_Tag=TRN_AllocRespBuffer;
    ttags[1].ti_Data=4;
    ttags[2].ti_Tag=TAG_END;
    ttags[2].ti_Data=0;
 
    /* open up dos library */
    if(DOSBase=OpenLibrary("dos.library", 37L))
    {
        /* Check to see if a machine to talk to was specified */
        args[0]=0;
        if(lpargs=ReadArgs("Server/A",args,NULL))
        {
            /* open up nipc library */
            if(NIPCBase=OpenLibrary("nipc.library", 0L))
            {
                /* open up services library */
                if(ServicesBase=OpenLibrary("services.library", 37L))
                {
                    /* create a "near" side entity - to SEND data to service */
                    if(entity=CreateEntityA((struct TagItem *) cbtags))
                    {
                        /* Find out exactly who WE are */
                        GetHostName(entity,host_name,255);

                        /* Setup our entity name */
                        cetags[0].ti_Data=(ULONG) "talk_back";

                        /* create a "far" side entity - to RECEIVE data from service */
                        if(a_entity=CreateEntityA((struct TagItem *) cetags))
                        {
                           /* create a transaction for sending data to service */
                           if(trans=AllocTransactionA((struct TagItem *) ttags))
                           {
                              /* Locate the service */
                              if(dest=FindServiceA((UBYTE *)args[0], "Talk_Service", entity, NULL))
                              {
                                 /*
                                 ** OK - found, now setup a transaction to
                                 ** send to the talk service to request a
                                 ** talk with us
                                 */
                                 
                                 *(ULONG *)(trans->trans_RequestData)=*(ULONG *)(trans->trans_ResponseData);
                                 strcpy(((UBYTE *)trans->trans_RequestData+4),host_name);
                                 trans->trans_ReqDataActual=strlen(host_name)+4;
                                 trans->trans_Command=TKCMD_TALK;
                                 trans->trans_Timeout=10;
                                 DoTransaction(dest,entity,trans);
                                 
                                 /* Was there any type of transaction error? */
                                 if(trans->trans_Error==0)
                                 {
                                    /* Everything went OK, *PLUS* the person wishes to talk with us */
                                    /* Open up the input and output windows */
                                    if(output_w=(BPTR)Open("CON:0/0/640/150/Output Window",MODE_NEWFILE))
                                    {
                                       if(input_w=(BPTR)Open("CON:0/150/640/50/Input Window/CLOSE",MODE_NEWFILE))
                                       {
                                          /*
                                          ** Setup everything for reading
                                          ** from the input window using
                                          ** packets
                                          */
                                          
                                          /* Create a packet for reading the keyboard */
                                          if(packet=(struct StandardPacket *)AllocMem((long)sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR))
                                          {
                                             port=(struct MsgPort *) (((struct FileHandle *)((ULONG)input_w<<2))->fh_Type);
                                             if(replyport=(struct MsgPort *) CreatePort(NULL,0))
                                             {
                                                packet->sp_Msg.mn_Node.ln_Name=(char *)&(packet->sp_Pkt);
                                                packet->sp_Pkt.dp_Link=&(packet->sp_Msg);
                                                packet->sp_Pkt.dp_Port=replyport;
                                                packet->sp_Pkt.dp_Type=ACTION_WAIT_CHAR;
                                                packet->sp_Pkt.dp_Arg1=34000;
                                                SendPkt(&packet->sp_Pkt,port,replyport);
                                          
                                                /*
                                                ** set the wait mask for a
                                                ** wait for character from
                                                ** the input window and for
                                                ** any incoming transactions
                                                */
                                                
                                                waitmask = (1<<replyport->mp_SigBit) | (1<<sigbit);
                                          
                                                while(!done)
                                                {
                                                   signal = Wait(waitmask);
                                                   
                                                   /* Was there keyboard input? */
                                                   if(signal & (1<<replyport->mp_SigBit))
                                                   {  
                                                      /* check to make sure */
                                                      if(GetMsg(replyport))
                                                      {
                                                         /* check again */
                                                         if(packet->sp_Pkt.dp_Res1)
                                                         {
                                                            /* read the input */
                                                            if(!(howmany=Read(input_w,userinput,255)))
                                                            {
                                                               /* close gadget - signal shut down */
                                                               done=2;
                                                            } else
                                                            {
                                                               /* Send input to our output window and send a transaction to the service to be displayed on the other side */
                                                               userinput[howmany]=0;
                                                               sprintf(work_space,"(%s) %s\n",host_name,userinput);
                                                               Write(output_w,work_space,strlen(work_space));
                                                               trans->trans_Command=TKCMD_DATA;
                                                               *(ULONG *)(trans->trans_RequestData)=*(ULONG *)(trans->trans_ResponseData);
                                                               strcpy(((UBYTE *)trans->trans_RequestData+4),work_space);
                                                               trans->trans_ReqDataActual=strlen(work_space)+4;
                                                               trans->trans_Timeout=10;
                                                               DoTransaction(dest,entity,trans);
                                                            }
                                                         }
                                                         /* setup another packet for wait for character */
                                                         packet->sp_Pkt.dp_Port=replyport;
                                                         packet->sp_Pkt.dp_Type=ACTION_WAIT_CHAR;
                                                         packet->sp_Pkt.dp_Arg1=34000;
                                                         SendPkt(&packet->sp_Pkt,port,replyport);
                                                      }
                                                   }
                                                   /* was there a transaction? */
                                                   if(signal & (1<<sigbit))
                                                   {
                                                      /* get the transaction */
                                                      if(a_trans=GetTransaction(a_entity))
                                                      {
                                                         /* was it a data transaction or close down transaction */
                                                         if(a_trans->trans_Command == TKCMD_DATA)
                                                         {
                                                            Write(output_w,(UBYTE *)((UBYTE *)a_trans->trans_RequestData+4),a_trans->trans_ReqDataActual-4);
                                                         } else if(a_trans->trans_Command == TKCMD_ABORT)
                                                         {
                                                            done=1;
                                                         }
                                                         
                                                         /* reply to the DoTransaction() from the other side */
                                                         ReplyTransaction(a_trans);
                                                      }
                                                   }
                                                }
                                                /* Attempt to clear out any "left over" packet messages */
                                                while((ULONG *)GetMsg(replyport) != (ULONG *)&packet->sp_Msg);
                                                DeletePort(replyport);
                                             }
                                             /* Free up packet allocation */
                                             FreeMem(packet,(long)sizeof(struct StandardPacket));
                                          }
                                          /* close input and output windows */
                                          Close(input_w);
                                       }
                                       Close(output_w);
                                    }
                                    /* If we initiated the close down - attempt to signal other side to prevent a "hang" */
                                    if(done == 2)
                                    {
                                       /* Just trying to make sure the TKCMD_ABORT gets through */
                                       
                                       howmany=4;
                                       while(howmany--)
                                       {
                                          trans->trans_ReqDataActual=4;
                                          trans->trans_Command=TKCMD_ABORT;
                                          trans->trans_Timeout=10;
                                          DoTransaction(dest,entity,trans);
                                          if(trans->trans_Error == 0)
                                          {
                                                break;
                                          }
                                       }
                                    }
                                 } else if(trans->trans_Error == 1)
                                 {
                                    /* talk service already active */
                                    FPrintf(Output(),"Busy - talk already in process.\n");
                                 } else if(trans->trans_Error == 2)
                                 {
                                    /* we were either ignored or no one was available */
                                    FPrintf(Output(),"No response from talk request.\n");
                                 }
                                 /* lose the service */
                                 LoseService(dest);
                              } else
                              { 
                                 /* something drastically went wrong */
                                 /* either machine doesn't exist or service isn't available on that machine */
                                 FPrintf(Output(),"Unable to connect with the machine or service on that machine.\n");
                              }
                              /* free up the transaction allocation */
                              trans->trans_ReqDataLength = 516;
                              FreeTransaction(trans);
                           }
                           /* clean up - delete/close everything */
                           DeleteEntity(a_entity);
                        }
                        DeleteEntity(entity);
                     }
                     CloseLibrary(ServicesBase);
                }
                CloseLibrary(NIPCBase);
            }
            FreeArgs(lpargs);
        } else
        {
            /* readargs failed - no machine name provided */
            FPrintf(Output(),"Must provide a machine name to talk to.\n");
        }
        CloseLibrary(DOSBase);
    }
}
