/*
 *  MBAMIGA.C - 19th Aug 1988 - System dependent functions.
 * modified version of mbibm.c
 * Pete Hardie VE5VA
 *
 */

#include "mb.h"
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/text.h>
#include <devices/serial.h>
#include <ctype.h>
#include <time.h>
extern struct Window *mywindow;
extern struct IntuiMessage *NewMessage, *GetMsg();
short debug;
char tmpstr[100];
extern struct IOExtSer *Read_Request;
extern unsigned char rs_in[2];
extern struct IOExtSer *Write_Request;
extern unsigned char rs_out[2];
extern char gotstream;
extern char *tcmds[];
/*
{
  "cono on\n",
  "cono of\n",
  "m on\n",
  "m of\n"
};
*/
char passchr = 0,hfstream = 0,vhfstream = 0;
extern unsigned char tncchar,gotchar;
char l_date[7], l_time[5];
long time();
char pt_flag = 0;
extern short tapr_flag;
short done_flag = 1;
/*
 *  Set the current I/O device.
 */

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

/*
 *  Return character from the current port.
 */

char inchar()
{
   register char ch;
   extern char rdchar();
   if (port->dev is p_console) {
      ch = rdchar(0);             /*char from console */;
   }
   else {
      ch = rdchar(1);             /*char from serial port*/;
   }

   if (ch is del) ch = '\b';

   if (!(port->ec)) return ch;

   if (ch isnt ctl_g) {
      if(ch != '\n')ttputc(ch);
     /*output ch here and then ... */
      if (ch is '\r') ttputc('\n');/*output '\n' here*/;
   }
   return ch;
}

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

instat()
{
  register int st;

   if(port->dev is p_console) {
      /* check for char at console */
      if(conchar())return(1);
      return(0);
   }
   if(port->dev is p_tnc) {
      /* check for char at serial port */
      if(tncchar)return(1);
      if(CheckIO(Read_Request)) {
            WaitIO(Read_Request);
            tncchar = rs_in[0];
            if(tapr_flag) tncchar &= 0177;
            BeginIO(Read_Request);
            return(1);
      }
   }
   return(0) ;
}

outi(ch)
char ch;
{
   schar(ch);
}

/*
 *  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' */;
      if(ch != passchr)ttputc(ch);
/*
      if (pt_flag) {
         bdos(5,ch,0);
         if(ch is '\r') bdos(5,'\n',0);
      }
*/
   }

/*
 *  If the port is the console, nothing more to do.
 */

   if (port->dev is p_console) return;

/*
 *  Stuff this byte out the port.
 */

   outi(ch);
   if (ch is '\r')
      if (port->flags & p_lf) outi('\n');
}

outstr(cp)
char *cp;
{
   while(*cp) outchar(*cp++);
}

/* bdos() is called from mbtnc ... this saves changing mbtnc */
bdos(p,ch,n)
short p,n;
char ch;
{
   outi(ch);
}


short wait_timeout = 0;
char cmdstring[100];
FILE *fd;
inittnc(infile)
char *infile;
{
   register char *cp;
   register char c;
   PORTS *oldport;
   oldport = port;
   wait_timeout = 0;
   ioport(findport('A'));
if(debug)ttputs("inittnc\n");
   /* Force a ^C X  to the KAM or other TNC. If KAM is in mode other
      than packet this will force it back.
      If already in packet mode this will be interpreted as an XFLOW
      request ... which won't hurt anything
   */
   outstr("\003X\n\n");
   waitcmd(10);
   /* have to turn off monitoring and conok so that nothing interferes
      with the startup
   */
   alloff();
   outstr("echo off\n"); /* echo MUST be off too */
   waitcmd(10);
   /* If wait_timeout is too big then the TNC probably isn't there
      so let's get outta here
   */
   if(wait_timeout >= 3) {
      fclose(port->fl);
      donebeep();
      endtimer();
      cleanser();
      freefcb();
      closeterm();
      printf("Too many timeouts from TNC - possible reasons are:\n");
      printf("1. TNC and AMIGA are not using same baudrate\n");
      printf("2. The TNC is not plugged into the AMIGA\n");
      printf("3. The TNC PARITY is not set to NONE (or SPACE)\n");
      exit(1);
   }
if(debug)ttputs("inittnc1\n");
   /* get the thing into sync with what comes in from the TNC which
      may have stored up a lot of monitored stuff
      Do this by sending out a command and watch for the reply.
   */
   outstr("txd\n");
   do {
      if((c = rdchar(1)) != 'T') {
         ttputc(c);
         continue;
      }
      ttputc('T');
      if((c = rdchar(1)) != 'X') {
         ttputc(c);
         continue;
      }
      ttputc('X');
      if((c = rdchar(1)) != 'D') {
         ttputc(c);
         continue;
      }
      ttputc('D');
      break;
   }while(1);
   waitcmd(0);
   if(!passchr) {
      outstr("pass\n");
      while((c = toupper(rdchar(1))) != 'P') {
         ttputc(c);
         /* If the reply contains '?' then there's no pass command */
         if(c == '?') {
            ttputs("NO PASS COMMAND\n\n");
            goto nopass;
         }
      }
      ttputc(c);
      while((c = toupper(rdchar(1))) != '$')ttputc(c);
      ttputc(c);
      ttputc(c = rdchar(1));
      passchr = hex(c) << 4;
      ttputc(c = rdchar(1));
      passchr |= hex(c);
   }
   waitcmd(0);
   /* Now find out what the stream switching character(s) is(are) */
   /* but forget it if there is no pass char */
   if(passchr && !hfstream && !vhfstream) {    /* if not done before */
      outstr("streamsw\n");
      while((c = toupper(rdchar(1))) != 'W') {
         ttputc(c);
         if(c == '?') {
            /* something's gone wrong. If there is a passchar then there
               must also be a streamswitch. If not then clear passchr and
               announce the problem
            */
            passchr = 0;
            ttputs("Error from STREAMSW command - streamswitching off\n");
            goto nopass;
         }
      }
      ttputc(c);
      while((c = toupper(rdchar(1))) != '$')ttputc(c);
      ttputc(c);
      ttputc(c = rdchar(1));
      vhfstream = hex(c) << 4;
      ttputc(c = rdchar(1));
      vhfstream |= hex(c);
      /* IF it is a KAM there are two streamswitching chars to read */
      if((c = rdchar(1)) == '/') {
         ttputc(c);
         hfstream = vhfstream;
         ttputc(rdchar(1));         /* do the $ sign */
         ttputc(c = rdchar(1));
         vhfstream = hex(c) << 4;
         ttputc(c = rdchar(1));
         vhfstream |= hex(c);
      }
      else ttputc(c);
   }
   waitcmd(0);
   sprintf(cmdstring,"\nPASS = $%02x, streamswitch VHF = ",passchr);
   ttputs(cmdstring);
   if((vhfstream > ' ') && (vhfstream < 0177))
      sprintf(cmdstring,"%c",vhfstream);
   else
      sprintf(cmdstring,"$%02x",vhfstream);
   ttputs(cmdstring);
   if(hfstream) {
      if((hfstream > ' ') && (hfstream < 0177))
         sprintf(cmdstring," , HF = %c",hfstream);
      else
         sprintf(cmdstring," , HF = $%02x",hfstream);
      ttputs(cmdstring);
   }
   ttputc('\n');
nopass:
   if((fd = fopen(infile,"r")) == NULL) {
      ttputs("can't open ");
      ttputs(infile);
      ttputs("\n");
      return;
   }
   while(fgets(cmdstring,99,fd) != NULL) {
      /* Ignore blank lines and comments */
      if((cmdstring[0] == 0) || (cmdstring[0] == 012)
                             || (cmdstring[0] == '#'))continue;
      if(cmdstring[0] == '!') {
         remnl(&cmdstring[0]);
         system(&cmdstring[1]);
         continue;
      }
      outstr(cmdstring);
      waitcmd(0);

   }
   fclose(fd);
   ioport(oldport);
}
amatch(s1,s2)
char *s1,*s2;
{
   register char *p,*q;
   p = s1;
   q = s2;
   while(*q && (toupper(*p) == toupper(*q))) {
      p++;
      q++;
   }
   if(*q == 0)return(1);
   return(0);
}
hex(c)
char c;
{
   char k;
   k = c;
   if((c >= '0') && (c <= '9'))return(c - '0');
   k = toupper(c);
   if((k >= 'A') && (c <= 'F'))return(c - 'A' + 10);
   return(0);
}
flipdebug()
{
   debug ^= 1;
   if(debug) {
      ttputs("debug now ON\n");
   }
   else {
      ttputs("debug now OFF\n");
   }
}
extern long waitbits,windbit,readbit,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 long Wait();
extern short cursecs;
do_idle()
{
   register char ch,*cp;
   register PORTS *p;
   PORTS *devcon,*devtnc;
   int curmin;
   ULONG class;
   USHORT code;
   devcon = devtnc = NULL;
   cursor_off();
   for(p=porthd; p != NULL; p = p->next) {
      if(p->id == 'A')devtnc = p;
      if(p->id == 'L')devcon = p;
      p->ec = p->ecmon;
   }
   if(devtnc == NULL) {
      ttputs("error - can't find port 'A'\n");
      done(1);
   }
   if(devcon == NULL) {
      ttputs("error - can't find port 'L'\n");
      done(1);
   }
   ioport(devtnc);
   allon();
   cp = devtnc->line;
   /* figure out time to next forward and set timer */
   curtim();
   curmin = 60 - cursecs;
   settimer(curmin);
   while(1)  {
      waitbits = windbit | readbit | timerbit;

      getbits = Wait(waitbits);
      if(windbit & getbits) {
         while(NewMessage = GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            switch( class ) {
            case CLOSEWINDOW:
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  if(timerbit & getbits) {
                     WaitIO(&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  SetWindowTitles(mywindow,"",-1L);
                  done(1);
               }
               break;

            case RAWKEY:
            /* toasc also maps the Fn keys into the range 0320 - 0331 */
               ch = toasc(code); /* change it to ascii */
               ReplyMsg(NewMessage);
               if(ch == wchar) {
                  ioport(devcon);
                  cursor_on();
                  /* could also have a timerbit or a readbit set here
                     and must handle them before exit
                  */
                  if(timerbit & getbits) {
                     WaitIO(&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  if(readbit & getbits) {
                     if(CheckIO(Read_Request)) {
                        WaitIO(Read_Request);
                        /* throwing away the serial port char is probably
                           not a good idea!
                        */
                        BeginIO(Read_Request);
                     }
                  }
                  return;
               }
               if(ch == '\033') {
                  ttputs("do_idle\n");
               }
               break;
            default:
               ReplyMsg( NewMessage );
            }   /* end of switch (class) */
         }
      }
      if(readbit & getbits) {
         /* save a line and if it is a CONNECT then return */
         if(CheckIO(Read_Request)) {
            WaitIO(Read_Request);
            ch = rs_in[0];
            if(tapr_flag)ch &= 0177;
            BeginIO(Read_Request);
            *cp = ch;   /* autolf is off so don't need to look for '\n' */
            /* This handles the stream characters. If it sees a doubled
               streamchar then it passes one through as data. Otherwise
               it soaks both up and does not pass either as data.
            */
            if(gotstream) {
               if(ch == gotstream) {
                  gotstream = 0;
               }
               else {
                  gotstream = 0;
                  ttputc(ch);
                  continue;
               }
            }
            else {
               if(ch && ((ch == vhfstream) || (ch == hfstream))) {
                  gotstream = ch;
                  ttputc(ch);
                  continue;
               }
            }
            if((cp++ == devtnc->line + linelen -2) || (ch == '\r')) {
               if(ch == '\r')cp--;
               *cp++ = '\n';
               *cp = 0;
               ttputs(devtnc->line);
               ioport(devtnc);
               p->flags setbit p_give;
               if(iscon(devtnc->line)) {
                  /* readbit has been handled but timerbit could be set */
                  if(timerbit & getbits) {
                     WaitIO(&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  return;
               }
               monitor();
               cp = devtnc->line;
            }
         }
      }
      /* 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(&timermsg);
         /* call the forwarding routine */
         curtim();
         curmin = 10 * (l_time[2] - '0') + (l_time[3] - '0');
         afwd(curmin);
         settimer(60);
      }
   }
}
/* replace getdat from mbutil to reduce busy waits */
/* IMPLEMENTATION BUG. In the original getdat it busy-idles and
   if the port is transparent and DCD goes off while the port is
   not idle, then it forces the mode to discon and the returns(true).
   This code can only do this properly IF it can wait for DCD to go
   off. I haven't even tried to implement this yet and not sure if it
   can be done properly. I assume that there's a way to Wait() for a
   DCD event but haven't checked it out
*/
char *cp;
getdat()
{

   register char ch;
   register PORTS *p;
   int curmin;
   ULONG class;
   USHORT code;
   p = port;
   cp = p->line;

   /* Is a timeout timer required? */
   curmin = 0;
   if(p->flags & p_dotmr) {
      if((p->mode & idle) && (p->dev == p_tnc))
         curmin = 2;
      else
         curmin = p->ctime;
   }
   if(curmin)settimer(curmin);   /* set timeout timer if required */
   p->flags setbit p_dotmr;

/* WARNING - EXTREMELY ugly code ahead */
   if(p == cport) {
      waitbits = windbit | timerbit;
      if(gotchar) {
         ch = gotchar;
         gotchar = 0;
         goto centry;                  /* UGGHHHH */
      }
   }
   else {
      waitbits = windbit | readbit | timerbit;
      if(tncchar) {
         ch = tncchar;
         tncchar = 0;
         goto tentry;                   /* ARRRGGHHHH */
      }
   }
   while(1)  {
      getbits = Wait(waitbits);
      if(windbit & getbits) {
         while(NewMessage = GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            switch( class ) {
            case CLOSEWINDOW:
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  if(timerbit & getbits) {
                     WaitIO(&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  SetWindowTitles(mywindow,"",-1L);
                  done(1);
               }
               break;
            case RAWKEY:
            /* toasc also maps the Fn keys into the range 0320 - 0331 */
               ch = toasc(code); /* change it to ascii */
               ReplyMsg(NewMessage);
               if(ch == '\033') {
                  if(p == cport)ttputs("getdatCON\n");
                  else          ttputs("getdatTNC\n");
               }
centry:
               if((p != cport) && (ch == achar)) {
                  p->mode = forced;
                  if(timerbit & getbits) {
                     WaitIO(&timermsg);
                  }
                  else {
                     if(curmin)cantimer();
                  }
                  return (true);
               }
               if((p != cport) && (ch == tchar)) {
                  p->flags setbit p_opreq;
                  if(timerbit & getbits) {
                     WaitIO(&timermsg);
                  }
                  else {
                     if(curmin)cantimer();
                  }
                  return (true);
               }
               if((p == cport) && charf(ch)) {
                  if(timerbit & getbits) {
                     WaitIO(&timermsg);
                  }
                  else {
                     if(curmin)cantimer();
                  }
                  return(true);
               }
               break;
            default:
               ReplyMsg( NewMessage );
            }   /* end of switch (class) */
         }
      }
      if(readbit & getbits) {
         if(CheckIO(Read_Request)) {
            WaitIO(Read_Request);
            ch = rs_in[0];
            if(tapr_flag)ch &= 0177;
            BeginIO(Read_Request);
tentry:
            /* stream character processing */
            if(gotstream) {
               if(ch == gotstream) {
                  gotstream = 0;
               }
               else {
                  gotstream = 0;
                  ttputc(ch);
                  continue;
               }
            }
            else {
               if(ch && ((ch == vhfstream) || (ch == hfstream))) {
                  gotstream = ch;
                  ttputc(ch);
                  continue;
               }
            }
            if(charf(ch)) {
               if(timerbit & getbits) {
                  WaitIO(&timermsg);
               }
               else {
                  if(curmin)cantimer();
               }
               if(!(p->flags & p_trans)) {
                  if(isdis(p->line)) {
                     if(!(p->mode & idle))p->mode = discon;
                     return (true);
                  }
                  if(isretry(p->line)) {
                     return (false);
                  }
                  if(isreq(p->line)) {
                     p->flags setbit p_req;
                     pcall(p->rcall,p->line+20);
                     p->flags clrbit p_dotmr;
                     return (false);
                  }
               }
               return (true);
            }
         }
      }
      if(timerbit & getbits) {
         WaitIO(&timermsg);
         *cp = 0;
         if(!(p->mode & idle))p->mode = timeout;
         return (true);
      }
   }
}
charf(s)
char s;
{
   register char ch;
   PORTS *p;
   p = port;
   ch = s;
   *cp = ch;
   if((cp == p->line + linelen - 2) || (ch == '\r')) {
      if(ch == '\r')*cp = '\n';
      *++cp = 0;
      if(p->dev == p_tnc)ttputs(p->line);
      if(p->flags & p_echo)ttputc('\n');
      return(1);
   }
   switch(*cp) {
   default:
      if (p->flags & p_echo) ttputc(*cp);
      cp++;
      break;
   case '\b':
      if(cp > p->line) {
         cp--;
         if(p->flags & p_echo) ttputs("\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 */
      break;
   }
   return(0);
}

/* replace waitcmd from mbtnc to reduce busy waits */
char *waitfor = "cmd:";
waitcmd(x)
int x;
{

   register char ch;
   register int i;
   ULONG class;
   USHORT code;
   if(port->dev != p_tnc)return;
   if(s_flag & s_dv) i = 5;
   else i = 2;
   if(x) i = x;
   settimer(i);   /* set timeout timer  */
   i = 0;
   waitbits = windbit | readbit | timerbit;
   while(1)  {
      getbits = Wait(waitbits);
      if(windbit & getbits) {
         while(NewMessage = GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            switch( class ) {
            case CLOSEWINDOW:
               /* say a fond farewell, don't ask fer sure? */
               if(done_flag) {
                  SetWindowTitles(mywindow,"",-1L);
                  if(timerbit & getbits) {
                     WaitIO(&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  done(1);
               }
               break;
            case RAWKEY:    /* keyboard */
            /* toasc also maps the Fn keys into the range 0320 - 0331 */
               ch = toasc(code); /* change it to ascii */
               ReplyMsg(NewMessage);
               if(ch == '\033') { /* ESC key forces termination */
                  ttputs("ESC\n");
                  if(timerbit & getbits) {
                     WaitIO(&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  return;
               }
               break;

            default:
               ReplyMsg( NewMessage );
            }   /* end of switch (class) */
         }
      }
      if(readbit & getbits) {
         if(CheckIO(Read_Request)) {
            WaitIO(Read_Request);
            ch = rs_in[0];
            if(tapr_flag)ch &= 0177;
            BeginIO(Read_Request);
            if(gotstream) {
               if(ch == gotstream) {
                  gotstream = 0;
               }
               else {
                  gotstream = 0;
                  ttputc(ch);
                  continue;
               }
            }
            else {
               if(ch && ((ch == vhfstream) || (ch == hfstream))) {
                  gotstream = ch;
                  ttputc(ch);
                  continue;
               }
            }
            if(ch == '\r')ch = '\n';
            ttputc(ch);
            if(tolower(ch) == waitfor[i]) {
               i++;
               if(i == 4) {
                  if(timerbit & getbits) {
                     WaitIO(&timermsg);
                  }
                  else {
                     cantimer();
                  }
                  return;
               }
            }
            else {
               i = 0;
            }
         }
      }
      /* check for timeout */
      if(timerbit & getbits) {
         WaitIO(&timermsg);
         wait_timeout++;
         return;
      }
   }
}


char comnd1,comnd2;
getcomd()
{
   port->opt1 = comnd1;
   port->opt2 = comnd2;
}
putcomd(c1,c2)
char c1,c2;
{
   comnd1 = c1;
   comnd2 = c2;
}
term(s)
PORTS *s;
{
   register char ch,*cp;
   register PORTS *m;
   ULONG class;
   USHORT code;
   byte ec;

   m = cport;
   m->flags clrbit p_give;
   ec = s->ec;
   s->ec = true;
   ioport(m);
   while(1)  {
      waitbits = windbit | readbit ;

      getbits = Wait(waitbits);
      if(windbit & getbits) {
         while(NewMessage = GetMsg(mywindow->UserPort)) {
            class = NewMessage->Class;
            code = NewMessage->Code;
            switch( class ) {
            case RAWKEY:
            /* toasc also maps the Fn keys into the range 0320 - 0331 */
               ch = toasc(code); /* change it to ascii */
               ReplyMsg(NewMessage);
               if(ch == 0)break;
               if(ch == rchar) {
                  m->flags setbit p_give;
                  s->ec = ec;
                  if(readbit & getbits) {
                     if(CheckIO(Read_Request)) {
                        WaitIO(Read_Request);
                        BeginIO(Read_Request);
                     }
                  }
                  return;
               }
               if(ch == '\r')ch = '\n';
               ttputc(ch);
               if(ch == '\n')schar('\r');
               else schar(ch);
               if(m->fl isnt NULL) if(ch isnt cpmeof)putc(ch,m->fl);
               break;
            default:
               ReplyMsg( NewMessage );
            }   /* end of switch (class) */
         }
      }
      if(readbit & getbits) {
         if(CheckIO(Read_Request)) {
            WaitIO(Read_Request);
            ch = rs_in[0];
            if(tapr_flag)ch &= 0177;
            BeginIO(Read_Request);
            /* This handles the stream characters. If it sees a doubled
               streamchar then it passes one through as data. Otherwise
               it soaks both up and does not pass either as data.
            */
            if(gotstream) {
               if(ch == gotstream) {
                  gotstream = 0;
               }
               else {
                  gotstream = 0;
                  ttputc(ch);
                  continue;
               }
            }
            else {
               if(ch && ((ch == vhfstream) || (ch == hfstream))) {
                  gotstream = ch;
                  ttputc(ch);
                  continue;
               }
            }
            if(ch == '\r')ch = '\n';
            if(m->fl isnt NULL) if(ch isnt cpmeof)putc(ch,m->fl);
            ttputc(ch);
         }
      }
   }
}
