#include <stdio.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>

#include "pcl4c.h"
#include "term.h"
#include "ascii.h"

#define DEBUG 0
#define FALSE 0
#define TRUE !FALSE

#define MAXTRY 5
#define LIMIT 20

extern GetChar();
extern PutChar();
extern SayError();
extern DisplayLine();

int TxPacket(Port,PacketNbr,PacketSize,Buffer,NCGchar)
int Port;            /* COM port [0..3] */
int PacketNbr;       /* Packet # to send */
int PacketSize;      /* Packet size ( must be 128 or 1024 ) */
char Buffer[];       /* Data buffer */
char NCGchar;        /* NAK, 'C', or 'G' */
{int  i;
 int Code;
 unsigned short CheckSum;
 int Attempt;
 int PacketType;
 char temp[81];
 /* begin */
#if DEBUG
printf("[TxP: COM%d PacketNbr=%d PacketSize=%d NCGchar=%c]",
    1+Port,PacketNbr,PacketSize,NCGchar);
#endif
 /* better be 128 or 1024 packet length */
 if(PacketSize==1024) PacketType = STX;
 else PacketType = SOH;
 PacketNbr &= 0x00ff;
 /* make up to MAXTRY attempts to send this packet */
 for(Attempt=1;Attempt<=MAXTRY;Attempt++)
     {/* send SOH/STX  */
      Code = PutChar(Port,PacketType);
      /* send packet # */
      Code = PutChar(Port,PacketNbr);
      /* send 1's complement of packet */
      Code = PutChar(Port,255-PacketNbr);
      /* send data */
      CheckSum = 0;
      for(i=0;i<PacketSize;i++)
          {Code = PutChar(Port,Buffer[i]);
           if(NCGchar==NAK) CheckSum += Buffer[i];
           else CheckSum = UpdateCRC(CheckSum, Buffer[i]);
          }
      /* send checksum */
      if(NCGchar==NAK)
          {Code = PutChar(Port,CheckSum & 0x00ff );
          }
      else
          {Code = PutChar(Port, (CheckSum>>8) & 0x00ff );
           Code = PutChar(Port, CheckSum & 0x00ff );
          }
      /* no ACK to wait for if 'G' */
      if(NCGchar=='G')
         {if(PacketNbr==0) SioDelay(SHORT_WAIT*ONE_SECOND/2);
          return(TRUE);
         }
      /* wait for receivers ACK */
      Code = GetChar(Port,LONG_WAIT*ONE_SECOND);
      if((char)Code==CAN)
          {DisplayLine("*** Canceled by REMOTE ***",NULL,0);
           return(FALSE);
          }
      if((char)Code==ACK) return(TRUE);
      if((char)Code!=NAK)
          {PacketError(Port,PacketNbr,Attempt,"Out of sync");
           return(FALSE);
          }
      /* Attempt again */
     }/* end -- for(Attempt) */
 /* can't send packet ! */
 SayError(Port,"packet timeout");
 return(FALSE);
} /* end -- TxPacket */

int RxPacket(Port,PacketNbr,PacketSizePtr,Buffer,NCGchar,EOTptr)
int Port;            /* COM port [0..3] */
int PacketNbr;       /* Packet # expected */
int *PacketSizePtr;  /* Pointer to PacketSize received ( 128 or 1024) */
char Buffer[];       /* 1024 byte data buffer */
char NCGchar;        /* NAK, C, or G */
int *EOTptr;         /* Pointer to EOT flag */
{int i;
 int Code;
 int Attempt;
 int RxPacketNbr;
 int RxPacketNbrComp;
 unsigned short CheckSum;
 unsigned short RxCheckSum;
 unsigned short RxCheckSum1, RxCheckSum2;
 /*char PacketType;*/
 char temp[81];
 /* begin */
#if DEBUG
printf("[RxP: COM%d PacketNbr=%d NCGchar=%c EOTflag=%d]",
    1+Port,PacketNbr,NCGchar,*EOTptr);
#endif
 PacketNbr &= 0x00ff;
 for(Attempt=1;Attempt<=MAXTRY;Attempt++)
     {/* wait for SOH / STX */
      Code = GetChar(Port,SHORT_WAIT*ONE_SECOND);
      if(Code==-1)
          {PacketError(Port,PacketNbr,Attempt,"timed out waiting for SOH/STX");
           return(FALSE);
          }
      switch((char)Code)
          {
           case SOH:
               /* 128 byte buffer incoming */
               /*PacketType = SOH;*/
               *PacketSizePtr = 128;
               break;
           case STX:
               /* 1024 byte buffer incoming */
               /*PacketType = STX;*/
               *PacketSizePtr = 1024;
               break;
           case CAN:
                /* sender has canceled ! */
                DisplayLine("*** Canceled by REMOTE ***",NULL,0);
                return(FALSE);
           case EOT:
                /* all packets have been sent */
                Code = PutChar(Port,ACK);
                *EOTptr = TRUE;
                return(TRUE);
           default:
                /* error ! */
                sprintf(temp,"Expecting SOH/STX/EOT/CAN not %xH",(char)Code);
                PacketError(Port,PacketNbr,Attempt,temp);
                return(FALSE);
          }
      /* receive packet # */
      Code = GetChar(Port,SHORT_WAIT*ONE_SECOND);
      if(Code==-1)
        {PacketError(Port,PacketNbr,Attempt,"timed out waiting for packet number");
         return(FALSE);
        }
      RxPacketNbr = 0x00ff & Code;
      /* receive 1's complement */
      Code = GetChar(Port,SHORT_WAIT*ONE_SECOND);
      if(Code==-1)
        {PacketError(Port,PacketNbr,Attempt,"timed out waiting for complement of packet #");
         return(FALSE);
        }
      RxPacketNbrComp = 0x00ff & Code;
      /* verify packet number */
      if(RxPacketNbr+RxPacketNbrComp!=255)
          {PacketError(Port,PacketNbr,Attempt,"Bad packet number");
           return(FALSE);
          }
      /* receive data */
      CheckSum = 0;
      for(i=0;i<*PacketSizePtr;i++)
          {Code = GetChar(Port,LONG_WAIT*ONE_SECOND);
           if(Code==-1)
                   {PacketError(Port,PacketNbr,Attempt,"timed out waiting for data for packet #");
                    return(FALSE);
                   }
           Buffer[i] = Code;
           /* compute CRC or checksum */
           if(NCGchar!=NAK) CheckSum = UpdateCRC(CheckSum,Code);
           else CheckSum = (CheckSum + Code) & 0x00ff;
          }
      /* receive CRC/checksum */
      if(NCGchar!=NAK)
          {/* receive 2 byte CRC */
           Code = GetChar(Port,SHORT_WAIT*ONE_SECOND);
           if(Code==-1)
                   {PacketError(Port,PacketNbr,Attempt,"timed out waiting for 1st CRC byte");
                    return(FALSE);
                   }
           RxCheckSum1 = Code & 0x00ff;
           Code = GetChar(Port,SHORT_WAIT*ONE_SECOND);
           if(Code==-1)
                   {PacketError(Port,PacketNbr,Attempt,"timed out waiting for 2nd CRC byte");
                    return(FALSE);
                   }
           RxCheckSum2 = Code & 0x00ff;
           RxCheckSum = (RxCheckSum1<<8) | RxCheckSum2;
          }
      else
          {/* receive one byte checksum */
           Code = GetChar(Port,SHORT_WAIT*ONE_SECOND);
           if(Code==-1)
                   {PacketError(Port,PacketNbr,Attempt,"timed out waiting for checksum");
                    return(FALSE);
                   }
           RxCheckSum = Code & 0x00ff;
          }
      /* don't send ACK if 'G' */
      if(NCGchar=='G') return(TRUE);
      /* packet # and checksum OK ? */
      if((RxCheckSum==CheckSum)&&(RxPacketNbr==PacketNbr))
          {/* ACK the packet */
           PutChar(Port,ACK);
           return(TRUE);
          } /* end if */
      /* bad packet */
      DisplayLine("Bad Packet",NULL,0);
      Code = PutChar(Port,NAK);
     } /* end -- for(Attempt) */
 /* can't receive packet */
 SayError(Port,"RX packet timeout");
 return(FALSE);
} /* end -- RxPacket */

PacketError(Port,Packet,Attempt,MsgPtr)
int Port;
int Packet;
int Attempt;
char *MsgPtr;
{char temp[81];
 sprintf(temp,"Packet %d : Attempt %d : %s",Packet,Attempt,MsgPtr);
 return( SayError(Port,temp) );
}

int TxStartup(Port,NCGcharPtr)
int Port;
char *NCGcharPtr;
{int i;
 int Code;
#if DEBUG
 printf("### TxStartup");
#endif
 /* clear Rx buffer */
 SioRxFlush(Port);
 /* wait for receivers start up NAK, 'C', or 'G' */
 for(i=1;i<LIMIT;i++)
     {if(SioKeyPress())
          {SayError(Port,"Aborted by user");
           return(FALSE);
          }
      Code = GetChar(Port,SHORT_WAIT*ONE_SECOND);
      if(Code==-1) continue;
      /* received a byte */
      if((char)Code==NAK)
          {*NCGcharPtr = NAK;
#if DEBUG
           printf("(CS) OK\n");
#endif
           return(TRUE);
          }
      if((char)Code=='C')
          {*NCGcharPtr = 'C';
#if DEBUG
           printf("(CRC) OK\n");
#endif
           return(TRUE);
          }
      if((char)Code=='G')
          {*NCGcharPtr = 'G';
#if DEBUG
           printf("(G) OK\n");
#endif
           return(TRUE);
          }
     } /* end -- for(i) */
 /* no response */
 SayError(Port,"No response from receiver");
 return(FALSE);
} /* end -- TxStartup */


int RxStartup(Port,NCGcharPtr)
int Port;
char *NCGcharPtr;
{int i;
 int Code;
#if DEBUG
 printf("### RxStartup");
 printf(" %c[%xH]",*NCGcharPtr,*NCGcharPtr);
#endif
 /* clear Rx buffer */
 SioRxFlush(Port);
 /* Send NAKs, 'C's, or 'G's */
 for(i=1;i<LIMIT;i++)
     {if(SioKeyPress())
          {DisplayLine("*** Canceled by USER ***",NULL,0);
           return(FALSE);
          }
      /* stop attempting CRC/'G' after 1st 4 tries */
      if((*NCGcharPtr!=NAK)&&(i==5)) *NCGcharPtr = NAK;
      /* tell sender that I am ready to receive */
      Code = PutChar(Port,*NCGcharPtr);
      Code = GetChar(Port,SHORT_WAIT*ONE_SECOND);
      if(Code==-1) continue;
      /* no error -- must be incoming byte -- push byte back onto queue ! */
      SioUnGetc(Port,(char)Code);
#if DEBUG
      printf("OK\n");
#endif
      return(TRUE);
     } /* end -- for(i) */
 /* no response */
 SayError(Port,"No response from sender");
 return(FALSE);
} /* end -- RxStartup */

int TxEOT(Port)
int Port;
{int i;
 int Code;
 for(i=0;i<10;i++)
     {Code = PutChar(Port,EOT);
      /* await response */
      Code = GetChar(Port,SHORT_WAIT*ONE_SECOND);
      if((char)Code==ACK) return(TRUE);
     } /* end -- for(i) */
  return(FALSE);
 } /* end -- TxEOT */