/*	Buffered Asynchronous File I/O Routines (Read Only) */

#include "asyncio.h"

/* #define __NO_PRAGMAS
#include <functions20.h>
#include <xfunctions.h> */

struct AsyncFile *OpenAsync(char *name,long mode,long bufsize)
{	struct AsyncFile	*afh = NULL;
	struct FileHandle	*fh;

	if (fh = (struct FileHandle *)Open(name,mode))
	{
	/*	Printf("%s\n",name); */
		if (afh = AllocMem(sizeof(*afh) + 2 * bufsize,MEMF_CLEAR+MEMF_PUBLIC))
		{
			afh->Handle = (struct FileHandle *)((long)fh << 2);
			afh->Port.mp_Node.ln_Type = NT_MSGPORT;
			afh->Port.mp_Flags = PA_SIGNAL;
			afh->Port.mp_SigTask = FindTask(0);
			afh->Port.mp_SigBit = 15;
			afh->Buffer[0] = (char *)(afh + 1);
			afh->Buffer[1] = afh->Buffer[0] + bufsize;
			afh->Size = bufsize;
			NewList(&afh->Port.mp_MsgList);
		}
		else Close((AFILE)fh);
	}

	return afh;
}

WaitAsync(struct AsyncFile *afh)
{
	if (afh->Pending)	/* is there any I/O pending? */
	{
		while ( !GetMsg(&afh->Port) )	/* has I/O returned yet? */
		{
			WaitPort(&afh->Port);
		}
		afh->Pending = 0;	/* I/O no longer pending */
	}

	return afh->Packet.sp_Pkt.dp_Res1; /* zero if first call */
}

long CloseAsync(struct AsyncFile *afh)
{	long	result = 0;

	if (afh->Pending)
	{
		WaitAsync(afh);
		result = afh->Packet.sp_Pkt.dp_Res1;
	}

	Close((AFILE)((long)afh->Handle >> 2));
	FreeMem(afh,sizeof(*afh) + 2 * afh->Size);

	return result;
}

/*	_DoAsync() can also be used to perform other actions, even if they
	require more arguments (i.e., dp_Arg4, etc.). Just set those up before
	the call. (InitPacket does not fiddle with the dp_Arg's at all.) */

_DoAsync(struct AsyncFile *afh,long arg2,long arg3,long action)
{	
	InitPacket(action,&afh->Packet,&afh->Port);
	afh->Packet.sp_Pkt.dp_Arg1 = afh->Handle->fh_Arg1;
	afh->Packet.sp_Pkt.dp_Arg2 = arg2;
	afh->Packet.sp_Pkt.dp_Arg3 = arg3;
	PutMsg((void *)afh->Handle->fh_Type,(void *)&afh->Packet);
	afh->Pending = 1;
}

long ReadAsync(struct AsyncFile *afh, char *buf,long bytes)
{	long	result = 0,
			copied = 0,
			tocopy;

	while (TRUE)
	{
		if (tocopy = afh->Avail)
		{
			if (bytes < afh->Avail)
			{	
				if (buf)
				{
					if (bytes == 1) *buf = *(char *)afh->CurPos;
					else if (bytes == 4) *(LONG *)buf = *(LONG *)afh->CurPos;
					else CopyMem(afh->CurPos,buf,bytes);
					buf += bytes;
				}

				afh->CurPos += bytes;
				afh->Avail -= bytes;
				copied += bytes;
				bytes = 0;
			}
			else
			{
				if (buf)
				{
					if (buf != afh->CurPos)
					{	if (tocopy == 1) *buf = *(char *)afh->CurPos;
						else if (tocopy == 4) *(LONG *)buf = *(LONG *)afh->CurPos;
						else CopyMem(afh->CurPos,buf,tocopy);
					}
					buf += afh->Avail;
				}

				bytes -= tocopy;
				copied += tocopy;
				afh->Avail = 0;
			}

		}

		if (bytes == 0 || (afh->Avail == 0 && afh->End == 1)) return copied;

		if (afh->SentOnce == 0)
		{
			/* if (buf != NULL && bytes > afh->Size) afh->LastSent = buf;
			else */ afh->LastSent = afh->Buffer[afh->Which];
			_DoAsync(afh,(long)afh->LastSent,afh->Size,ACTION_READ);
			afh->Which ^= 1;
			afh->SentOnce = 1;
		}

		result = WaitAsync(afh);
		afh->CurPos = afh->LastSent;

		if (result == afh->Size)
		{
			/* if (buf != NULL && bytes > afh->Size) afh->LastSent = buf;
			else */ afh->LastSent = afh->Buffer[afh->Which];
			_DoAsync(afh,(long)afh->LastSent,afh->Size,ACTION_READ);
		}
		else afh->End = 1;

		if (result < 0) return result;

		afh->Avail = result;
		afh->Which ^= 1;
	}
}
