/* cbio.c
 * Clipboard routines.
 * Copyright (C) 1990 Commodore-Amiga, Inc.
 * Modified extensively by David N. Junod
 *
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <devices/clipboard.h>
#include <clib/macros.h>
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <string.h>
#include "cf.h"

/* little macro to force a number to even */
#define	FEVEN(a) (((a)&1)?(++(a)):(a))

extern ULONG SysBase, DOSBase;

struct IOClipReq *clipIO;
struct MsgPort *clip_port;

LONG CBOpen (LONG unit)
{
    LONG retval = -1L;

    /* Create a message port for clip messages */
    if (clip_port = CreatePort (0L, 0L))
    {
	/* Create IO request for clipboard communications */
	if (clipIO = (struct IOClipReq *)
	      CreateExtIO (clip_port, sizeof (struct IOClipReq)))
	{
	    /* Open the clipboard device */
	    if ((retval = OpenDevice ("clipboard.device", unit, clipIO, 0)) == 0L)
	    {
		/* Return success */
		return (retval);
	    }

	    /* Free the clipboard IO request */
	    DeleteExtIO (clipIO);
	    clipIO = NULL;
	}

	/* Delete the clipboard message port */
	DeletePort (clip_port);
	clip_port = NULL;
    }

    /* return failure */
    return (retval);
}

VOID CBClose ()
{
    /* Close the clipboard device */
    CloseDevice (clipIO);

    /* Free the clipboard IO request */
    DeleteExtIO (clipIO);
    clipIO = NULL;

    /* Delete the clipboard message port */
    DeletePort (clip_port);
    clip_port = NULL;
}

LONG CBWrite (struct Buffer * buff)
{
    if (buff->b_Type == 0)
	return (CBWriteFTXT ((STRPTR) buff->b_Buff));
    else
	return (CBWriteA (buff->b_Buff, (buff->b_Size - sizeof (struct Buffer))));
}

LONG CBWriteFTXT (STRPTR string)
{
    LONG length, slen = strlen (string);
    BOOL odd = (slen & 1);	/* pad byte flag */
    LONG error = 0L;

    /* Reset the clip id */
    clipIO->io_ClipID = 0;

    length = (odd) ? slen + 1 : slen;
    clipIO->io_Offset = 0;
    error = writeLong ((LONG *) "FORM");/* "FORM" */

    length += 12;
    error = writeLong (&length);	/* #  */
    error = writeLong ((LONG *) "FTXT");/* "FTXT" for example */
    error = writeLong ((LONG *) "CHRS");/* "CHRS" for example */
    error = writeLong (&slen);		/* #  (length of string) */

    clipIO->io_Command = CMD_WRITE;
    clipIO->io_Error = 0;
    clipIO->io_Data = (char *) string;
    clipIO->io_Length = slen;		/* length of string */
    error = (LONG) DoIO (clipIO);	/* text string */

    /* Write the pad byte */
    if (odd)
    {
	clipIO->io_Command = CMD_WRITE;
	clipIO->io_Error = 0;
	clipIO->io_Data = NULL;
	clipIO->io_Length = 1;
	error = (LONG) DoIO (clipIO);
    }

    /* Indicate that we're done reading from the clipboard */
    clipIO->io_Command = CMD_UPDATE;
    clipIO->io_Error = 0;
    error = (LONG) DoIO (clipIO);

    return (error);
}

LONG CBWriteA (STRPTR stream, LONG length)
{
    /* Write the block to the clipboard */
    clipIO->io_Command = CMD_WRITE;
    clipIO->io_Error = 0;
    clipIO->io_Data = stream;
    clipIO->io_Length = length;
    clipIO->io_Offset = 0;
    clipIO->io_ClipID = 0;
    DoIO (clipIO);

    /* Indicate that we're done reading from the clipboard */
    clipIO->io_Command = CMD_UPDATE;
    clipIO->io_Error = 0;
    return ( (LONG) DoIO (clipIO) );
}

VOID CBClear (VOID)
{
    CBWriteA (NULL, 0L);
}

STRPTR PrintID (LONG id, STRPTR buff)
{
    UBYTE *c;
    WORD i, j;

    /* Initialize the character pointer */
    c = (UBYTE *) &id;

    /* Build up the little buffer */
    for (i = 4, j = 0; i--; c++)
	buff[j++] = *c;

    /* Mark the end of the ID string */
    buff[j]=0;

    return (buff);
}

struct Buffer *CBRead (BOOL cook)
{
    struct Buffer *buff = NULL;
    LONG cbuff[5] = {NULL};
    LONG buff_type = 0;
    LONG length = 0L;
    LONG msize;

    /* Read clipboard header */
    clipIO->io_Command = CMD_READ;
    clipIO->io_Error = 0;
    clipIO->io_ClipID = 0;
    clipIO->io_Offset = 0;
    clipIO->io_Data = (char *) cbuff;
    clipIO->io_Length = 20;
    DoIO (clipIO);

    /* Make sure something was read */
    if ((clipIO->io_Actual) > 0L)
    {
	/* Make sure it was IFF FORM */
	if (cbuff[0] == ID_FORM)
	{
	    /* If we're cooking, see if it was an FTXT form */
	    if (cook && (cbuff[2] == ID_FTXT))
	    {
		LONG offset = clipIO->io_Offset;
		LONG maxoff = cbuff[1] + 8L;
		LONG chunk = cbuff[3];
		LONG clen = cbuff[4];

		if (chunk != ID_CHRS)
		{
		    /* Back up to the beginning of the first chunk */
		    offset -= 8L;

		    /* Try to find a CHRS chunk */
		    while ((offset < maxoff) && (chunk != ID_CHRS))
		    {
			/* Seek to the next chunk */
			offset += ( FEVEN(clen) + 8L );

			/* Read in the chunk type and length */
			clipIO->io_Offset = offset;
			DoIO (clipIO);

			/* Get the information we're interested in */
			chunk = cbuff[0];
			clen = cbuff[1];
		    }

		    /* Jump past the chunk type and length */
		    offset += 8L;
		}

		/* See if we can handle it */
		if (offset < maxoff)
		{
		    /* Align at the beginning of the CHRS chunk */
		    clipIO->io_Offset = offset;

		    /* Set the length to read */
		    length = clen;
		}
	    }
	    else
	    {
		/*
		 * The entire length of the clipboard contents, plus the
		 * ID_FORM and first chunk
		 */
		length = cbuff[1] + 8L;

		/* Buffer type is main IFF chunk type */
		buff_type = cbuff[2];

		/* Realign at the beginning of the clip */
		clipIO->io_Offset = 0;
	    }

	    /* Make sure we have something to return... */
	    if (length > 0L)
	    {
		/* Compute the length of our memory allocation */
		msize = sizeof (struct Buffer) + length;

		/* Allocate our buffer */
		if (buff = (struct Buffer *) AllocMem (msize, MEMF_CLEAR))
		{
		    /* set up the buffer variables */
		    buff->b_Size = msize;
		    buff->b_Type = buff_type;
		    buff->b_ClipID = clipIO->io_ClipID;

		    /* Point the buffer at the right place */
		    buff->b_Buff = (VOID *) ((buff) + 1);

		    /* read in the text string */
		    clipIO->io_Data = (char *) buff->b_Buff;
		    clipIO->io_Length = length;
		    DoIO (clipIO);
		}
	    }
	}

	/* indicate that we're done reading */
	clipIO->io_Offset = 0x7FFFFFF0;
	clipIO->io_Length = 1;
	clipIO->io_Data = NULL;
	DoIO (clipIO);
    }

    return (buff);
}

LONG writeLong (LONG * ldata)
{
    clipIO->io_Command = CMD_WRITE;
    clipIO->io_Error = 0;
    clipIO->io_Data = (char *) ldata;
    clipIO->io_Length = 4L;
    return ( (LONG) DoIO (clipIO) );
}

VOID freebuffer (struct Buffer * buff)
{
    /* Make sure that buffer is a pointer, not an error */
    if ((LONG) buff > 0L)
    {
	/* Free the author buffer, if we allocated it */
	if (buff->b_ASize > 0L)
	    FreeMem ((APTR) buff->b_Author, buff->b_ASize);

	/* Free the project buffer, if we allocated it */
	if (buff->b_PSize > 0L)
	    FreeMem ((APTR) buff->b_Project, buff->b_PSize);

	/* Free the entire buffer */
	FreeMem ((APTR) buff, buff->b_Size);
    }
}

