/*
**	termClip.c
**
**	Clipboard support routines
**
**	Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

STATIC struct IFFHandle	*ClipHandle;
STATIC STRPTR		 ClipBuffer,
			 ClipIndex;
STATIC LONG		 ClipSize,
			 ClipLength;

	/* ClipServer():
	 *
	 *	Quiet background process which saves data to the clipboard
	 *	or fills a buffer with fresh clipboard data.
	 */

VOID __saveds
ClipServer()
{
	struct IOClipReq __aligned ClipRequest;

		/* Clear the request. */

	memset(&ClipRequest,0,sizeof(struct IOClipReq));

		/* Open the clipboard.device, we will not use the
		 * IORequest to do any IO operations on it, but
		 * rather to keep clipboard.device available
		 * when needed.
		 */

	if(!OpenDevice("clipboard.device",PRIMARY_CLIP,(struct IORequest *)&ClipRequest,NULL))
	{
		if(ClipPort = CreateMsgPort())
		{
			ULONG		 Mask;
			struct Message	*ClipMsg;
			UBYTE		*Buffer;
			BYTE		 Terminated = FALSE;

			Signal(ThisProcess,SIG_HANDSHAKE);

			while(!Terminated)
			{
				Mask = Wait(SIG_KILL | PORTMASK(ClipPort));

				if(Mask & SIG_KILL)
					Terminated = TRUE;

				if(Mask & PORTMASK(ClipPort))
				{
					while(ClipMsg = GetMsg(ClipPort))
					{
						Buffer = ClipMsg -> mn_Node . ln_Name;

						if(Buffer[0])
							SaveClip(Buffer,strlen(Buffer));
						else
						{
							WORD Len = LoadClip(Buffer,256);

							Buffer[Len] = 0;
						}

						ReplyMsg(ClipMsg);
					}
				}
			}

			DeleteMsgPort(ClipPort);
		}

		CloseDevice((struct IORequest *)&ClipRequest);
	}

	Forbid();

	ClipProcess = NULL;

	Signal(ThisProcess,SIG_HANDSHAKE);
}

	/* CloseClip():
	 *
	 *	Close clipboard handle, stop reading.
	 */

VOID
CloseClip()
{
	if(ClipHandle)
	{
		CloseIFF(ClipHandle);

		if(ClipHandle -> iff_Stream)
			CloseClipboard((struct ClipboardHandle *)ClipHandle -> iff_Stream);

		FreeIFF(ClipHandle);

		ClipHandle = NULL;
	}

	if(ClipBuffer)
	{
		FreeVec(ClipBuffer);

		ClipBuffer = NULL;
	}
}

	/* GetClip(STRPTR Buffer,WORD Len,BYTE Filter):
	 *
	 *	Read text data from clipboard and put it into the supplied buffer.
	 */

WORD
GetClip(STRPTR Buffer,WORD Len,BYTE Filter)
{
	WORD BytesPut = 0;

		/* Is the read buffer already exhausted? */

	if(!ClipLength)
	{
			/* Is there still any data to read? */

		if(ClipSize)
		{
			WORD Size = MIN(ClipSize,1024);

				/* Try to read the data and return failure if necessary. */

			if(ReadChunkBytes(ClipHandle,ClipBuffer,Size) != Size)
				return(-1);
			else
			{
				ClipSize	-= Size;
				ClipLength	 = Size;
				ClipIndex	 = ClipBuffer;
			}
		}
		else
		{
				/* We just parsed a single chunk, now go on and
				 * look for another one.
				 */

			if(!ParseIFF(ClipHandle,IFFPARSE_SCAN))
			{
				struct ContextNode *ContextNode = CurrentChunk(ClipHandle);

				if(ContextNode -> cn_Type == ID_FTXT)
				{
					WORD Size;

					ClipSize	= ContextNode -> cn_Size;
					Size		= MIN(ClipSize,1024);

					if(ReadChunkBytes(ClipHandle,ClipBuffer,Size) != Size)
						return(-1);
					else
					{
						ClipSize	-= Size;
						ClipLength	 = Size;
						ClipIndex	 = ClipBuffer;
					}
				}
				else
					return(-1);
			}
			else
				return(-1);
		}
	}

		/* The following loop processes the contents of
		 * the clipboard buffer read. Special characters
		 * such as LF and CR will be converted according
		 * to the current settings if enabled. No bytes
		 * will be lost, though.
		 */

	while(ClipLength && BytesPut < Len)
	{
		if(*ClipIndex == '\n')
		{
			if(Filter)
			{
				*Buffer++ = *ClipIndex++;

				BytesPut++;

				ClipLength--;
			}
			else
			{
				switch(Config -> TerminalConfig -> SendLF)
				{
					case LF_IGNORE:

						ClipIndex++;
						ClipLength--;

						break;

					case LF_ASLFCR:

						if(BytesPut + 2 <= Len)
						{
							*Buffer++ = '\n';
							*Buffer++ = '\r';

							BytesPut += 2;

							ClipLength--;
							ClipIndex++;
						}
						else
							return(BytesPut);

						break;

					case LF_ASLF:

						*Buffer++ = *ClipIndex++;

						BytesPut++;

						ClipLength--;

						break;
				}
			}
		}
		else
		{
			if(*ClipIndex == '\r')
			{
				if(Filter)
				{
					ClipIndex++;
					ClipLength--;
				}
				else
				{
					switch(Config -> TerminalConfig -> SendCR)
					{
						case CR_IGNORE:

							ClipIndex++;
							ClipLength--;

							break;

						case CR_ASCRLF:

							if(BytesPut + 2 <= Len)
							{
								*Buffer++ = '\r';
								*Buffer++ = '\n';

								BytesPut += 2;

								ClipLength--;
								ClipIndex++;
							}
							else
								return(BytesPut);

							break;

						case CR_ASCR:

							*Buffer++ = *ClipIndex++;

							BytesPut++;

							ClipLength--;

							break;
					}
				}
			}
			else
			{
				register UBYTE c = *ClipIndex++;

				ClipLength--;

				if(!Filter || IsPrintable[c])
				{
					*Buffer++ = c;

					BytesPut++;
				}
			}
		}
	}

	return(BytesPut);
}

	/* OpenClip():
	 *
	 *	Open the clipboard for sequential reading.
	 */

BYTE
OpenClip(LONG Unit)
{
	BYTE Error;

	CloseClip();

	if(ClipBuffer = (STRPTR)AllocVec(1024,MEMF_ANY))
	{
		if(ClipHandle = AllocIFF())
		{
			if(ClipHandle -> iff_Stream = (ULONG)OpenClipboard(Unit))
			{
				InitIFFasClip(ClipHandle);

				if(!OpenIFF(ClipHandle,IFFF_READ))
				{
					if(!StopChunk(ClipHandle,ID_FTXT,ID_CHRS))
					{
						if(!ParseIFF(ClipHandle,IFFPARSE_SCAN))
						{
							struct ContextNode *ContextNode = CurrentChunk(ClipHandle);

							if(ContextNode -> cn_Type == ID_FTXT)
							{
								ClipSize	= ContextNode -> cn_Size;
								ClipLength	= 0;

								return(CLIPERR_NONE);
							}
							else
								Error = CLIPERR_NOTEXT;
						}
						else
							Error = CLIPERR_NOTEXT;
					}
					else
						Error = CLIPERR_IFF;
				}
				else
					Error = CLIPERR_OPEN;
			}
			else
				Error = CLIPERR_OPEN;
		}
		else
			Error = CLIPERR_MEM;
	}
	else
		Error = CLIPERR_MEM;

	CloseClip();

	return(Error);
}

	/* GetClipContents(LONG Unit,APTR *Buffer,LONG *Size):
	 *
	 *	Merge text contents of the clipboard into a single string.
	 */

BYTE
GetClipContents(LONG Unit,APTR *Buffer,LONG *Size)
{
	struct IFFHandle	*Handle;
	LONG			 Bytes = 0;
	APTR			 Store = NULL;

	if(Handle = AllocIFF())
	{
		if(Handle -> iff_Stream = (ULONG)OpenClipboard(Unit))
		{
			InitIFFasClip(Handle);

			if(!OpenIFF(Handle,IFFF_READ))
			{
				if(!StopChunk(Handle,ID_FTXT,ID_CHRS))
				{
					struct ContextNode *Node;

					while(!ParseIFF(Handle,IFFPARSE_SCAN))
					{
						Node = CurrentChunk(Handle);

						if(Node -> cn_Type == ID_FTXT)
							Bytes += Node -> cn_Size;
					}
				}

				CloseIFF(Handle);
			}

			if(Bytes)
			{
				if(!OpenIFF(Handle,IFFF_READ))
				{
					if(!StopChunk(Handle,ID_FTXT,ID_CHRS))
					{
						if(Store = AllocVec(Bytes,MEMF_ANY))
						{
							STRPTR			 Index		= Store;
							LONG			 BytesRead	= 0;
							struct ContextNode	*Node;

							while(!ParseIFF(Handle,IFFPARSE_SCAN) && BytesRead < Bytes)
							{
								Node = CurrentChunk(Handle);

								if(Node -> cn_Type == ID_FTXT)
								{
									LONG Count = Node -> cn_Size;

									if(BytesRead + Count > Bytes)
										Count = Bytes - BytesRead;

									if(Count > 0)
									{
										if((Count = ReadChunkBytes(Handle,Index,Count)) > 0)
										{
											Index		+= Count;
											BytesRead	+= Count;
										}
									}
								}
							}

							Bytes = BytesRead;
						}
					}

					CloseIFF(Handle);
				}
			}

			CloseClipboard((struct ClipboardHandle *)Handle -> iff_Stream);
		}

		FreeIFF(Handle);
	}

	if(Store && !Bytes)
	{
		FreeVec(Store);

		Store = NULL;
	}

	*Buffer	= Store;
	*Size	= Bytes;

	return((BYTE)(Store != NULL));
}

	/* AddClip(STRPTR Buffer,LONG Size):
	 *
	 *	Merge previous clipboard contents with new text,
	 *	then store the new string in the clipboard.
	 */

BYTE
AddClip(STRPTR Buffer,LONG Size)
{
	LONG	Bytes;
	APTR	Store;

	if(GetClipContents(Config -> ClipConfig -> ClipboardUnit,&Store,&Bytes))
	{
		struct IFFHandle	*Handle;
		BYTE			 Success = FALSE;

		if(Handle = AllocIFF())
		{
			if(Handle -> iff_Stream = (ULONG)OpenClipboard(Config -> ClipConfig -> ClipboardUnit))
			{
				InitIFFasClip(Handle);

				if(!OpenIFF(Handle,IFFF_WRITE))
				{
					if(!PushChunk(Handle,ID_FTXT,ID_FORM,IFFSIZE_UNKNOWN))
					{
						if(!PushChunk(Handle,0,ID_CHRS,IFFSIZE_UNKNOWN))
						{
							if(WriteChunkBytes(Handle,Store,Bytes) == Bytes)
							{
								if(WriteChunkBytes(Handle,Buffer,Size) == Size)
								{
									if(!PopChunk(Handle))
										Success = TRUE;
								}
							}
						}
					}

					if(Success)
					{
						if(PopChunk(Handle))
							Success = FALSE;
					}

					CloseIFF(Handle);
				}

				CloseClipboard((struct ClipboardHandle *)Handle -> iff_Stream);
			}

			FreeIFF(Handle);
		}

		FreeVec(Store);

		return(Success);
	}
	else
		return(SaveClip(Buffer,Size));
}

	/* SaveClip(STRPTR Buffer,LONG Size):
	 *
	 *	Send a given text buffer to the clipboard.
	 */

BYTE
SaveClip(STRPTR Buffer,LONG Size)
{
	BYTE Success = FALSE;

	if(Size > 0)
	{
		struct IFFHandle *Handle;

		if(Handle = AllocIFF())
		{
			if(Handle -> iff_Stream = (ULONG)OpenClipboard(Config -> ClipConfig -> ClipboardUnit))
			{
				InitIFFasClip(Handle);

				if(!OpenIFF(Handle,IFFF_WRITE))
				{
					if(!PushChunk(Handle,ID_FTXT,ID_FORM,IFFSIZE_UNKNOWN))
					{
						if(!PushChunk(Handle,0,ID_CHRS,IFFSIZE_UNKNOWN))
						{
							if(WriteChunkBytes(Handle,Buffer,Size) == Size)
							{
								if(!PopChunk(Handle))
									Success = TRUE;
							}
						}
					}

					if(Success)
					{
						if(PopChunk(Handle))
							Success = FALSE;
					}

					CloseIFF(Handle);
				}

				CloseClipboard((struct ClipboardHandle *)Handle -> iff_Stream);
			}

			FreeIFF(Handle);
		}
	}

	return(Success);
}

	/* LoadClip(STRPTR Buffer,LONG Size):
	 *
	 *	Put the contents of the clipboard into a given
	 *	buffer. Note that only the first FTXT chunk will
	 *	be read. Since this code will only be called by
	 *	the clipboard server process which serves the
	 *	string gadget editing hook, this will hopefully
	 *	not be fatal. If you want more data to be read,
	 *	including multiple FTXT chunks, use the OpenClip(),
	 *	GetClip(), CloseClip() combo above.
	 */

LONG
LoadClip(STRPTR Buffer,LONG Size)
{
	struct IFFHandle	*Handle;
	LONG			 Bytes = 0;

	if(Handle = AllocIFF())
	{
		if(Handle -> iff_Stream = (ULONG)OpenClipboard(Config -> ClipConfig -> ClipboardUnit))
		{
			InitIFFasClip(Handle);

			if(!OpenIFF(Handle,IFFF_READ))
			{
				if(!StopChunk(Handle,ID_FTXT,ID_CHRS))
				{
					if(!ParseIFF(Handle,IFFPARSE_SCAN))
					{
						struct ContextNode *ContextNode = CurrentChunk(Handle);

						if(ContextNode -> cn_Type == ID_FTXT)
						{
							if(Size > ContextNode -> cn_Size)
								Size = ContextNode -> cn_Size;

							if(ReadChunkRecords(Handle,Buffer,Size,1))
								Bytes = Size;
						}
					}
				}

				CloseIFF(Handle);
			}

			CloseClipboard((struct ClipboardHandle *)Handle -> iff_Stream);
		}

		FreeIFF(Handle);
	}

	return(Bytes);
}
