#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <devices/printer.h>
#include <devices/prtbase.h>

extern struct PrinterData *PD;
extern struct PrinterExtendedData *PED;
	
#define PWrite (*(PD->pd_PWrite))	/* write data to device */
#define PReady (*(PD->pd_PBothReady))	/* wait for both buffers ready */

#define LEAD   4		/* bytes to start each complete cycle */
#define EXTRA  9		/* bytes to prefix each pass of the head */
#define TAIL   2		/* bytes to end each complete cycle */

void *AllocMem();

/* these two tables wouldn't normally be bothered with but in light of
 * the number of times that these operations need to be performed
 * for each pass of the print head I thought they'd be worthwhile
 */

/*
 * this table simulates the operation   (1<<(5-(y%6)))
 * for values of y between 0 and 23 (the number of pixels in a printer pass)
 *
 */

UBYTE bit_table[] = {
		    32,16,8,4,2,1,
		    32,16,8,4,2,1,
		    32,16,8,4,2,1,
		    32,16,8,4,2,1
};

/* this table simulates  y/6 for values of y between 0 and 23 */
/* saves a long division opeation */
UBYTE div_table[] = {
		    0,0,0,0,0,0,
		    1,1,1,1,1,1,
		    2,2,2,2,2,2,
		    3,3,3,3,3,3
};

UBYTE *GfxBuffer;

ULONG Render(ct,x,y,status)
register UBYTE ct;
register UWORD x,y;
register UBYTE status;
{
	static ULONG ROWSIZE;
	static ULONG COLOURSIZE;
	static ULONG BUFSIZE;
	static ULONG colours[4];
	static ULONG centre;
	static ULONG bufptr;
	static UBYTE *ptr;
	static ULONG psize;
	int i,filler;
	int err;

/* Aztec doesn't preserve a6, we need this in a printer driver */
#asm
	move.l	a6,-(sp)
#endasm
	err = 0;

	switch (status) {

	case 0:	/* alloc memory for printer buffer */

		filler 	   = (centre) ? ((PED->ped_MaxXDots -x ) / 2 ) : 0;
		ROWSIZE    = ((x+filler)<<2) + EXTRA;
		COLOURSIZE = ROWSIZE<<2;
		BUFSIZE	   = COLOURSIZE+LEAD+TAIL+1;	/* plus 1 for NULL */

		colours[0] = LEAD +  EXTRA + filler + ROWSIZE*0;
		colours[1] = LEAD +  EXTRA + filler + ROWSIZE*1;
		colours[2] = LEAD +  EXTRA + filler + ROWSIZE*2;
		colours[3] = LEAD +  EXTRA + filler + ROWSIZE*3;
		psize = x+filler;

		dbprintf("Size Data:\n");
		dbprintf("\tfiller: %d\n",filler);
		dbprintf("\tROWSIZE: %d\n",ROWSIZE);
		dbprintf("\tCOLOURSIZE: %d\n",COLOURSIZE);
		dbprintf("\tBUFSIZE: %d\n",BUFSIZE);
		dbprintf("\tcolours: %d %d %d %d\n",
			colours[0],colours[1],colours[2],colours[3]);

		GfxBuffer =  AllocMem(BUFSIZE*2,MEMF_PUBLIC);

		if (err = (GfxBuffer==0))	    break;
		if (err = PWrite("\x1b\x1a\x49",3)) break;
		if (err = PWait(1,0))		    break;

		/* set to first buffer */
		bufptr  = 0;
		break;

	case 1:	/* render pixel */
		y %= 24; /* blecch, I wanted to avoid this calculation */
		i = bufptr + (x<<2) + div_table[y] + colours[3-ct];
		GfxBuffer[i] |= bit_table[y];
		break;

	case 2: /* write a buffer */
		dbprintf("Writing a buffer\n");

		/* BUFSIZE-1 is so that we don't send the trailing null */
		if (err=PWrite(&GfxBuffer[bufptr],BUFSIZE-1)) break;
		bufptr = BUFSIZE-bufptr;
		break;

	case 3: /* initialize the current buffer */
		dbprintf("Performing initialization\n");

		ptr = &GfxBuffer[bufptr];

		for (i=BUFSIZE;i--;) *ptr++ = 0;	/* clear buffer */

		ptr = &GfxBuffer[bufptr];
		sprintf(ptr,"\x1bL07");
		sprintf(ptr+LEAD     	  ,"\r\x1bc\x1b;%04d",psize); /* c */
		sprintf(ptr+LEAD+ROWSIZE  ,"\r\x1bm\x1b;%04d",psize); /* m */
		sprintf(ptr+LEAD+ROWSIZE*2,"\r\x1by\x1b;%04d",psize); /* y */
		sprintf(ptr+LEAD+ROWSIZE*3,"\r\x1bb\x1b;%04d",psize); /* b */
		sprintf(ptr+LEAD+ROWSIZE*4,"\r\n");	/* crlf + NULL */

		break;

	case 4: /* cleanup -- release memory */
		dbprintf("Print finished -- cleanup\n");
		dbprintf("waiting for printer\n");

		err = PReady();

		dbprintf("Freeing memory\n");

		FreeMem(GfxBuffer,BUFSIZE*2);

		dbprintf("Returning\n");

		break;

	case 5:	/* handle any special commands */
		centre = x & SPECIAL_CENTER;
		dbprintf("special: %d %d %d %d\n",ct,x,y,status);

		break;

	default:
		break;
	}
#asm
	move.l	(sp)+,a6
#endasm
	return(err);
}
