#include "ffr.h"

#define BUFFER_SIZE 32768L
#define FIRST_BLOCK 512L

#define mySetIoErr(fh,e) ((struct Process *)fh->fh_ReplyPort.mp_SigTask)->pr_Result2=(e)

#define NEWLIST(l) ((l)->lh_Head = (struct Node *)&(l)->lh_Tail, \
                    /*(l)->lh_Tail = NULL,*/ \
                    (l)->lh_TailPred = (struct Node *)&(l)->lh_Head)

static VOID BeginRead(struct FFRHandle *fh,LONG bytes)
{
//  struct ExecBase *SysBase = *(struct ExecBase **)4L;
  struct StandardPacket *sp;

  fh->fh_Flag = -1; sp = &fh->fh_Pkt;

  sp->sp_Msg.mn_Node.ln_Name = (char *)&sp->sp_Pkt;
  sp->sp_Msg.mn_ReplyPort    = &fh->fh_ReplyPort;
  sp->sp_Msg.mn_Length       = sizeof(struct StandardPacket);

  sp->sp_Pkt.dp_Link         = &sp->sp_Msg;
  sp->sp_Pkt.dp_Port         = &fh->fh_ReplyPort;

  sp->sp_Pkt.dp_Type         = ACTION_READ;
  sp->sp_Pkt.dp_Arg1         = fh->fh_Handle->fh_Arg1;
  sp->sp_Pkt.dp_Arg2         = (LONG)fh->fh_Pre;
  sp->sp_Pkt.dp_Arg3         = bytes;

  PutMsg(fh->fh_Handle->fh_Type,&sp->sp_Msg);
}

static LONG EndRead(struct FFRHandle *fh)
{
//  struct ExecBase *SysBase = *(struct ExecBase **)4L;
  struct StandardPacket *sp = &fh->fh_Pkt;
  LONG res1;

  (VOID) WaitPort(&fh->fh_ReplyPort);
  (VOID) GetMsg(&fh->fh_ReplyPort);

  fh->fh_Flag   = FALSE;

  if ((res1=sp->sp_Pkt.dp_Res1) < 0)
    { mySetIoErr(fh,sp->sp_Pkt.dp_Res2); return FALSE; }

  fh->fh_Ptr    = fh->fh_Pre;
  fh->fh_Pre    = fh->fh_Buffer;
  fh->fh_Buffer = fh->fh_Ptr;
  fh->fh_Left   = res1;

  if (res1 == sp->sp_Pkt.dp_Arg3)
    BeginRead(fh,BUFFER_SIZE);

  return TRUE;
}

/*
LONG FFRGetC(struct FFRHandle *fh)
{
  mySetIoErr(fh,0);

  if (fh->fh_Left || (fh->fh_Flag && EndRead(fh) && fh->fh_Left))
    { fh->fh_Left--; return *fh->fh_Ptr++; };

  return -1L;
}
*/

LONG FFRRead(struct FFRHandle *fh,APTR buffer,LONG bytes)
{
//  struct ExecBase *SysBase = *(struct ExecBase **)4L;
  UBYTE *ptr;
  LONG left,size;

  if (bytes <= fh->fh_Left)
  {
    CopyMem(fh->fh_Ptr,buffer,bytes);
    fh->fh_Ptr += bytes; fh->fh_Left -= bytes;

    return bytes;
  }

  left = bytes; ptr = buffer;

  if (left > 0)
    do {
      size = MIN(fh->fh_Left,left);

      if (size != 0)
      {
        CopyMem(fh->fh_Ptr,ptr,size);
        fh->fh_Ptr += size; fh->fh_Left -= size;
        ptr += size; left -= size;
      }

      if (left)
        if (!fh->fh_Flag)
          break;
      else if (!EndRead(fh))
             return -1L;
    } while(left > 0);

  return bytes-left;
}

VOID CloseFFR(struct FFRHandle *fh)
{
//  struct ExecBase *SysBase = *(struct ExecBase **)4L;

  if (fh->fh_Flag != FALSE)
    { (VOID) WaitPort(&fh->fh_ReplyPort); (VOID) GetMsg(&fh->fh_ReplyPort); }

  FreeVec(fh->fh_Pre);
  FreeVec(fh->fh_Buffer);
  FreeSignal(fh->fh_ReplyPort.mp_SigBit);
  FreeVec(fh);
}

struct FFRHandle *OpenFFR(BPTR handle,LONG preRead)
{
  if (handle != NULL)
  {
//    struct ExecBase *SysBase = *(struct ExecBase **)4L;
    struct FFRHandle *fh;

    if ((fh=AllocVec(sizeof(struct FFRHandle),MEMF_PUBLIC|MEMF_CLEAR)) != NULL)
    {
      fh->fh_Handle = (struct FileHandle *)BADDR(handle);

      fh->fh_ReplyPort.mp_Node.ln_Type = NT_MSGPORT;
      if ((BYTE)(fh->fh_ReplyPort.mp_SigBit=AllocSignal(-1))>=0)
      {
        fh->fh_ReplyPort.mp_SigTask = FindTask(NULL);
        NEWLIST(&fh->fh_ReplyPort.mp_MsgList);

        if ((fh->fh_Buffer=AllocVec(BUFFER_SIZE,MEMF_PUBLIC)) != NULL)
        {
          if ((fh->fh_Pre=AllocVec(BUFFER_SIZE,MEMF_PUBLIC)) != NULL)
          {
            BeginRead(fh, FIRST_BLOCK-preRead); return fh;
          }
          FreeVec(fh->fh_Buffer);
        }
        FreeSignal(fh->fh_ReplyPort.mp_SigBit);
      }
      FreeVec(fh);
    }
  }
  return NULL;
}
