/*
 *	Data functions.
 *
 */

#include "hc.h"

struct CircBuffer
{
	UBYTE Mem[4096];
	UWORD Read;
	UWORD Write;
	UWORD Size;
	UWORD OverRun;
};

void WriteData (struct CircBuffer *, UWORD);
void ServeWaiter (void);
UWORD Truth (UWORD Value, UBYTE *Buffer);

static struct CircBuffer *FileBuffer;
static UBYTE *WriteBuffer[2];
static UWORD FreeBuffer, Modem = 1, ToHC, FromHC;

/*
 *	UWORD OpenData (void)	void CloseData (void)
 *
 *		Alloc and free the memory needed for data manipulation.
 */

UWORD OpenData (void)
{
	FileBuffer = AllocMem (sizeof (struct CircBuffer) * NUM_HC, MEMF_CLEAR);
	if (FileBuffer)
	{
		WriteBuffer[0] = AllocMem (4096, 0L);
		if (WriteBuffer[0])
		{
			WriteBuffer[1] = AllocMem (4096, 0L);
			if (WriteBuffer[1])
			{
				return 1;
			}
		}
	}
DB	else
DB	{
DB		Say ("FileBuffer allocation failed.\n");
DB	}

	CloseData ();
	return 0;
}

void CloseData (void)
{
	if (WriteBuffer[1]) FreeMem (WriteBuffer[1], 4096);
	if (WriteBuffer[0]) FreeMem (WriteBuffer[0], 4096);
	if (FileBuffer) FreeMem (FileBuffer, sizeof (struct CircBuffer) * NUM_HC);

	FileBuffer = NULL;
	WriteBuffer[0] = NULL;
	WriteBuffer[1] = NULL;
}


/*
 *	UWORD PutData (UWORD File, UBYTE *Buffer, UWORD Size)
 *
 *		Put a HC11 address between each byte sent to the serial port.  The
 *		address is a lower nibble containing the id of the sender and a
 *		higher nibble containg the id of the receiver.
 */

UWORD PutData (UWORD File, UBYTE *Buffer, UWORD Size)
{
	UWORD SizeCount;
	UWORD BufferCount;

	if (File == BAUD)
	{
		SetBaud (atoi (Buffer));
		return Size;
	}
	else if (File == FROM)
	{
		FromHC = Truth (FromHC, Buffer);
		return Size;
	}
	else if (File == TO)
	{
		ToHC = Truth (ToHC, Buffer);
		return Size;
	}
	else if (File == MODEM)
	{
		Modem = Truth (Modem, Buffer);
		return Size;
	}
	else if (File > 0 && File <= NUM_HC)
	{

		File = (File << 3) | 0x80;
		SizeCount = Size;
		BufferCount = 0;
	
		while (SizeCount--)
		{
			if (ToHC)
			{
DB				sprintf (dbi.Msg, "Sending file iden byte: %x.\n", File);
DB				Say (dbi.Msg);
				WriteBuffer[FreeBuffer][BufferCount++] = File;
			}

			if (Modem == 1 && *Buffer == 10)
				*Buffer = 13;

			WriteBuffer[FreeBuffer][BufferCount++] = *Buffer++;
			if (BufferCount == 4096)
			{
				WriteSer (WriteBuffer[FreeBuffer], 4096);
				BufferCount = 0;
				FreeBuffer = (FreeBuffer + 1) & 1;
			}
		}

		if (BufferCount)
		{
			WriteSer (WriteBuffer[FreeBuffer], BufferCount);
			FreeBuffer = (FreeBuffer + 1) & 1;
		}

		return Size;
	}
	else
	{
		return 0;
	}
}


/*
 *	UWORD Truth (UWORD Value, UBYTE *Buffer)
 *
 *		Check the truth contained in a string, and return true, false or the
 *		old value.
 */

UWORD Truth (UWORD Value, UBYTE *Buffer)
{
	if (*Buffer == '0')
		return 0;
	else if (*Buffer == '1')
		return 1;
	else
		return Value;
}


/*
 *	UWORD GetData (UWORD File, UBYTE *Buffer, UWORD Size)
 *
 *		Check which file is read and take that much data from the buffers.
 *		They are filled by the serial routines.
 */

UWORD GetData (UWORD File, UBYTE *Buffer, UWORD Size)
{
	struct CircBuffer *b;
	UWORD rcount = Size;

	if (File == 0 || File > NUM_HC)
		return 0;

	/*
	 *	Get the data associated with the file number.
	 */

 	b = &FileBuffer[File - 1];

	/*
	 *	No data -> wait for more by return ing minus one (-1).
	 */

	if (0 == b->Size)
		return -1;

	/*
	 *	Transfer as much as we have.
	 */

	while (rcount > 0 && b->Size > 0)
	{
		*Buffer++ = b->Mem[b->Read++];
		b->Read &= 0x0fff;
		rcount--;
		b->Size--;
	}

	return Size - rcount;
}


/*
 *	UWORD DataSize (UWORD File)
 *
 *		Return the current size of the file, which is the amount of data
 *		pending in the circular buffer.
 */

UWORD DataSize (UWORD File)
{
	switch (File)
	{
		case ROOT:
			return 0;
		case MODEM:
			return Modem;
		case FROM:
			return FromHC;
		case TO:
			return ToHC;
		case BAUD:
			return GetBaud ();
		case HC1:
		case HC2:
		case HC3:
		case HC4:
		case HC5:
		case HC6:
		case LOOP:
			return FileBuffer[File-1].Size;
		default:
			return -1;
	}
}


/*
 *	void ClassData (UBYTE *Buffer, UWORD Size)
 *
 *		This parse a buffer as pairs of bytes.  The first byte contain the
 *		id of the sender in the low nibble and the id of the receiver in
 *		the high nibble.
 *
 *		If the data is ours (id == 0), then we put the data in the second
 *		byte in its buffer, if it's for someone else (id != 0), we resend
 *		it out of the srial port.
 *
 *		The code check for over-run of the circular buffer for each id and
 *		set the head of the read in accordance to the new position of the
 *		write head.
 */

void ClassData (UBYTE *Buffer, UWORD Size)
{
	UWORD i;

	if (FromHC)
	{
		while (Size > 1)
		{
			i = (UWORD)Buffer[0];
			if (i & 0x80)
			{
				i &= 0x3F;
				if (i > 0 && i <= NUM_HC)
				{
DB					Say ("Data decoded from HC11.\n");
					WriteData (&FileBuffer[i - 1], (UWORD)Buffer[1]);
				}
				else if (i & 0x07)
				{
DB					sprintf (dbi.Msg, "Data passing through (i == %x).\n", i);
DB					Say (dbi.Msg);
//					WriteSer (Buffer, 2);
				}
				else
				{
					/*
					 *	Data we sent somehow looped the loop...
					 */
DB					Say ("Data looped around.\n");
					WriteData (&FileBuffer[LOOP-1], (UWORD)Buffer[1]);
				}
				Size -= 2;
				Buffer += 2;
			}
			else
			{
DB				Say ("Trying to synchronize.\n");
				SyncSer ();
				Size -= 1;
				Buffer += 1;
			}
		}
	}
	else
	{
		while (Size--)
		{
			WriteData (FileBuffer, (UWORD)*Buffer);
			Buffer++;
		}
	}

	for (i = 0; i < NUM_HC; i++)
	{
		if (FileBuffer[i].OverRun)
		{
			FileBuffer[i].Read = (FileBuffer[i].Write + 1) & 0x0fff;
			FileBuffer[i].OverRun = 0;
		}
	}

	ServeWaiter ();
}


/*
 *	void WriteData (struct CircBuffer *b, UWORD c)
 *
 *		Put a byte of data in a circular buffer and check for overflow.
 */

void WriteData (struct CircBuffer *b, UWORD c)
{
	if (Modem == 1)
	{
		if (c == 10)
			return;
		else if (c == 13)
			c = 10;
	}
	b->Mem[b->Write++] = c;
	b->Write &= 0x0fff;
	if (b->Write == b->Read)
		b->OverRun = 1;
	b->Size = (b->Size + 1) & 0x0FFF;
}


/*
 *	void ServeWaiter (void)
 *
 *		Serve packets that are waiting for data.  The REALLY nice thing here
 *		is that since the packet was waiting, no data was in the buffer, and
 *		since this function is called by ClassData, which is itself always
 *		called when there's been 4096 bytes of data (maximum) arrived, we're
 *		sure that we lose no data...
 */

void ServeWaiter (void)
{
	struct DosPacket *Packet;
	struct Node *Node, *Next;

DB	Say ("Serving waiters.\n");

	for (Node = ReadList.lh_Head; Node->ln_Succ != NULL; Node = Next)
	{
		Next = Node->ln_Succ;
		Packet = (struct DosPacket *)Node->ln_Name;
		if (Packet != NULL)
		{
			if (FileBuffer[Packet->dp_Arg1 - 1].Size > 0)
			{
DB				Say ("Replying to waiter.\n");
				Remove (Node);
				Packet->dp_Res1 = GetData (Packet->dp_Arg1, (UBYTE *)Packet->dp_Arg2, Packet->dp_Arg3);
				ReturnPacket (Packet, Process);
			}
		}
	}
}
