/* $Id: plsupport.c,v 1.1 1993/03/15 21:30:58 mjl Exp $
   $Log: plsupport.c,v $
 * Revision 1.1  1993/03/15  21:30:58  mjl
 * Files shuffled around in the Amiga driver reorganization.
 *
 * Revision 1.2  1992/10/12  17:11:27  mjl
 * Amiga-specific mods, including ANSI-fication.
 *
 * Revision 1.1  1992/05/20  21:35:29  furnish
 * Initial checkin of the whole PLPLOT project.
 *
*/

#include "plplot.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "plamiga.h"
#include <libraries/dosextens.h>

extern short fbuffer;
extern FILE *PlotFile;

/* Most of the IFF and preferences stuff borrowed from Tom Rokicki */
static union printerIO {
    struct IOStdReq ios;
    struct IODRPReq iodrp;
    struct IOPrtCmdReq iopc;
} *printerIO;

static struct PrinterData *PD;
static struct PrinterExtendedData *PED;
static struct MsgPort *replyport;
static struct RastPort rastport;
static struct BitMap Bitmap;
static short dummycolors[] =
{0x0000, 0x0fff};
static struct ColorMap dummyColorMap =
{NULL, NULL, 2, (APTR) & dummycolors};


static void 
doio(void)
{
    register struct IODRPReq *ioreq;

    ioreq = &printerIO->iodrp;
    SendIO((struct IORequest *) ioreq);
    while (1) {
	if (GetMsg(replyport) == NULL)
	    Wait(1L << replyport->mp_SigBit);
	else
	    break;
    }
}

void 
dmpport(long flags, int x, int y)
{
    register struct IODRPReq *ioreq;

    ioreq = &printerIO->iodrp;
    ioreq->io_Command = PRD_DUMPRPORT;
    ioreq->io_RastPort = &rastport;
    ioreq->io_ColorMap = &dummyColorMap;
    ioreq->io_Modes = 0;
    ioreq->io_SrcX = 0;
    ioreq->io_SrcY = 0;
    ioreq->io_SrcWidth = x;
    ioreq->io_SrcHeight = y;
    ioreq->io_DestCols = x;
    ioreq->io_DestRows = y;
    ioreq->io_Special = flags | SPECIAL_TRUSTME;
    doio();
}

static long bitmapx, bitmapy, wordsperrow;
static short *bitmap;

int 
mapinit(long bmapx, long bmapy)
{
    bitmapx = bmapx;
    bitmapy = bmapy;
    wordsperrow = (bmapx + 15) / 16;
    Bitmap.BytesPerRow = wordsperrow * 2;
    Bitmap.Rows = bmapy;
    /* Allocate memory for bitmap */
    if ((bitmap = (short *) calloc(wordsperrow * 2, (int) bmapy)) == NULL) {
	fprintf(stderr, "\nCan't allocate memory for bitmap dump.\n");
	return (1);
    }
    Bitmap.Planes[0] = (PLANEPTR) bitmap;
    return (0);
}

void 
mapfree(void)
{
    free((VOID *) bitmap);
}

void 
mapclear(void)
{
    memset((char *) bitmap, '\0', (int) (wordsperrow * (long) bitmapy * 2));
}

/* Function to draw the line in the bitmap */

void 
mapline(register int x1, register int y1, register int x2, register int y2)
{
    register short *p;
    register unsigned int b;
    register int t;
    int d;
    int nextrow;

    /* we always want to go left to right. */
    if (x1 > x2) {
	t = x1;
	x1 = x2;
	x2 = t;
	t = y1;
	y1 = y2;
	y2 = t;
    }
    p = bitmap + y1 * wordsperrow;
    /* we always want to go `up'. */
    if (y2 > y1)
	nextrow = wordsperrow;
    else {
	y2 = y1 * 2 - y2;
	nextrow = -wordsperrow;
    }
    b = 1L << (15 - (x1 & 15));
    p += (x1 >> 4);
    x2 -= x1;
    y2 -= y1;
    /* two routines, one for major in x, one for major in y */
    if (y2 > x2) {
	d = y2;
	t = x2 / 2;
	y1 = y2;
	while (y1 >= 0) {
	    *p |= b;
	    p += nextrow;
	    t += x2;
	    if (t >= d) {
		t -= d;
		b >>= 1;
		if (b == 0) {
		    b = 0x8000;
		    p++;
		}
	    }
	    y1--;
	}
    }
    else {
	d = x2;
	t = y2 / 2;
	x1 = x2;
	while (x1 >= 0) {
	    *p |= b;
	    b >>= 1;
	    if (b == 0) {
		b = 0x8000;
		p++;
	    }
	    t += y2;
	    if (t >= d) {
		t -= d;
		p += nextrow;
	    }
	    x1--;
	}
    }
}

int 
openprinter(void)
{
    replyport = (struct MsgPort *) CreatePort("PLPlot.PIO", 0L);
    if (replyport == NULL) {
	fprintf(stderr, "Couldn't open reply port for printer dump.\n");
	return (1);
    }

    printerIO = (union printerIO *)
	AllocMem((long) sizeof(union printerIO),
		 (long) (MEMF_CLEAR | MEMF_PUBLIC));
    if (printerIO == NULL) {
	DeletePort(replyport);
	fprintf(stderr, "Couldn't create IO request block for printer dump.\n");
	return (1);
    }
    printerIO->ios.io_Message.mn_Node.ln_Type = NT_MESSAGE;
    printerIO->ios.io_Message.mn_Length = sizeof(union printerIO);
    printerIO->ios.io_Message.mn_ReplyPort = replyport;

    if (OpenDevice("printer.device", 0L, (struct IORequest *) printerIO, 0L) != NULL) {
	FreeMem((VOID *) printerIO, (long) sizeof(union printerIO));
	DeletePort(replyport);
	fprintf(stderr, "Error opening printer device.\n");
	return (1);
    }

    PD = (struct PrinterData *) printerIO->iodrp.io_Device;
    PED = &PD->pd_SegmentData->ps_PED;
    return (0);
}

void 
closeprinter(void)
{
    CloseDevice((struct IORequest *) printerIO);
    FreeMem((VOID *) printerIO, (long) sizeof(union printerIO));
    DeletePort(replyport);
}

int 
queryprint(long *bmapx, long *bmapy, long *bmapxmax, long *bmapymax, 
	   long *xdpi, long *ydpi)
{
    int mode;
    Bitmap.BytesPerRow = 0;
    Bitmap.Rows = 0;
    Bitmap.Depth = 1;
    rastport.BitMap = &Bitmap;
    dmpport((long) (SPECIAL_NOPRINT), 0, 0);

    *bmapxmax = PED->ped_MaxXDots;
    *bmapymax = 95 * PED->ped_YDotsInch / 10;	/* 9.5" to prevent problems */

    *xdpi = PED->ped_XDotsInch;
    *ydpi = PED->ped_YDotsInch;

    if (PD->pd_Preferences.PrintFlags & BOUNDED_DIMENSIONS) {
	*bmapx = PD->pd_Preferences.PrintMaxWidth * PED->ped_XDotsInch / 10;
	*bmapy = PD->pd_Preferences.PrintMaxHeight * PED->ped_YDotsInch / 10;
	mode = 1;
    }
    else if (PD->pd_Preferences.PrintFlags & ABSOLUTE_DIMENSIONS) {
	*bmapx = PD->pd_Preferences.PrintMaxWidth * PED->ped_XDotsInch / 10;
	*bmapy = PD->pd_Preferences.PrintMaxHeight * PED->ped_YDotsInch / 10;
	mode = 0;
    }
    else if (PD->pd_Preferences.PrintFlags & PIXEL_DIMENSIONS) {
	*bmapx = PD->pd_Preferences.PrintMaxWidth;
	*bmapy = PD->pd_Preferences.PrintMaxHeight;
	mode = 0;
    }
    else {
	*bmapx = *bmapxmax;
	*bmapy = *bmapymax;
	mode = 1;
    }

    PD->pd_Preferences.PrintImage = IMAGE_NEGATIVE;
    PD->pd_Preferences.PrintShade = SHADE_BW;
    PD->pd_Preferences.PrintThreshold = 7;
    PD->pd_Preferences.PrintAspect = ASPECT_HORIZ;
    PD->pd_Preferences.PrintFlags = INTEGER_SCALING;
    return (mode);
}

void 
ejectpage(void)
{
    printerIO->ios.io_Command = PRD_RAWWRITE;
    printerIO->ios.io_Data = (APTR) "\014";
    printerIO->ios.io_Length = 1;
    doio();
}

/* If type = 0 dump the plplot window to the printer with full preferences
   support, i.e. color, shading, threshold, etc.  Otherwise we override many
   of the preferences selections, create a full page black and white
   bitmap and dump it to the printer. */

void 
screendump(PLINT type)
{
    register struct IODRPReq *ioreq;
    long bmapx, bmapy;
    long cxy, x1, y1;
    long dwidth, dheight, xlas, ylas, xnew, ynew;
    int penwid = 1;

    if (type != 0 && fbuffer == 0)
	return;
    if (openprinter())		/* return if error */
	return;

    ioreq = &printerIO->iodrp;

    if (type == 0) {
	ioreq->io_Command = PRD_DUMPRPORT;
	ioreq->io_RastPort = PLWRPort;
	ioreq->io_ColorMap = PLScreen->ViewPort.ColorMap;
	ioreq->io_Modes = PLScreen->ViewPort.Modes;
	ioreq->io_SrcX = XOffset;
	ioreq->io_SrcY = YOffset;
	ioreq->io_SrcWidth = PLWidth;
	ioreq->io_SrcHeight = PLHeight;
	ioreq->io_DestCols = 0;
	ioreq->io_DestRows = 0;
	ioreq->io_Special = SPECIAL_FULLROWS | SPECIAL_FULLCOLS |
	    SPECIAL_TRUSTME;
	doio();
    }
    else {
	/* get bmapx and bmapy (the others are dummy variables) */
	queryprint(&bmapx, &bmapy, &dwidth, &dheight, &dwidth, &dheight);

	/* Set up map */
	if (mapinit(bmapx, bmapy)) {
	    closeprinter();
	    return;
	}

	prepupdate();

	dwidth = bmapx - 2;
	dheight = bmapy - 2;
	while (!getpoint(&cxy, &x1, &y1)) {
	    if (cxy == PENU) {
		if (type == 1) {
		    xlas = ((long) dheight * (long) x1) / InitPLWidth;
		    ylas = ((long) dwidth * (long) y1) / InitPLHeight;
		}
		else {
		    xlas = ((long) dwidth * (long) x1) / InitPLWidth;
		    ylas = ((long) dheight * (long) y1) / InitPLHeight;
		}
	    }
	    else if (cxy == PEND) {
		if (type == 1) {
		    xnew = ((long) dheight * (long) x1) / InitPLWidth;
		    ynew = ((long) dwidth * (long) y1) / InitPLHeight;
		    switch (penwid) {
		    case 3:
		      mapline(ylas, xlas, ynew, xnew);
		    case 2:
		      mapline(ylas + 2, xlas + 2, ynew + 2, xnew + 2);
		    case 1:
		    default:
		      mapline(ylas + 1, xlas + 1, ynew + 1, xnew + 1);
		    }
		}
		else {
		    xnew = ((long) dwidth * (long) x1) / InitPLWidth;
		    ynew = ((long) dheight * (long) y1) / InitPLHeight;
		    switch (penwid) {
		    case 3:
		      mapline(xlas, dheight - ylas, xnew, dheight - ynew);
		    case 2:
		      mapline(xlas + 2, dheight - ylas + 2, xnew + 1, dheight - ynew + 2);
		    case 1:
		    default:
		      mapline(xlas + 1, dheight - ylas + 1, xnew + 1, dheight - ynew + 1);
		    }
		}
		xlas = xnew;
		ylas = ynew;
	    }
	    else if (cxy == SPEN) {
		;
	    }
	    else if (cxy == PWID) {
		penwid = x1;
	    }
	}
	finiupdate();

	dmpport(0L, bmapx, bmapy);
	mapfree();
    }
    closeprinter();
}

/* prepupdate() flushes and rewinds the plot buffer file.  This should always
   be called before attempting to read the buffer file. */

void 
prepupdate(void)
{
/*    plgfile(PlotFile); */
    if (fbuffer) {
	fflush(PlotFile);
	rewind(PlotFile);
    }
}

/* Use getpoint to read the next command in the plot buffer file. */
/* Returns 0 if okay or 1 if end of file. */

int 
getpoint(long *com, long *x, long *y)
{
    short csh, xsh, ysh;

/*    plgfile(PlotFile); */
    if (!fread((char *) &csh, sizeof(short), 1, PlotFile))
	return (1);

    *com = csh;
    if (csh == PENU || csh == PEND) {
	fread((char *) &xsh, sizeof(short), 1, PlotFile);
	fread((char *) &ysh, sizeof(short), 1, PlotFile);
	*x = xsh;
	*y = ysh;
    }
    else if (csh == SPEN || csh == PWID) {
	fread((char *) &xsh, sizeof(short), 1, PlotFile);
	*x = xsh;
    }

    return (0);
}

void 
finiupdate(void)
{
/*    plgfile(PlotFile); */
    fseek(PlotFile, 0L, 2);
}

static long iffpos;
static short curbyte;
static short curcount;
static short runcount;
static FILE *OutFile;

struct BitMapHeader {
    UWORD w, h;
    WORD x, y;
    UBYTE nPlanes;
    UBYTE masking;
    UBYTE compression;
    UBYTE pad1;
    UWORD transparentColor;
    UBYTE xAspect, yAspect;
    WORD pageWidth, pageHeight;
} bmhd;

static void 
iffobyte(register int b)
{
    putc(b, OutFile);
    iffpos++;
}
char outchunk[256];

static void 
iffoutbyte(register int b)
{
    register int i;

    if (b == curbyte && runcount < 125) {
	runcount++;
    }
    else {
	if (runcount > 2) {
	    if (curcount > 0) {
		iffobyte(curcount - 1);
		for (i = 0; i < curcount; i++)
		    iffobyte(outchunk[i]);
		curcount = 0;
	    }
	    iffobyte(256 - runcount + 1);
	    iffobyte(curbyte);
	}
	else {
	    while (runcount > 0) {
		outchunk[curcount++] = curbyte;
		runcount--;
	    }
	    if (curcount > 110) {
		iffobyte(curcount - 1);
		for (i = 0; i < curcount; i++)
		    iffobyte(outchunk[i]);
		curcount = 0;
	    }
	}
	curbyte = b;
	runcount = 1;
    }
}

static void 
finishrow(void)
{
    register int i;

    if (runcount <= 2) {
	while (runcount > 0) {
	    outchunk[curcount++] = curbyte;
	    runcount--;
	}
    }
    if (curcount > 0) {
	iffobyte(curcount - 1);
	for (i = 0; i < curcount; i++)
	    iffobyte(outchunk[i]);
	curcount = 0;
    }
    if (runcount > 0) {
	iffobyte(256 - runcount + 1);
	iffobyte(curbyte);
	curbyte = -1;
	runcount = 0;
    }
}

#define BADFLAGS    (SPRITES|VP_HIDE|GENLOCK_AUDIO|GENLOCK_VIDEO)
#define FLAGMASK    (~BADFLAGS)
#define CAMGMASK    (FLAGMASK & 0x0000FFFFL)

/* Routine to write out color bitmap. */
void 
saveiff(void)
{
    long int formlen = 0, formpos, bmhdlen, camglen, camgbod;
    long int bodylen = 0, bodypos, cmaplen;
    long int numcolors, rowlen, rowoff, coloff;
    short int i, j, k;
    UWORD *coltable;
    UBYTE *byteptr, rgb[3];
    struct BitMap *PLBitMap;
    char *filename;
    FILE *IFFFile;
    char *plfilereq();

    filename = plfilereq();
    if (!filename)
	return;

    if ((IFFFile = fopen(filename, "w+")) == NULL) {
	fprintf(stderr, "Can't open %s\n", filename);
	return;
    }

    iffpos = 0;
    curbyte = -1;
    curcount = 0;
    runcount = 0;
    OutFile = IFFFile;

    bmhdlen = sizeof(struct BitMapHeader);
    camglen = 4;
    PLBitMap = &PLScreen->BitMap;

    fwrite("FORM", sizeof(char), 4, IFFFile);
    formpos = ftell(IFFFile);
    fwrite((char *) &formlen, sizeof(long), 1, IFFFile);

    fwrite("ILBM", sizeof(char), 4, IFFFile);
    formlen += 4;

    fwrite("BMHD", sizeof(char), 4, IFFFile);
    formlen += 4;
    fwrite((char *) &bmhdlen, sizeof(long), 1, IFFFile);
    formlen += 4;
    bmhd.w = 16 * ((PLWidth + 15) / 16);
    bmhd.h = PLHeight;
    bmhd.x = PLWindow->LeftEdge + XOffset;
    bmhd.y = PLWindow->TopEdge + YOffset;
    bmhd.nPlanes = PLBitMap->Depth;
    bmhd.masking = 0;
    bmhd.compression = 1;
    bmhd.pad1 = 0;
    bmhd.transparentColor = 0;
    bmhd.xAspect = 10;
    bmhd.yAspect = 11;
    bmhd.pageWidth = PLScreen->Width;
    bmhd.pageHeight = PLScreen->Height;
    fwrite((char *) &bmhd, bmhdlen, 1, IFFFile);
    formlen += bmhdlen;

    fwrite("CAMG", sizeof(char), 4, IFFFile);
    formlen += 4;
    fwrite((char *) &camglen, sizeof(long), 1, IFFFile);
    formlen += 4;
    camgbod = PLScreen->ViewPort.Modes & CAMGMASK;
    fwrite((char *) &camgbod, sizeof(long), 1, IFFFile);
    formlen += 4;

    fwrite("CMAP", sizeof(char), 4, IFFFile);
    formlen += 4;
    numcolors = 1;
    for (i = PLBitMap->Depth; i > 0; i--)
	numcolors *= 2;
    cmaplen = numcolors * 3;
    fwrite((char *) &cmaplen, sizeof(long), 1, IFFFile);
    formlen += 4;
    coltable = (UWORD *) PLCMap->ColorTable;
    for (i = 0; i < numcolors; i++) {
	rgb[0] = (*coltable >> 4) & 0xf0;
	rgb[1] = *coltable & 0xf0;
	rgb[2] = (*coltable << 4) & 0xf0;
	fwrite((char *) rgb, sizeof(UBYTE), 3, IFFFile);
	formlen += 3;
	coltable++;
    }

    fwrite("BODY", sizeof(char), 4, IFFFile);
    formlen += 4;
    bodypos = ftell(IFFFile);
    fwrite((char *) &bodylen, sizeof(long), 1, IFFFile);
    formlen += 4;
    rowlen = ((PLWidth + 15) / 16);
    rowlen *= 2;
    rowoff = (PLWindow->TopEdge + YOffset) * PLBitMap->BytesPerRow;
    coloff = (PLWindow->LeftEdge + XOffset) / 8;
    for (i = 0; i < PLHeight; i++) {
	for (j = 0; j < PLBitMap->Depth; j++) {
	    byteptr = (UBYTE *) PLBitMap->Planes[j] + rowoff + coloff;
	    for (k = 0; k < rowlen; k++)
		iffoutbyte(*byteptr++ & 255);
	    finishrow();
	}
	rowoff += PLBitMap->BytesPerRow;
    }
    if (iffpos & 1)
	iffobyte(0);
    bodylen = iffpos;
    formlen += iffpos;
    fflush(IFFFile);
    fseek(IFFFile, formpos, 0);
    fwrite((char *) &formlen, sizeof(long), 1, IFFFile);
    fseek(IFFFile, bodypos, 0);
    fwrite((char *) &bodylen, sizeof(long), 1, IFFFile);
    fclose(IFFFile);
}

/*
 *   Code we steal to write a black and white IFF file.
 */
static struct iffhead {
    char formname[4];
    long formlen;		/* fill me in */
    char ilbmname[4];
    char bmhdname[4];
    long bmhdlen;
    short w, h;			/* fill me in */
    long dummy0;
    char numplanes, masking, compression, pad1;
    short tc;
    char xas, yas;
    short pw, ph;
    char cmapname[4];
    long cmaplen;
    char r0, g0, b0, r1, g1, b1;
    char bodyname[4];
    long bodylen;		/* fill me in */
} iffhead = { {'F','O','R','M'}, 0, {'I','L','B','M'}, {'B','M','H','D'}, 20,
      0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 320, 200, {'C','M','A','P'}, 6, 240, 240,
      240, 0, 0, 0, {'B','O','D','Y'}, 0 } ;

/*
 *   Finally we get into the nitty gritty of writing the stupid file.
 */

void 
iffwritefile(PLINT xdpi, PLINT ydpi, FILE *File)
{
    register int i, j;
    register short *p;

    p = bitmap;
    OutFile = File;
    fwrite((char *) &iffhead, 1, sizeof(struct iffhead), OutFile);
    iffpos = 0;
    curbyte = -1;
    curcount = 0;
    runcount = 0;
    for (j = 0; j < bitmapy; j++) {
	for (i = wordsperrow; i; i--, p++) {
	    iffoutbyte((*p >> 8) & 255);
	    iffoutbyte(*p & 255);
	}
	finishrow();
    }
    if (iffpos & 1)
	iffobyte(0);
    fseek(OutFile, 0L, 0);
    iffhead.w = bitmapx;
    iffhead.h = bitmapy;
    iffhead.pw = bitmapx;
    iffhead.ph = bitmapy;
    iffhead.formlen = iffpos + sizeof(struct iffhead) - 8;
    iffhead.bodylen = iffpos;
    iffhead.xas = xdpi;
    iffhead.yas = ydpi;
    fwrite((char *) &iffhead, 1, sizeof(struct iffhead), OutFile);
}

/* Use 1.3 palette program to set colors. */
/* Note this comes with 2.0 as 'sys:tools/colors' */

void 
plcolreq(void)
{
    short i;
    extern PLINT MaxColors;
    struct FileLock *lock;
    struct Process *thisproc;
    APTR winptr;

    /* This ensures that a requester won't appear if device doesn't exist */
    thisproc = (struct Process *) FindTask(0);
    winptr = thisproc->pr_WindowPtr;
    thisproc->pr_WindowPtr = (APTR) - 1;	/* No window */

    /* Check to see if palette is in tools: or sys:tools */
    /* Note this has been renamed to 'sys:tools/colors' in 2.0 */

    if (lock = (struct FileLock *) Lock("sys:tools/colors", ACCESS_READ)) {
	Execute("sys:tools/colors", 0, 0);
	UnLock((BPTR) lock);
    }
    else if (lock = (struct FileLock *) Lock("tools:palette", ACCESS_READ)) {
	Execute("tools:palette", 0, 0);
	UnLock((BPTR) lock);
    }
    /* Can't find palette so just return */
    else {
	thisproc->pr_WindowPtr = winptr;
	return;
    }

    /* Restore window pointer */
    thisproc->pr_WindowPtr = winptr;

    /* Save new colors in PLCurPrefs. */
    for (i = 0; i < MaxColors; i++)
	PLCurPrefs.Color[i] = GetRGB4(PLCMap, i);

}

/* This routine disables all the gadgets attached to PLWindow.
   It's useful when saving the window as an IFF file or when dumping it
   to the printer. (We don't want the user moving the window around or
   resizing it during those periods.). We always disable the window sizing
   and close gadgets. If flag is zero we disable the dragging gadget also. */

void 
disablegads(PLINT flag)
{
    int type;
    struct Gadget *gadget;

    gadget = PLWindow->FirstGadget;
    while (gadget) {
	type = gadget->GadgetType & 0x00ff;
	if (type == SIZING || type == CLOSE || (type == WDRAGGING && !flag))
	    OffGadget(gadget, PLWindow, 0);
	gadget = gadget->NextGadget;
    }
}

void 
enablegads(void)
{
    struct Gadget *gadget;

    gadget = PLWindow->FirstGadget;
    while (gadget) {
	OnGadget(gadget, PLWindow, 0);
	gadget = gadget->NextGadget;
    }
    RefreshWindowFrame(PLWindow);
}
