#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <devices/serial.h>
#include <devices/timer.h>
#include <libraries/dosextens.h>
#include <proto/dos.h>
#include <stdio.h>
#include <fcntl.h>

#include <dlg/dlgproto.h>

#include <pragmas/dlg.h>

#include "zmodem.h"

#define RBUFSIZE 1024

extern int Online_NFiles,zresume;
extern long TTEFF,TTCPS,TTTM,TMPBT,TBT;


void SetTimer(ULONG , ULONG );
void InitAsync(void);
void EndAsync(void);


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 * */

long misses=0L;
int hits=0;
long hmdelay=0L;
extern UBYTE hitmiss;
static UBYTE rs_in[RBUFSIZE+128];   
static int rlength;
static int count;
char *sendbuff;
int sendlen,ZFlag;
char *zdiskbuff;
char *zbuffer;
long zbufsize,ZFlagSize;
long zbaud;
int doublebuffer;

int proto(struct IOExtSer *RR,struct IOExtSer *WR,struct timerequest *TR,char *BF,
     long BS,long BD,char *flname,int send,int db)

{ReadReq  = RR;
 WriteReq = WR;
 TimeReq  = TR;

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

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

  doublebuffer = db | (zbaud >= 9600);

  if (send==1)
      return(zmodemsend(flname));
    else
      return(zmodemreceive(flname));
}

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((struct IORequest *)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((struct IORequest *)WriteReq);
}

void SendBreak(void)
{
  WriteReq->IOSer.io_Command=SDCMD_BREAK;
  DoIO((struct IORequest *)WriteReq);
}

void waitwrite(void)
{
  WaitIO((struct IORequest *)WriteReq);
}

void purgewrite(void)
{
  AbortIO((struct IORequest *)WriteReq);
  WaitIO((struct IORequest *)WriteReq);
}

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

  SendIO((struct IORequest *)TimeReq);
}

int readbyte(void)
{
  register int len;

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

  misses++;

  if(hits<(int)hitmiss) hmdelay+=500L;
  else if(hmdelay > 1000L) hmdelay-=500L;

  hits=0;

  if(hmdelay> 1000)  {
    TimeReq->tr_node.io_Command = TR_ADDREQUEST;
    TimeReq->tr_time.tv_secs    = 0;
    TimeReq->tr_time.tv_micro   = hmdelay;

    DoIO((struct IORequest *)TimeReq);
  }

  ReadReq->IOSer.io_Command = SDCMD_QUERY;
  DoIO((struct IORequest *)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((struct IORequest *)ReadReq);
    rlength = --len; count = 0;
    return(rs_in[count++]);
  }

  ReadReq->IOSer.io_Length = 1;
  ReadReq->IOSer.io_Flags = IOF_QUICK;
  BeginIO((struct IORequest *)ReadReq);

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

  SetTimer(10,0);      /* start timeout */

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

  if(CheckIO((struct IORequest *)ReadReq)) {    /* if IO request has completed */
    AbortIO((struct IORequest *)TimeReq);
    WaitIO((struct IORequest *)TimeReq);
    WaitIO((struct IORequest *)ReadReq);     /* remove the message from the queue */
    return(rs_in[0]);
  }
/* see if the timer timed out */
  if(CheckIO((struct IORequest *)TimeReq)) {         /* was timeout */
    AbortIO((struct IORequest *)ReadReq);
    WaitIO((struct IORequest *)ReadReq);
    return(TIMEOUT);
  }
  goto wait;                     /* wait for Timeout or character */
}

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

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

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

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

void InitAsync(void)
{
  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));
}

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

void Aclose(void)
{
  if(fh) {
    if(doublebuffer) EndAsync();
    Close(fh);
    fh=NULL;
  }
}
