/*
 * SAMIGA.C - 19th Aug 1988 - System dependent functions for the
 * single-user SYSOP version of 'mb'.
 * Pete Hardie VE5VA
 *
 */

#include "mb.h"
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/text.h>
#include <ctype.h>
#include <time.h>
#include "rexxhostbase.h"

extern struct RexxHost      *rexx_host;
extern long rexxbit;
char arexx_port_name[50];
char *arexx_port = 0L;
char arexx_flag;

extern struct Window *mywindow;
extern struct IntuiMessage *NewMessage;
short debug;
char tmpstr[300];
extern struct IOStdReq *Con_Read,*Con_Write;
extern char optflags[];


char l_date[7], l_time[5];
char z_date[7], z_time[5];
char pt_flag = 0;
short done_flag = 1;
/*
 *  Set the current I/O device.
 */

ioport(pp)
PORTS *pp;
{
  port = pp;
}

/*
 *  Return non-zero if a character waits at current input.
 */

instat()
{
  register int st;

   /* check for char at console */
   if(CheckIO((struct IORequest *)Con_Read))return(1);
   return(0);
}

/*
 *  Put the character out the current port,
 *  echo to console if the current port is not the console.
 *  Note special handling of end-of-line character.
 */

outchar(ch)
char ch;
{
   if (ch is '\r') return;
   if (ch is '\n') ch = '\r';
/*
 *  Put it out to the console, if it is going there.
 */

   if ((port->dev is p_console) or port->ec)   {
      if (ch is '\r') ttputc('\n');/* output '\n' */;
      ttputc(ch);
   }
}
#define PROCLEN 200
unsigned char procstr[PROCLEN];
outstr(cp)
unsigned char *cp;
{
   register unsigned char *p,*q;
   register int beepcount;

   if(arexx_port) {
      remnl(cp);
if(debug)printf("Send '%s' to '%s'\n",cp,arexx_port);
      SendRexxMsg((STRPTR)arexx_port,0L,(STRPTR)cp,0L);
      return;
   }
   /* count the number of beeps in the line so that we can give an audible
      as well as visual beep
   */
   if((port->dev is p_console) || port->ec) {
      beepcount = 0;
      for(q = cp,p = &procstr[0];*q && p < &procstr[PROCLEN-1];q++) {
         if(*q is '\r')continue;
         *p++ = *q;
         if(*q == '\007')beepcount++;
      }
      *p = 0;
      Con_Write->io_Data = (APTR) &procstr[0];
      Con_Write->io_Length = p-&procstr[0];
      Con_Write->io_Command = CMD_WRITE;
      DoIO((struct IORequest *)Con_Write);
      initbeep();
      while(beepcount--) {
         beep();
         Delay(15L);
      }
      donebeep();
   }
}
/* samiga can't talk to the tnc! */
PORTS *devtnc = 0L;
char *inittnc(infile)
char *infile;
{
   return((char *)0);
}
/*
hex(c)
char c;
{
   register char k;
   k = c;
   if((k >= '0') && (k <= '9'))return(k - '0');
   k = toupper(k);
   if((k >= 'A') && (k <= 'F'))return(k - 'A' + 10);
   return(0);
}
*/
flipdebug()
{
   debug ^= 1;
   if(debug) {
      ttputs("debug now ON\n");
   }
   else {
      ttputs("debug now OFF\n");
   }
}
extern unsigned char ichar;
extern long keybit,windbit,timerbit;
long getbits;
struct timeRequest {
   struct IORequest tr_node;
   struct timeval tr_time;
};
extern struct timeRequest timermsg;

/* replace the original do_idle because it busy-waited ...
   this version sleeps until there is something to do
*/
extern short cursecs;
struct RexxMsg *rexxmessage;  /* incoming rexx messages */
do_idle()
{
   register unsigned char ch,*q;
   register PORTS *p;
   int curmin,curmon;
   ULONG class,waitbits;
   char go_flag;
   USHORT code;
   STRPTR Arg;       /* Temporary string pointer */

   reply_to_rexx();
   arexx_port = 0L;
   arexx_flag = 0;
   cursor_off();
   for(p=porthd; p != NULL; p = p->next) {
      p->ec = p->ecmon;
   }
   ioport(cport);
   /* figure out time to next forward and set timer */
   curtim();
   curmin = 60 - cursecs;
   settimer(curmin);
   /* There might already be an unread char at the serial or console port */
   clrbusy();
   waitbits = windbit | timerbit | keybit | rexxbit;
ttputs("\nIDLE\n");
   while(1)  {
      getbits = Wait(waitbits);
      go_flag = 0;
      /* Although SYSOP has an AREXX port open it does not take commands
         from other AREXX ports. It only gives them
      */
      if(rexxbit & getbits) {
         while(rexxmessage = GetRexxMsg(rexx_host,0L)) {
            if(Arg = GetRexxCommand(rexxmessage)) {
               if(!strncmp((char *)Arg,"GO",2)) {
                  /* Tell the pause() routine that our output is being
                     CAUSED BY, but not necessarily going to, an AREXX script
                  */
                  arexx_flag = 1;
                  go_flag++;
                  q = (unsigned char *)Arg + 2;
                  while((*q == ' ') || (*q == '\n'))q++;
                  if(*q) {
                     strcpy(arexx_port_name,(char *)q);
                     arexx_port = arexx_port_name;
                  }
                  else {
                     arexx_port = 0L;
                  }
               }
               ReplyRexxCommand(rexxmessage,0L,0L,0L);
            }
            else {
               FreeRexxCommand(rexxmessage);
            }
            rexxmessage = 0;
         }
         if(go_flag)goto cmd_exit;
      }
      if(windbit & getbits) {
         while(NewMessage =
              (struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            ReplyMsg((struct Message *)NewMessage);
            if(class == CLOSEWINDOW) {
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  if(timerbit & getbits) {
                     WaitIO((struct IORequest *)&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  SetWindowTitles(mywindow,(UBYTE *)"",(UBYTE *) -1L);
                  /* Need this to cancel out the clrbusy in done() */
                  setbusy();
                  done(1);
               }
            }
         }
      }
      if(getbits & keybit) {
         WaitIO((struct IORequest *)Con_Read);
         ch = ichar;
         Con_Read->io_Data = (APTR)&ichar;
         Con_Read->io_Command = CMD_READ;
         Con_Read->io_Length = 1;
         SendIO((struct IORequest *)Con_Read);
         if(ch == wchar) {
cmd_exit:
            ioport(cport);
            cursor_on();
            /* could have a timerbit set here.
               readbit could also be set but must handle that a
               different way.
            */
            if(timerbit & getbits) {
               WaitIO((struct IORequest *)&timermsg);
            }
            else {
               cantimer();
            }
            return;
         }
         if(ch == '\033') {
            ttputs("do_idle\n");
         }
      }
      /* This code is deliberately placed last. If a timer bit occurs
         at the same time as a character arrives from the serial port
         then this code will hang the serial port up because afwd()
         tries to use the port again without clearing the existing char.
         So workaround it. Putting the timer code here guarantees that
         even if the ser char did occur it will be handled first and the
         ser char code handles the problem of returning from this routine
         with a timer bit set.
      */
      if(timerbit & getbits) {
         WaitIO((struct IORequest *)&timermsg);
         curtim();
         curmin = 10 * (l_time[2] - '0') + (l_time[3] - '0');
         curmon = 10 * (l_date[2] - '0') + (l_date[3] - '0');
         settimer(60);
      }
   }
}
/* replace getdat from mbutil to reduce busy waits */
/* MAXLEN is the longest unterminated line the justification routine
   will accept before it rips out the word and keeps it for later use.
   MAXJUST is the longest word that the routine will keep ... if the word
   is any longer than this then it will just allow you to mess up the line!
*/
#define MAXLEN 77
#define MAXJUST 20
unsigned char x_pos = 0; /* Position on line for right justification */
unsigned char s_pos = 0; /* Position of last space on the line */
char saveword[MAXJUST+2];
int got_command;
char *cp;
getdat()
{

   register char ch;
   register PORTS *p;
   register char *q;
   int eflag;
   int curmin;
   ULONG class,waitbits;
   USHORT code;
   STRPTR Arg;

   reply_to_rexx();
   x_pos = s_pos = 0;
   p = port;
   cp = p->line;
   *cp = 0;
   /* If there was a justified word left over from previous line .. put it
      in here now.
   */
   if(*saveword) {
      q = saveword;
      while(*q) {
         ttputc(*q);
         *cp++ = *q++;
         x_pos++;
      }
   }
   *saveword = 0;
   /* Is a timeout timer required? */
   curmin = 0;
   if(p->flags & p_dotmr) {
         curmin = p->ctime;
   }
   if(curmin)settimer(curmin);   /* set timeout timer if required */
   p->flags setbit p_dotmr;

   waitbits = windbit | timerbit | keybit | rexxbit;
   while(1)  {
      getbits = Wait(waitbits);
      if(windbit & getbits) {
         while(NewMessage =
                    (struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            ReplyMsg((struct Message *)NewMessage);
            if(class == CLOSEWINDOW) {
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  if(timerbit & getbits) {
                     WaitIO((struct IORequest *)&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  SetWindowTitles(mywindow,(UBYTE *)"",(UBYTE *) -1L);
                  done(1);
               }
            }
         }
      }
      if(rexxbit & getbits) {
         got_command = 0;
         while(rexxmessage = GetRexxMsg(rexx_host,0L)) {
            if(Arg = GetRexxCommand(rexxmessage)) {
               /* If we didn't receive an AREXX GO command then send back
                  an error result code to make them go away
               */
               if(!arexx_flag) {
                  ReplyRexxCommand(rexxmessage,0L,0L,(STRPTR)"ERROR");
                  rexxmessage = 0;
               }
               else {
                  strcpy(p->line,(char *)Arg);
                  strcat(p->line,"\n");
                  got_command = 1;
                  if(timerbit & getbits) {
                     WaitIO((struct IORequest *)&timermsg);
                  }
                  else {
                     if(curmin)cantimer();
                  }
                  ReplyRexxCommand(rexxmessage,0L,0L,0L);
                  rexxmessage = 0;
               }
            }
            else {
               FreeRexxCommand(rexxmessage);
               rexxmessage = 0;
            }
         }
         if(got_command)return(1);
      }
      if(getbits & keybit) {
         WaitIO((struct IORequest *)Con_Read);
         ch = ichar;
         Con_Read->io_Data = (APTR)&ichar;
         Con_Read->io_Command = CMD_READ;
         Con_Read->io_Length = 1;
         SendIO((struct IORequest *)Con_Read);
         if(ch == '\033') {
            if(p == cport)ttputs("getdatCON\n");
            else          ttputs("getdatTNC\n");
         }
         if(charf(ch)) {
cmd_exit:
            if(timerbit & getbits) {
               WaitIO((struct IORequest *)&timermsg);
            }
            else {
               if(curmin)cantimer();
            }
            return(true);
         }
      }
      if(timerbit & getbits) {
         WaitIO((struct IORequest *)&timermsg);
         *cp = 0;
         if(!(p->mode & idle))p->mode = timeout;
         return (true);
      }
   }
}
charf(s)
unsigned char s;
{
   register unsigned char ch;
   register char *q,*r;
   register int i;
   PORTS *p;

   p = port;
   ch = s;
   /* Ignore characters with high order bit set...they tie up the TNC */
   if(ch > 0177)return(0);
   *cp = ch;
   if((cp == p->line + linelen - 2) || (ch == '\r')) {
do_lf:
      if(ch == '\r')*cp = '\n';
      *++cp = 0;
      if(p->flags & p_echo)outchar('\n');
      return(1);
   }
   if((ch >= ' ') && (ch <= 0176)) {
      /*
         Do justification for local user.
      */
      if(p->dev == p_console) {
         if(ch == ' ') {
            s_pos = x_pos;
            /* If the space is at exactly the point we would have justified
               the line then fake a CR anyway
            */
            if(x_pos == MAXLEN) {
               ch = '\r';
               *saveword = 0;
               goto do_lf;
            }
         }
         else {
            /* It's not a space ... should we do the justify?
            */
            if(x_pos == MAXLEN) {
               /* This word can't be longer than MAXJUST or we just leave
                  it alone and let them mess up the line
               */
               if((i = (MAXLEN - s_pos)) < MAXJUST) {
                  q = saveword;
                  r = &p->line[s_pos+1];
                  do {
                     *q++ = *r++;
                     ttputs("\b \b");
                  }while(--i);
                  *q = 0;
                  cp = &p->line[s_pos];
                  ch = '\r';
                  goto do_lf;
               }
            }
         }
      }
      x_pos++;
      if (p->flags & p_echo) ttputc(ch);
      cp++;
      return(0);
   }
   switch(ch) {
   default:
      break;
   case 26:    /* Control - Z */
      cp++;
      x_pos++;
      break;
   case '\t':
      if(p->flags & p_echo) ttputc(ch);
      s_pos = x_pos;
      x_pos++;
      cp++;
      break;
   case '\b':
      if(cp > p->line) {
         cp--;
         if(p->flags & p_echo) ttputs("\b \b");
         x_pos--;
         /* If they wipe out a space we've got to find the previous one */
         if(*cp == ' ') {
            r = cp-1;
            while(r > p->line) {
               if(*r == ' ') break;
               r--;
            }
            s_pos = r - p->line;
         }

      }
      break;

   case '\n':
      if(cp is p->line) {
         *cp = '\0';
         return(1);
      }
      break;
   }
#ifdef OLD_CODE
   switch(*cp) {
   default:
      if (p->flags & p_echo) outchar(*cp);
      cp++;
      break;
   case '\b':
      if(cp > p->line) {
         cp--;
         if(p->flags & p_echo)outstr("\b \b");
      }
      break;

   case '\n':
      if(cp is p->line) {
         *cp = '\0';
         return(1);
      }
      break;
   case '\0' :    /* Ignore most ctl char */
   case ctl_c:    /* Ignore most ctl char */
   case ctl_v:    /* Ignore most ctl char */
   case '\033':
      break;
   }
#endif
   return(0);
}

/* replace waitcmd from mbtnc to reduce busy waits */
/* NOT NEEDED in SYSOP */
waitcmd(x)
int x;
{
}



/* Replace the pause routine so that it can be used with TNC as well
   as the console. If routine times out after 2 minutes then it will
   return 'Q'.
   The test for optflags[2] allows the SYSOP to specify whether the system
   will pause after 20 lines of output to the user.
*/
pause()
{

   register char ch,*q;
   ULONG class,waitbits;
   USHORT code;

   if(arexx_flag)return(' ');
   if (port->user->state & u_pause) return ' ';
   /* If remote user has interrupted, then return a 'Q' anyway */
   if(!optflags[2] && ((port->mode != local) || (pt_flag)))return(' ');

   q = &tmpstr[0];
   prtx(pausemsg);
   if(port->mode != local)outchar('\n');
   settimer(120);   /* set timeout timer  */
   waitbits = windbit | timerbit | keybit;
   while(1)  {
      getbits = Wait(waitbits);
      if(windbit & getbits) {
         while(NewMessage =
                     (struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            ReplyMsg((struct Message *)NewMessage);
            if(class == CLOSEWINDOW) {
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  if(timerbit & getbits) {
                     WaitIO((struct IORequest *)&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  SetWindowTitles(mywindow,(UBYTE *)"",(UBYTE *) -1L);
                  done(1);
               }
            }
         }
      }
      if(getbits & keybit) {
         WaitIO((struct IORequest *)Con_Read);
         ch = toupper(ichar);
         Con_Read->io_Data = (APTR)&ichar;
         Con_Read->io_Command = CMD_READ;
         Con_Read->io_Length = 1;
         SendIO((struct IORequest *)Con_Read);
         if(timerbit & getbits) {
            WaitIO((struct IORequest *)&timermsg);
         }
         else {
            cantimer();
         }
         outchar('\n');
         return(ch);
      }
      /* check for timeout */
      if(timerbit & getbits) {
         WaitIO((struct IORequest *)&timermsg);
         return('Q');
      }
   }
}

/* Null routines from the mblog.c file.
   SYSOP does not do any logging whatever (there's also a small fix in
   mbinit.c to turn off all the logging flags).
   It also does no monitoring.
*/
/* Define the length of a heard item. It is easier to do this and have
   mbinit allocate a very small heard list than to have another version
   of mbinit just for one small change.
*/
char *lgfile = 0,*monfile = 0,*hrdfile = 0;
int hrdmax = 1;
char fbb_direction;
/* Dummy fbb forwarding */
char *mbvers,*fbbvers;
fbbfwd()
{
}

/* If an AREXX GO command is received as a command by getdat, instead of
   by do_idle, set an error. It is always an error for this to happen.
   If the process is alive and is not talking to AREXX then it is an error.
   If the process is alive and is talking to AREXX then it's an error to
   send two GO comands to AREXX in one script.

   BUT can't do this here if I have to reply to message right away.
   Will have to look for GO in mbcmd and trap it.
*/
go_arexx()
{
   port->msg = mcant;
}

/* This is called by mbcmd after it has checked
   If we have executed an AREXX message, then reply to the message
   and send an error string if appropriate.
*/
reply_to_rexx()
{
/*
   Can ONLY use this to send an error for the GO command. ALL other commands
   Reply to the message immediately.
   Problem is, I HAVE to reply to the command when it is received. If I wait
   until after the command has executed, then the AREXX script is stuck
   waiting for the Reply so that it can proceed to read the output from the
   command.
*/
return; /* Remove this */
   if(rexxmessage) {
      if(port->msg == mcant)
         ReplyRexxCommand(rexxmessage,0L,0L,(STRPTR) "ERROR");
      else
         ReplyRexxCommand(rexxmessage,0L,0L,0L);
      rexxmessage = 0;
   }
}
