#include "bbs.h"
#include <clib/timer_protos.h>

extern int  zresume;
char sbuff[133];

BPTR fh;
static struct FileHandle *Fileh;	/* The file handle                  */
static struct StandardPacket *Packet;	/* MUST be longword aligned         */
static struct MsgPort *RPort;		/* A standard EXEC message port     */
static char Pending;			/* operation 'in progress' flag     */

static struct IOExtSer *ReadReq, *WriteReq;

static struct timerequest *TimeReq;
struct Library *TimerBase; /* for lattice , should be struct Device * */
static struct timeval ms,me;

#define RBUFSIZE 1024L
static UBYTE rs_in[RBUFSIZE+128];
static int rlength;
static int count;
char *sendbuff;
int sendlen;
char *zdiskbuff;
char *zbuffer;
long zbufsize;
long zbaud;
int doublebuffer;
int EndSynC;
ULONG STpos;

/* Prototypes for functions defined in crctab.c */
short update_crc16(int cp,int crc);
long update_crc32(int cp,long crc);
int startzmodem(char *name,int flg);

int proto(struct IOExtSer *RR, struct IOExtSer *WR, struct timerequest *TR,
			 char *BF, long BS, long BD, char *flname, int send, int db)
{
 int tempvar;
	ReadReq = RR;
	WriteReq = WR;
	TimeReq = TR;

 TimerBase = (struct Library *)TimeReq->tr_node.io_Device;
	zdiskbuff = zbuffer = BF;
	zbufsize = BS;
	zbaud = BD;
	Online_NFiles=0;
	DONF=0;
	FreeDFlag=0;

	rlength = count = sendlen = 0;
	sendbuff = 0L;			/* serial init vals */

	doublebuffer = 1;

	if(send==1)
 		tempvar=startzmodem(flname,1);        /* do zmodem send */
	 else	tempvar=startzmodem(flname,0);       /* do zmodem receive */
	return(tempvar);
}

void sendbyte(int ch)
{
 unsigned char s;

 if(sendbuff) {
		sendbuff[sendlen++] = ch & 0xFF;
		return;
	}

 s = ch & 0xFF;
 WriteReq->IOSer.io_Length  = 1L;
 WriteReq->IOSer.io_Data    = (APTR)&s;
 WriteReq->IOSer.io_Command = CMD_WRITE;
 DoIO(WriteReq);
}

void sendbuffer(char *buf, int len)
{
	WriteReq->IOSer.io_Length = (long)len;
	WriteReq->IOSer.io_Data = (APTR)buf;
	WriteReq->IOSer.io_Command = CMD_WRITE;
	SendIO(WriteReq);
}

void waitwrite(void)
{
	WaitIO(WriteReq);
}

void purgewrite(void)
{
	AbortIO(WriteReq);
	WaitIO(WriteReq);
}

static void stimer(ULONG seconds, ULONG micros)
{
   TimeReq->tr_node.io_Command = TR_ADDREQUEST;
   TimeReq->tr_time.tv_secs    = seconds;
   TimeReq->tr_time.tv_micro   = micros;
   SendIO(TimeReq);
}

int readbyte(void)
{
   register int len;

   if(rlength)
   {
      --rlength;
      return((int)rs_in[count++]);
   }

   ReadReq->IOSer.io_Command = SDCMD_QUERY;
   DoIO(ReadReq);
   ReadReq->IOSer.io_Command = CMD_READ;
   ReadReq->IOSer.io_Data = (APTR)&rs_in[0];
   len = ReadReq->IOSer.io_Actual;
   if(len)
   {
      if(len > RBUFSIZE) len = RBUFSIZE;
      ReadReq->IOSer.io_Length = (long)len;
      DoIO(ReadReq);
      rlength = --len; count = 0;
      return((int)rs_in[count++]);
   }

   ReadReq->IOSer.io_Length = 1L;
   ReadReq->IOSer.io_Flags = IOF_QUICK;
   BeginIO(ReadReq);

   if(ReadReq->IOSer.io_Flags & IOF_QUICK)
   {
      return((int)rs_in[0]);
   }
   else
   if(CheckIO(ReadReq))     /* if IO request has completed */
    {
       WaitIO(ReadReq);     /* remove the message from the queue */
       return((int)rs_in[0]);
    }

   stimer(10L,0L);      /* start timeout */

/* wait for a TX window event, the timer to timeout or for a receive character */
for(;;)
	{
   Wait((1L << ReadReq->IOSer.io_Message.mn_ReplyPort->mp_SigBit) |
	(1L << TimeReq->tr_node.io_Message.mn_ReplyPort->mp_SigBit));

   if(CheckIO(ReadReq))     /* if IO request has completed */
    {
       AbortIO(TimeReq);
       WaitIO(TimeReq);
       WaitIO(ReadReq);     /* remove the message from the queue */
       return((int)rs_in[0]);
    }
/* see if the timer timed out */
  if(CheckIO(TimeReq))          /* was timeout */
   {
     AbortIO(ReadReq);
     WaitIO(ReadReq);
     return TIMEOUT;
   }
   if(!CheckCarrier()) {
               if(!CheckIO(TimeReq))
			AbortIO(TimeReq);

			WaitIO(TimeReq);
               if(!CheckIO(ReadReq))
AbortIO(ReadReq);
     WaitIO(ReadReq);
			return(TIMEOUT);
			}
	}                     /* wait for Timeout or character */
}

void purgeline(void)
{
   rlength = count = 0;

   ReadReq->IOSer.io_Command = CMD_CLEAR;
   DoIO(ReadReq);                       /* flush receive buffer  */
}

int checkline(void)
{
   if(rlength) return(rlength);

   ReadReq->IOSer.io_Command = SDCMD_QUERY;
   DoIO(ReadReq);
   return((int)(ReadReq->IOSer.io_Actual));
}

void itp(long size,UBYTE r)
{
ULONG csecs,csize;

 TimeReq->tr_node.io_Command = TR_GETSYSTIME;
 DoIO(TimeReq);
 if(size==0)	{
	    ms.tv_secs = TimeReq->tr_time.tv_secs;
	    ms.tv_micro = TimeReq->tr_time.tv_micro;
	} else {
	    me.tv_secs = TimeReq->tr_time.tv_secs;
	    me.tv_micro = TimeReq->tr_time.tv_micro;
	    SubTime(&me,&ms);
	    me.tv_secs += 1L;
	    if(Cmds->AcLvl[LVL_VARYING_LINK_RATE]==0&&r==0)
 		me.tv_secs+=2L;

 	csecs=me.tv_secs*100+(me.tv_micro/10000);
	    csize=size*100;
	    sprintf(sbuff, "[10;12H%lu Efficency %lu%%  ",(csize/csecs),(((csize/csecs)*100L)/(zbaud/10L)));
	    ZmodemStatPrint(sbuff);
	}
}

void throughput(long size)
{
 ULONG csecs,csize;
 long TEFF,TCPS,TTM,TTS;

 if (size==0)      itp(0L,0);
 else  {
	    TimeReq->tr_node.io_Command = TR_GETSYSTIME;
	    DoIO(TimeReq);
	    me.tv_secs = TimeReq->tr_time.tv_secs;
	    me.tv_micro = TimeReq->tr_time.tv_micro;
	    SubTime(&me,&ms);
	    me.tv_secs += 1L;
	    csecs=me.tv_secs*100+(me.tv_micro/10000);
	    csize=size*100;

     TEFF=(((csize/csecs)*100L)/(zbaud/10L));
     TCPS=(csize/csecs);
     TTEFF+=TEFF;
     TTCPS+=TCPS;
     TTM=(me.tv_secs/60L);
     TTS=(me.tv_secs%60L);
     ZModemInfo.LastTime=me.tv_secs;
     TTTM+=me.tv_secs;
     TBT+=TMPBT;
     if(!FreeDFlag)	DTBT+=TMPBT;

     sprintf(sbuff, "[9;12H%lu Mins %lu Secs",TTM,TTS);
     ZmodemStatPrint(sbuff);
     ZmodemStatPrint("[5;12H             ");
     sprintf(sbuff, "[10;12H%lu Efficency %lu%%  ", TCPS,TEFF);
     ZmodemStatPrint(sbuff);
     ZModemInfo.Cps=TCPS;
     ZModemInfo.Eff=TEFF;
   }
}

void InitAsync(void)
{
	EndSynC=1;
	Fileh = (struct FileHandle *)BADDR(fh);
	Packet = (struct StandardPacket *)AllocMem((long)sizeof(struct StandardPacket),
	      MEMF_CLEAR|MEMF_PUBLIC);
	RPort = CreatePort(0L, 0L);
	Pending = 0;
}

void WriteAsync(char *buf, long bytes)
{
	if(Pending) {
	     WaitPort(RPort);       /* wait for previous packet to return */
	     GetMsg(RPort);         /* remove packet from port            */
	     Pending = 0;
 }
	if(buf) {
	     /*
	      * DOS requires a pointer to the dos packet be placed
	      * in the ln)Name field of the message, and a pointer
	      * to the message be placed in the dp_Link field of the
	      * dos packet.
	      */
	     Packet->sp_Msg.mn_Node.ln_Name = (char *)&(Packet->sp_Pkt);
	     Packet->sp_Pkt.dp_Link  = &Packet->sp_Msg;
	     Packet->sp_Pkt.dp_Port  = RPort;
	     Packet->sp_Pkt.dp_Type  = ACTION_WRITE;
	     Packet->sp_Pkt.dp_Arg1  = Fileh->fh_Arg1;
	     Packet->sp_Pkt.dp_Arg2  = (long)buf;
	     Packet->sp_Pkt.dp_Arg3  = bytes;
	     PutMsg(Fileh->fh_Type,(struct Message *)Packet);
	     Pending = 1;
	}
}

void EndAsync(void)
{
	if(Pending)	{
	     WaitPort(RPort);
	     GetMsg(RPort);
	}
	DeletePort(RPort);
	FreeMem(Packet, (long)sizeof(struct StandardPacket));
	EndSynC=0;
}

BPTR Aopen(char *name, long mode)
{
 fh = Open(name, mode);
 InitAsync();
 return(fh);
}

void Aclose(void)
{
 if(EndSynC) {
      EndAsync();
      Close(fh);
      fh = 0L;
 }
}

