/*
**                    ---  filexfer.c ---
**
**  Demonstration program for the Personal Protocol Library.
**
**  See FILEXFER.H for configuration parameters.
**
**  Do NOT select YMODEM-G when using a null modem cable unless you are
**  certain that RTS & CTS are reversed ( this is often not true ).
**
*/
#include <stdio.h>
#include "ppl4c.h"
#include "ascii.h"
#include "pcl4c.h"
#include "filexfer.h"

#define FALSE 0
#define TRUE !FALSE
#define NO_ERROR 0

/*** protocol data ***/

static int BatchFlag = FALSE;
static int OneKflag = FALSE;
static char NCGchar = NAK;

/*** global variables ***/

static char PriRxBuf[2048];       /* Primary 2K receive buffer */
static char SecRxBuf[2048];       /* Secondary 2K receive buffer */
static int ThisPacket;            /* packet # */
static char FileSpec[64];         /* file specification */
static char EmptyFile[1] = "\0";
static char *BaudRate[10] =  {"300","600","1200","2400","4800","9600",
                            "19200","38400","57600","115200"};

static int LastPacket[2] = {-1,-1};
static char Protocol[2] = {'X', 'X'};
static int IsIdle[2] = {TRUE,TRUE};
static int ThePort[2] = {PRI_PORT,SEC_PORT};
static int TheBaud[2] = {PRI_BAUD,SEC_BAUD};
/*static char *ThePrefix[2] = {"Primary","Secondary"};*/
static char *TheBuffer[2] = {PriRxBuf,SecRxBuf};
static char *ModelText[4] = {"Small","Compact","Medium","Large"};

char KeyRead();
void UpdateStuff();
void SetProtocol();
void SayProtocol();

void main()
{int i;
 int Code;
 char ch;
 int C;
 int Version;
 void ErrorCheck();
 char *Ptr;
 /* display some info */
 puts("\n*** FILEXFER 1.0:  Aug 10, 1992 ***");
 Version = SioInfo('V');
 printf("Library = %d.%d  ",Version/16,Version%16);
 printf("Memory Model = %s\n",ModelText[3&SioInfo('M')] );
 /*printf("Preparing %d channels\n",NBR_OF_CHANNELS);*/
 for(C=0;C<NBR_OF_CHANNELS;C++)
   {printf("Channel %d: ",C);
    printf("Port = COM%d, ",1+ThePort[C]);
    printf("Rate = %s\n",BaudRate[TheBaud[C]]);
    ErrorCheck( SioRxBuf(ThePort[C],TheBuffer[C],Size2048) );
    ErrorCheck( SioReset(ThePort[C],TheBaud[C]) );
    /* set channel 0 to primary port, channel 1 to secondary port */
    xyInit(C,ThePort[C]);
#if RTS_CTS_CONTROL
    SioFlow(ThePort[C],3*ONE_SEC);
    printf("COM%d: Flow Control enabled. CTS = ",ThePort[C]);
    if(SioCTS(ThePort[C])) puts("ON");
    else puts("OFF");
#endif
    /* Set FIFO level if have 16550 UART */
    if( SioFIFO(ThePort[C],LEVEL_14) ) printf("COM%d: INS16550 detected",1+ThePort[C]);
    /* clear PCL4C receive buffer */
    ErrorCheck( SioRxFlush(ThePort[C]) );
    /* see FILEXFER.H for definition of AT_COMMAND_SET */
#if AT_COMMAND_SET
    /* wait for Modem to say its ready */
    printf("Waiting for Modem DSR on COM%d.",1+ThePort[C]);
    while( !SioDSR(ThePort[C]) )
      {
       if(SioKeyPress()||SioBrkKey()) MyExit(0,"Aborted by user");
       putchar('.');
       SioDelay(ONE_SEC);
      }
    putchar('\n');
    /* initialize (Hayes compatible) modem */
    SendTo(ThePort[C],"!AT!!~");
    SendTo(ThePort[C],"!AT E1 S7=60 S11=60 V1 X1 Q0 S0=1!");
    if(WaitFor(ThePort[C],"OK")) printf("\nCOM%d MODEM READY\n",ThePort[C]);
    else printf("\nWARNING: Expected OK not received\n");
#endif
   } /* end for(C) */
 /* begin main loop */
 printf("\n");
 while(1)
   {
    /* get user command */
    printf("\nQ)uit P)rotocol S)end R)eceive A)bort C)omm Status:");
    ch = KeyRead(); printf("\n");
    /* update status for each channel */
    for(C=0;C<NBR_OF_CHANNELS;C++) IsIdle[C] = xyDriver(C);
    switch(toupper(ch))
      {case 'Q':  /* QUIT */
         for(C=0;C<NBR_OF_CHANNELS;C++) SioDone(ThePort[C]);
         puts("Done");
         exit(0);
       case 'P':  /* SET PROTOCOL */
         SetProtocol();
         break;
       case 'A':  /* ABORT TRANSFER */
         C = GetChannel();
         if(C!=ESC) xyAbort(C);
         break;
       case 'S':  /* SEND FILE */
         if((C=GetChannel())==ESC) break;
         if(IsIdle[C])
           {if(GetString("Enter Filename:",FileSpec)==0) break;
            if(!FindFirst(FileSpec))
              {printf("No such file(s) '%s'\n",FileSpec);
               break;
              }
            xyStartTx(C,FileSpec,OneKflag,BatchFlag);
           }
         else printf("C%d: Channel is busy\n",C);
         break;
       case 'R':  /* RECEIVE FILE */
         if((C=GetChannel())==ESC) break;
         if(IsIdle[C])
           {if(BatchFlag)
              {/* YMODEM provides filename */
               xyStartRx(C,EmptyFile,NCGchar,BatchFlag);
              }
            else
              {/* need filename 1st */
               GetString("Enter Filename:",FileSpec);
               xyStartRx(C,FileSpec,NCGchar,BatchFlag);
              }
           }
         else printf("C%d: Channel is busy\n",C);
         break;
       case 'C':  /* COMM STATUS */
         for(C=0;C<NBR_OF_CHANNELS;C++)
           {/* get channel status */
            Code = xyDriver(C);
            if(Code<0)
              {/* comm error returned */
               printf("C%d: SIO Error %d",C,Code);
               SioError(Code);
              }
            else
              {/* no comm error */
               if(IsIdle[C])
                  {/* channel is idle */
                   Code = xyGetErrorCode(C);
                   if(Code==NO_ERROR)
                     {/* no error on idle channel */
                      printf("C%d: ",C);
                      SayProtocol(Protocol[C]);
                      printf(", channel is idle.\n");
                     }
                   else
                     {/* report error condition */
                      printf("C%d: '%c' Error '",C,Protocol[C]);
                      xySayError(Code);
                      printf("' occured in state '");
                      xySayState(xyGetErrorState(C));
                      printf("'\n");
                     }
                  }
               else
                  {/* channel is NOT idle */
                   printf("C%d: ",C);
                   SayProtocol(Protocol[C]);
                   printf(", State='");
                   xySayState(xyGetState(C));
                   i = xyGetPacket(C);
                   if(i>=0) printf("', Packet=%d",i);
                   Ptr = xyGetFilename(C);
                   if(strlen(Ptr)>0) printf(", File='%s'\n",Ptr);
                   else printf("\n");
                  }
              }
           } /* end for(C) */
         break;
      } /* end switch */
   } /* end while */
} /* end main */

char KeyRead()
{/* all keyboard input MUST be called thru KeyRead */
 while(!SioKeyPress()) UpdateStuff();
 return( SioKeyRead() );
} /* end KeyRead */

void UpdateStuff()
{int C;
 /* read each channel */
 for(C=0;C<NBR_OF_CHANNELS;C++) IsIdle[C] = xyDriver(C);
} /* end UpdateStuff */

void ErrorCheck(Code)
int Code;
{int C;
 /* trap PCL error codes */
 if(Code<0)
     {SioError(Code);
      for(C=0;C<NBR_OF_CHANNELS;C++) SioDone(ThePort[C]);
      exit(1);
     }
} /* end ErrorCheck */

void SetProtocol()
{char Answer;
 int Flag;   /* change protocol if TRUE */
 int C;
 if((C=GetChannel())==ESC) return;
 if(!IsIdle[C])
   {printf("WARNING: Cannot change protocol until channel is idle\n");
    return;
   }
#if RTS_CTS_CONTROL
 printf("X: XMODEM   C: XMODEM-CRC   1: XMODEM-1K   Y: YMODEM   G: YMODEM-G\n");
 printf("Enter X,C,1,Y, or G: ");
#else
 printf("X: XMODEM   C: XMODEM-CRC   1: XMODEM-1K   Y: YMODEM\n");
 printf("Enter X,C,1, or Y: ");
#endif
 Answer = KeyRead();
 SioCrtWrite(Answer);
 Flag = TRUE;
 switch( toupper(Answer) )
   {case 'X':
      BatchFlag = FALSE;
      OneKflag = FALSE;
      NCGchar = NAK;
      break;
    case 'C':
      BatchFlag = FALSE;
      OneKflag = FALSE;
      NCGchar = 'C';
      break;
    case '1':
      BatchFlag = FALSE;
      OneKflag = TRUE;
      NCGchar = 'C';
      break;
    case 'Y':
      BatchFlag = TRUE;
      OneKflag = TRUE;
      NCGchar = 'C';
      break;
#if RTS_CTS_CONTROL
    case 'G':
      BatchFlag = TRUE;
      OneKflag = TRUE;
      NCGchar = 'G';
      break;
#endif
    default:
      Flag = FALSE;
      printf("ERROR: No such protocol");
   }
 if(Flag)
   {Protocol[C] = toupper(Answer);
   }
} /* end SetProtocol */

int GetChannel()
{char ch;
 while(1)
   {printf("Enter channel [0 or 1] or ESC:");
    ch = KeyRead();  SioCrtWrite(ch);
    printf("\n");
#if 0
    SioCrtWrite(CR);
    SioCrtWrite(LF);
#endif
    if(ch==ESC) return(ESC);
    if((ch>='0')&&(ch<'0'+NBR_OF_CHANNELS)) return(ch-'0');
    puts("ERROR: Must enter 0 or 1 [or ESC]");
   }
} /* end GetChannel */

int GetString(Prompt,String)
char *Prompt;
char *String;
{char ch;
 int i = 0;
 printf("%s",Prompt);
 while(1)
   {ch = KeyRead();
    switch(ch)
      {case CR:
         SioCrtWrite(LF);
         SioCrtWrite(CR);
         String[i++] = '\0';
         return(i);
       case ESC:
         String[0] = '\0';
         return(0);
       case BS:
         if(i<=0) continue;
         SioCrtWrite(BS);
         SioCrtWrite(' ');
         SioCrtWrite(BS);
         i--;
         break;
      default:
         SioCrtWrite(ch);
         String[i++] = ch;
         break;
     } /* end switch */
   } /* end while */
} /* end GetString */

void SayProtocol(P)
char P;
{
 printf("Protocol=");
 switch(P)
  {case 'X':
     printf("XMODEM");
     break;
   case 'C':
     printf("XMODEM-CRC");
     break;
   case '1':
     printf("XMODEM-1K");
     break;
   case 'Y':
     printf("YMODEM");
     break;
   case 'G':
     printf("YMODEM-G");
     break;
   default:
     printf("unknown.");
     break;
  } /* end switch */
} /* end SayProtocol */
