/****************************
*  dispmod.c
*  Display module for ROBBS.
*****************************/

#include <libraries/dos.h>
#include <fcntl.h>
#include <functions.h>
#include <exec/types.h>
#include <stdio.h>
#include <intuition/intuition.h>
#include <exec/memory.h>
#include <rexx/storage.h>
#include <rexx/rxslib.h>
#include "ConsoleIO.h"
#include "Rexx.h"
#include "defs.h"

char  portname[17]     = "ROBBS_ctrl";
char  macroport[17]    = "ROBBS_ctrl";
char  command[10]      = "SEND";
char  keystring[255];
char  status[100];
char  normaltext[]     = "\x9Bm";
char  redtext[]        = "\x9B33m";
UBYTE  titlestring[77] = VERSION;
STRPTR  Token;

int  connected;
int  outstanding_rexx_commands = 0;
int  CRPending = 0;

struct  RexxLib  *RexxSysBase;
struct  MsgPort  RexxPort;
struct  RexxMsg  *rmptr, *OutMsg, *CreateRexxMsg();

BOOL KeepGoing = TRUE;

void    FreePort();
char    nexttoken();
STRPTR  CreateArgstring();

struct IntuitionBase *IntuitionBase = NULL;
static struct  IOStdReq *Console = NULL;      /* see "ConsoleIO.h"  */

static  struct  Window *mywindow = NULL;      /* ptr to applications window */
static  struct  IntuiMessage *NewMessage;     /* msg structure for GetMsg() */
static struct IOStdReq *rd = NULL, *wr = NULL;

static char *validargs[] =
  {
  "TEXT",     /* 0  */ 
  "LTEXT",    /* 1  */ 
  "HTEXT",    /* 2    highlighted text, one colour only for now */
  "LHTEXT",   /* 3     same, with linefeed appended */
  "CRSR",     /* 4     cursor row, column */
  "GETLINE",  /* 5     getline [prompt]   */
  "STATUS",   /* 6  */
  "CONNECT",  /* 7  */
  "CTRL",     /* 8  */
  "TITLE",    /* 9  */
  "DIE",     /* 10 */
  NULL
  };

static  int    numcmds = 11;

/* my window structure */
static struct NewWindow NewWindow = {
   0,0,640,200, /* left, top, width, height */
   0,1,   /* detail, block pens */
   CLOSEWINDOW | GADGETUP,
   WINDOWCLOSE
      | SMART_REFRESH
      | ACTIVATE
      | WINDOWDEPTH
      | BORDERLESS
      ,
   NULL,       /* firstgadget */
   NULL,       /* checkmark */
   titlestring, /* title */
   0,          /* screen */
   NULL,       /* bitmap */
   0,0,        /* min x,y */
   640, 200,   /* max x,y */
   WBENCHSCREEN  /* type */
};

/*--------------------------------------------------------------*/
/*      cleanup: all done so close up and prepare to exit       */
/*--------------------------------------------------------------*/

static void cleanup()
  {
  if (RexxSysBase != NULL)
    {
    RemPort(&RexxPort);                   /* unlink it                     */
    FreePort(&RexxPort);                  /* release the port resources    */
    CloseLibrary(RexxSysBase);
    }
  if ( mywindow != NULL )
    CloseWindow( mywindow );
  if ( Console != NULL )
    CloseConsoleIO();
  if ( IntuitionBase != NULL )
    CloseLibrary(IntuitionBase);
  }

/*-------------------------------------------------------*/
/*      init: initialize dispmod - set defaults          */
/*-------------------------------------------------------*/

static void init()
  {

  IntuitionBase = (struct IntuitionBase *)
    OpenLibrary("intuition.library", INTUITION_REV);
  if( IntuitionBase == NULL )
    {
    puts("can't open intuition\n");
    goto badopen;
    }

  if(( mywindow = OpenWindow(&NewWindow) ) == NULL)
    {
    puts("Can't open a window.\n");
    goto badopen;
    }

  if(( Console = InitConsoleIO( mywindow, &rd, &wr ) ) == NULL)
    {
    puts("Can't open the console.\n");
    goto badopen;
    }

  RexxSysBase = (struct RexxLib *)OpenLibrary("rexxsyslib.library",0L);
  if (RexxSysBase == NULL)
    {
    printf("Can't open rexxsyslib.library.\n");
    goto badopen;
    }
  InitPort(&RexxPort,"ROBBS_disp");    /* Initialize our message port   */
  AddPort(&RexxPort);          /* Make the port public          */

  return;

  badopen:
  cleanup();
  exit( 4 );
  }

/*--------------------------------------------------------------*/
/*      buildstatus: build status string from variables         */
/*--------------------------------------------------------------*/

void buildstatus()
{
int j;

  sprintf(status, "%s %s %lx %lx",portname, macroport, mywindow, wr);
}

/*-----------------------------------------------------------------*/
/*  send_rexx_msg: send a message to a port                       */
/*-----------------------------------------------------------------*/

send_rexx_msg(port, cmd, s)
char *port;
char *cmd;
char *s;
{
  struct MsgPort *localport;

  /* RexxPort is reply port. look for reply there */

  OutMsg = CreateRexxMsg(&RexxPort, "dccs", "DCCS" );
  if (OutMsg != NULL)
    {

    OutMsg->rm_Args[0] = CreateArgstring( cmd, (long) strlen( cmd ));
    OutMsg->rm_Args[1] = CreateArgstring( s, (long) strlen( s ));

    OutMsg->rm_Action = RXFUNC | 1;
  /*    FillRexxMsg(OutMsg, 1L, 0L);    */

    Forbid();
    localport = FindPort(port);
    if (localport != 0) PutMsg(localport, OutMsg);
    Permit();
    };

  outstanding_rexx_commands++;
  return(0);              /* ya, I know, I need error checking. */
}

/*-----------------------------------------------------------------*/
/*  free_rexx_command: free the message we sent and decrement count */
/*-----------------------------------------------------------------*/

free_rexx_command(rexxmsg)
register struct RexxMsg *rexxmsg;
{
  /* delete the argument that we originally sent */

  DeleteArgstring(rexxmsg->rm_Args[0]);
  DeleteArgstring(rexxmsg->rm_Args[1]);

  /* delete the extended message */

  DeleteRexxMsg(rexxmsg);

  /* decrement the count of outstanding messages */

  outstanding_rexx_commands--;
}

/*-----------------------------------------------------------------*/
/*  sendfkey(number): inform of function key usage                */
/*-----------------------------------------------------------------*/

sendfkey( num )
short num;
{
char message[8];

  sprintf( message, "%c", num );
  send_rexx_msg(macroport, "FKEY", message);
}

/*-----------------------------------------------------------------*/
/*  sendhelp(number): inform of HELP key usage                      */
/*-----------------------------------------------------------------*/

sendhelp()
{
  send_rexx_msg(macroport, "HELP", NULL);
}

/*-----------------------------------------------------------------*/
/*  nexttoken(): search for next space or NULL in token string      */
/*-----------------------------------------------------------------*/
char
nexttoken()
{
  for ( ;(*Token != ' ' && *Token != '\0'); Token++ );
  if (*Token == ' ') {
    Token++;
    return(' ');
    }
  else
    return('\0');
}

/*-----------------------------------------------------------------*/
/*  cr2lf(s): convert all instances of CR in string s to linefeeds  */
/*         : leave cr/lf combination alone                         */
/*-----------------------------------------------------------------*/

cr2lf ( s )
char *s;
{

  if ( CRPending )
    if ( *s != '\n' )
      PutString("\n");
  CRPending = 0;
  for (; *s; s++) {
    if ( (*s == '\r') && (*(s+1) != '\n') )
      if ( *(s+1) = '\0' )
        CRPending = 1;
      else
        *s = '\n';
    }
}

/******************************************************/
/*             Main Program                            */
/*                                                    */
/*      This is the main body of the program.         */
/******************************************************/

main()
{
ULONG class, waitmask;
int c, i, j, x, y, code, keycount ;
UBYTE character;

  init();

  waitmask = (1L << mywindow->UserPort->mp_SigBit)
    |  (1L << Console->io_Message.mn_ReplyPort->mp_SigBit)
    |  (1L << RexxPort.mp_SigBit);

  KeepGoing = TRUE;
  while( KeepGoing )
    {
    if (( rmptr = (struct RexxMsg *) GetMsg(&RexxPort)) == NULL )
      {
      Wait( waitmask );
      rmptr = (struct RexxMsg *) GetMsg(&RexxPort);
      }
    if (rmptr)
      {
      if (rmptr->rm_Node.mn_Node.ln_Type == NT_REPLYMSG )
        free_rexx_command(rmptr);
      else
        {
        Token = rmptr->rm_Args[0];
        for (i=0; i < numcmds; i++)
          if ( StrcmpN(rmptr->rm_Args[0], validargs[i], 3L) == NULL)
            break;

        if (rmptr->rm_Action & 15L)  {
          Token = rmptr->rm_Args[1];
          }
        else  {
          nexttoken();
          }

        /* Set 'normal result codes'. these are set here, */
        /* and only changed in case of error or when      */
        /* returning a result string (as in STATUS).      */

        rmptr->rm_Result1 = 0;           /* return code      */
        rmptr->rm_Result2 = 0;           /* secondary result */

        switch (i)
          {
          case 0:    /* TEXT  */
          case 1:    /* LTEXT  */
/*            cr2lf(Token);        */
            PutString(normaltext);
            PutString(Token);
            if (i == 1) PutString("\n");
            break;
          case 2:    /* HTEXT  */
          case 3:    /* LHTEXT  */
/*            cr2lf(Token);        */
            PutString(redtext);
            PutString(Token);
            PutString(normaltext);
            if (i == 3) PutString("\n");
            break;
          case 4:    /* CURSOR  */
            for (j=0; Token[j] != ' '; j++);
              Token[j] = '\0';
            j++;
            y = atoi(Token);
            x = atoi(&Token[j]);
            CursorXY(x,y);
            break;
          case 5:    /* GETLINE */
            if (*Token)
              GetLine(Token, keystring);
            else
              GetLine(NULL, keystring);
            if (rmptr->rm_Action & (RXFF_RESULT))
              rmptr->rm_Result2 = (long)CreateArgstring(keystring,
                            (long)strlen(keystring));
            break;
          case 6:    /* STATUS  */
            buildstatus();
            if (rmptr->rm_Action & (RXFF_RESULT))
              rmptr->rm_Result2 = (long)CreateArgstring(status,
                            (long)strlen(status));
            break;
          case 7:    /* CONNECT */
            if (*Token)  {
              for (j=0; Token[j] != ' '; j++) {
                portname[j] = Token[j];
                }
                portname[j] = '\0';
              nexttoken();
              for (j=0; Token[j] != ' ' && Token[j]; j++) {
                command[j] = Token[j];
                }
                command[j] = '\0';
              connected = 1;
              }
            else
              connected = 0;
            break;
          case 8:    /* CTRL */
            if (*Token)
              strcpy(macroport, Token);
            break;
          case 9:    /* TITLE */
            strcpy(titlestring, Token);
            SetWindowTitles(mywindow, titlestring, 0L);
            break;
          case 10:    /* DIE */
            KeepGoing = FALSE;
            break;

          default:
            rmptr->rm_Result1 = 5;           /* return code      */
            break;
          }
        ReplyMsg(rmptr);                 /* send it back     */
        }
      }

    /*-----  Drain the keyboard buffer--------------*/

    keystring[0] = '\0';
    keycount = 0;
    while ( c = CheckKey() )
      {
      if (c != CSI)
        {
        keystring[keycount++] = c;
        keystring[keycount] = '\0';
        }
      else    /* here if c == CSI */
        {
        character = GetKey();
        if ( ('A' <= character) && (character <= 'D') );

        if ( ('0' <= character) && (character <= '9') ) {
          sendfkey(character);
          character = GetKey();
          }

        if (character == '?') {
          sendhelp();
          }

        if  (((character  < 'A') || (character > 'D')) &&
          ((character != 'T')
          && (character != 'S')
          && (character != '~')
          && (character != ' ')))
          while ( GetKey() != '~');

        if (character == ' ') {
          GetKey();
          }

        }    /* end if CSI */
      }   /* end while CheckKey() */
    if (keycount && connected) {
      send_rexx_msg(portname, command, keystring);
      }

     /* ---------------- Handle all Messages --------------------*/

    while( NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort) )
      {
      class = NewMessage->Class;
      code = NewMessage->Code;
      ReplyMsg( NewMessage );
      switch( class )
        {
        case CLOSEWINDOW:
          KeepGoing = FALSE;
          break;
        }      /* end of switch (class)    */
      }        /* end of while ( newmessage )*/
    
    }          /* end while ( keepgoing )    */

  while (outstanding_rexx_commands)
    {
    printf("dispmod: Outstanding commands: %d\n",outstanding_rexx_commands );
    if (( rmptr = (struct RexxMsg *) GetMsg(&RexxPort)) == NULL )
      {
      Wait( 1L << RexxPort.mp_SigBit );
      rmptr = (struct RexxMsg *) GetMsg(&RexxPort);
      }
      if (rmptr) {
      if( rmptr->rm_Node.mn_Node.ln_Type == NT_REPLYMSG ) {
        free_rexx_command(rmptr);
        }
      else {
        if (StrcmpN( rmptr->rm_Args[0], "REALLY_DIE", 10L) == NULL)
          ReplyMsg(rmptr);
        break;
        }
      }
    }

  cleanup();
  exit( 0 );
} /* end of main */
