/* $Id: pla_wn.c,v 1.3 1993/03/17 17:01:44 mjl Exp $
   $Log: pla_wn.c,v $
 * Revision 1.3  1993/03/17  17:01:44  mjl
 * Eliminated some dead assignments that turned up when running with SAS/C's
 * global optimizer enabled on the Amiga.
 *
 * Revision 1.2  1993/03/16  06:49:26  mjl
 * Changed driver functions that check for events to do so only after a
 * specified number of calls, to reduce overhead.
 *
 * Revision 1.1  1993/03/15  21:34:26  mjl
 * Reorganization and update of Amiga drivers.  Window driver now uses Amiga
 * OS 2.0 capabilities.
 *
*/

/*	pla_win.c

	PLPLOT Amiga window device driver.
*/

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

#include "drivers.h"
#include "plamiga.h"
#include "pla_menu.h"

/* Function prototypes */

static void setpen	(PLINT);
static void PLDraw	(PLINT x, PLINT y);
static void PLMove	(PLINT x, PLINT y);
static void WaitForPage	(PLStream *);
static void HandleEvents(PLStream *);
static int  (*func)	(void);

/* top level declarations */

struct ReqToolsBase *ReqToolsBase;
struct Process *myproc;

PlAmigaWin PlAmigadev;
PlAmigaWin (*pla) = &PlAmigadev;

static PLDev device;
static PLDev (*dev) = &device;

PLStream *the_pls;
APTR oldwinptr;

static WORD polyTable[PL_MAXPOLYLINE * 2];

void myputs (char *str)
{
    Write (Output(), str, strlen(str));
}

/*
 *  Source machine generated by GadToolsBox V1.4
 *  which is (c) Copyright 1991,92 Jaba Development
 */

struct IntuiMessage    PlplotMsg;

struct TextAttr topaz11 = {
    (STRPTR)"topaz.font", 11, 0x00, 0x42 };

struct TextAttr topaz8 = {
    (STRPTR)"topaz.font", 8, 0x00, 0x02 };

struct ColorSpec ScreenColors[] = {
    0, 0x09, 0x09, 0x08,
    1, 0x00, 0x00, 0x00,
    2, 0x0C, 0x0C, 0x0C,
    3, 0x05, 0x06, 0x0B,
    4, 0x00, 0x00, 0x0F,
    5, 0x0F, 0x00, 0x0F,
    6, 0x00, 0x0F, 0x0F,
    7, 0x0F, 0x0F, 0x0F,
    8, 0x06, 0x02, 0x00,
    9, 0x0E, 0x05, 0x00,
    10, 0x09, 0x0F, 0x01,
    11, 0x0E, 0x0B, 0x00,
    12, 0x05, 0x05, 0x0F,
    13, 0x09, 0x02, 0x0F,
    14, 0x00, 0x0B, 0x00,
    15, 0x00, 0x03, 0x06,
    ~0, 0x00, 0x00, 0x00 };

UWORD DriPens[] = {
    0, 1, 1, 2, 1, 3, 1, 0, 2, (UWORD) ~0 };

/*----------------------------------------------------------------------*\
* Initialization of Menus follows.
\*----------------------------------------------------------------------*/

struct NewMenu PlplotNewMenu[] = {

/********* Project menu *********/

    NM_TITLE, (STRPTR)"Project", NULL, 0, NULL, NULL,

/* Open */

    NM_ITEM, (STRPTR)"Open", (STRPTR)"O", NM_ITEMDISABLED, 0L,
    (APTR)plamiga_Open,

/* Save */

    NM_ITEM, (STRPTR)"Save", (STRPTR)"S", NM_ITEMDISABLED, 0L,
    (APTR)plamiga_Save,

/* Save As */

    NM_ITEM, (STRPTR)"Save As", (STRPTR)"A", 0, NULL, NULL,
    NM_SUB, (STRPTR)"ILBM (IFF)", NULL, 0, 0L,
    (APTR)plamiga_SaveAs_ILBM,

/* Print */

    NM_ITEM, (STRPTR)"Print", (STRPTR)"P", 0, NULL, NULL,
    NM_SUB, (STRPTR)"Bitmap", NULL, 0, 0L,
    (APTR)plamiga_Print_Bitmap,
    NM_SUB, (STRPTR)"Full page (landscape)", NULL, 0, 0L,
    (APTR)plamiga_Print_landscape,
    NM_SUB, (STRPTR)"Full page (portrait)", NULL, 0, 0L,
    (APTR)plamiga_Print_portrait,

/* About */

    NM_ITEM, (STRPTR)"About...", NULL, NM_ITEMDISABLED, 0L,
    (APTR)plamiga_About,

/* Quit */

    NM_ITEM, (STRPTR)"Quit", (STRPTR)"Q", 0, 0L,
    (APTR)plamiga_Quit,

/********* Settings menu *********/

    NM_TITLE, (STRPTR)"Settings", NULL, 0, NULL, NULL,

/* Bring up ScreenMode requester */

    NM_ITEM, (STRPTR)"Screen Mode", NULL, 0, 0L,
    (APTR)plamiga_Screenmode,

/* Bring up palette requester, color map 0 */

    NM_ITEM, (STRPTR)"Palette 0", (STRPTR)"U", 0, 0L,
    (APTR)plamiga_Palette0,

/* Bring up cmap 1 modifier (not ready yet) */

    NM_ITEM, (STRPTR)"Palette 1", NULL, NM_ITEMDISABLED, 0L,
    (APTR)plamiga_Palette1,

    NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,

/* Load config file */

    NM_ITEM, (STRPTR)"Load Settings", NULL, 0, 0L,
    (APTR)plamiga_LoadConfig,

/* Save config file */

    NM_ITEM, (STRPTR)"Save Settings", NULL, 0, 0L,
    (APTR)plamiga_SaveConfigAs,
    NM_ITEM, (STRPTR)"Save Settings As...", NULL, 0, 0L,
    (APTR)plamiga_SaveConfig,

/********* User menu *********/

    NM_TITLE, (STRPTR)"User", NULL, 0, NULL, NULL,
    NM_ITEM, (STRPTR)"DUMMY", NULL, NM_ITEMDISABLED, 0L,
    (APTR)plamiga_DUMMY,

/* This is the end */

    NM_END, NULL, NULL, 0, 0L, NULL };

/*----------------------------------------------------------------------*\
* amiwn_init()
*
* Initialize device.
\*----------------------------------------------------------------------*/

void
amiwn_init(PLStream *pls)
{
    PLFLT Initdpmx, Initdpmy;
    struct Screen *wb_screen;
    
    pls->termin = 1;		/* is an interactive terminal */
    pls->icol0 = 1;
    pls->width = 1;
    pls->bytecnt = 0;
    pls->page = 0;
    pls->plbuf_enable = 1;

    if (!pls->colorset)
        pls->color = 1;

/* Open the required libraries. */

    pla_OpenLibs();

/* Save old window pointer */

    myproc = (struct Process *) FindTask (NULL);
    oldwinptr = myproc->pr_WindowPtr;

/*
* Set up default screen & window parameters.  Take the default screen
* parameters from Workbench -- the user can always change it and save the
* configuration.
*/

    pla->scr_displayID	= INVALID_ID;
    wb_screen = LockPubScreen("Workbench");

    if (wb_screen != NULL) {
	pla->scr_displayID = GetVPModeID(&(wb_screen->ViewPort));
	if (pla->scr_displayID != INVALID_ID) {
	    pla->scr_left	= wb_screen->LeftEdge;
	    pla->scr_top	= wb_screen->TopEdge;
	    pla->scr_width	= wb_screen->Width;
	    pla->scr_height	= wb_screen->Height;
	}
	UnlockPubScreen(NULL, wb_screen);
    }

    if (pla->scr_displayID == INVALID_ID) {
	pla->scr_left		= 0;
	pla->scr_top		= 0;
	pla->scr_width		= 640;
	pla->scr_height		= 400;
	pla->scr_displayID	= NTSC_MONITOR_ID | HIRESLACE_KEY;
    }

    pla->scr_depth	= 2;
    pla->maxcolors	= pow(2., (double) pla->scr_depth);
    pla->scr_type	= CUSTOMSCREEN;

    pla_SetFont();

/* Initialize display */

    pla_InitDisplay();

/* Virtual screen is 25 times the actual one. */

    pla->init_width = pla->cur_width * 25;
    pla->init_height = pla->cur_height * 25;

/* Set up plotting scale factors */

    pla->xscale = (double) pla->cur_width / pla->init_width;
    pla->yscale = (double) pla->cur_height / pla->init_height;

/* Set up device parameters */

    dev->xold = UNDEFINED;
    dev->yold = UNDEFINED;
    dev->xmin = 0;
    dev->xmax = pla->init_width - 1;
    dev->ymin = 0;
    dev->ymax = pla->init_height - 1;

/* For borderless window, only the menubar width needs to be accounted for in */
/* the offsets */

    pla->xoffset = 8;
    pla->yoffset = pla->screen->BarHeight + 2;

    Initdpmx = GfxBase->NormalDPMX;
    Initdpmy = GfxBase->NormalDPMY;
    if (pla->screen->ViewPort.Modes & HIRES)
	Initdpmx *= 2.;
    if (pla->screen->ViewPort.Modes & LACE)
	Initdpmy *= 2.;

    setpxl((PLFLT) (Initdpmx / 40.), (PLFLT) (Initdpmy / 40.));
    setphy(0, (pla->init_width - 1), 0, (pla->init_height - 1));
}

/*----------------------------------------------------------------------*\
* amiwn_line()
*
* Draw a line in the current color from (x1,y1) to (x2,y2).
\*----------------------------------------------------------------------*/

void 
amiwn_line(PLStream *pls, short x1a, short y1a, short x2a, short y2a)
{
    int x1=x1a, y1=y1a, x2=x2a, y2=y2a;
    static long count = 0, max_count = 10;

    if ( (++count/max_count)*max_count == count) {
	count = 0;
	HandleEvents(pls);      /* Check for events */
    }

    if (x1 == dev->xold && y1 == dev->yold) {
	PLDraw(x2, y2);
    }
    else {
	PLMove(x1, y1);
	PLDraw(x2, y2);
    }
    dev->xold = x2;
    dev->yold = y2;
}

/*----------------------------------------------------------------------*\
* amiwn_polyline()
*
* Draw a polyline in the current color.
\*----------------------------------------------------------------------*/

void 
amiwn_polyline (PLStream *pls, short *xa, short *ya, PLINT npts)
{
    PLINT i, j;
    static long count = 0, max_count = 5;

    if ( (++count/max_count)*max_count == count) {
	count = 0;
	HandleEvents(pls);      /* Check for events */
    }

/* Old way */
/*
    for (i=0; i<npts-1; i++) 
      amiwn_line(pls, xa[i], ya[i], xa[i+1], ya[i+1]);
*/
/* New way */

    for (i=0, j=0; i<npts; i++) {
	polyTable[j++] = pla->xoffset + xa[i] * pla->xscale;
	polyTable[j++] = pla->yoffset + pla->cur_height - pla->yscale * ya[i];
    }

    Move(pla->WRPort, polyTable[0], polyTable[1]);
    PolyDraw(pla->WRPort, npts, polyTable);

    dev->xold = UNDEFINED;
    dev->yold = UNDEFINED;

}

/*----------------------------------------------------------------------*\
* amiwn_eop()
*
* End of page. 
\*----------------------------------------------------------------------*/

void 
amiwn_eop(PLStream *pls)
{
/*    DisplayBeep(pla->screen);*/
    WaitForPage(pls);
}

/*----------------------------------------------------------------------*\
* amiwn_bop()
*
* Set up for the next page.  
* Advance to next family file if necessary (file output).
\*----------------------------------------------------------------------*/

void 
amiwn_bop(PLStream *pls)
{
    setpen(0);
    RectFill(pla->WRPort, pla->xoffset, pla->yoffset,
	     pla->cur_width + pla->xoffset, pla->cur_height + pla->yoffset);
    setpen(pls->icol0);

    dev->xold = UNDEFINED;
    dev->yold = UNDEFINED;
    pls->page++;
}

/*----------------------------------------------------------------------*\
* amiwn_tidy()
*
* Close graphics file or otherwise clean up.
\*----------------------------------------------------------------------*/

void 
amiwn_tidy(PLStream *pls)
{
    myproc->pr_WindowPtr = oldwinptr;

    pla_CloseWindow();
    pla_CloseScreen();

    pla_CloseLibs();
    pls->page = 0;
    pls->OutFile = NULL;
}

/*----------------------------------------------------------------------*\
* amiwn_color()
*
* Set pen color.
* Best to never draw in color 0, since for the Amiga driver that is
* always the background.
\*----------------------------------------------------------------------*/

void 
amiwn_color(PLStream *pls)
{
    U_CHAR icol0;

    HandleEvents(pls);	/* Check for intuition messages */

    icol0 = pls->icol0;
    if (icol0 < 1 || icol0 >= pla->maxcolors)
	icol0 = 1;

    setpen(icol0);
}

/*----------------------------------------------------------------------*\
* amiwn_text()
*
* Switch to text mode.
\*----------------------------------------------------------------------*/

void 
amiwn_text(PLStream *pls)
{
    HandleEvents(pls);	/* Check for intuition messages */
}

/*----------------------------------------------------------------------*\
* amiwn_graph()
*
* Switch to graphics mode.
\*----------------------------------------------------------------------*/

void 
amiwn_graph(PLStream *pls)
{
    HandleEvents(pls);	/* Check for intuition messages */
}

/*----------------------------------------------------------------------*\
* amiwn_width()
*
* Set pen width.
\*----------------------------------------------------------------------*/

void 
amiwn_width(PLStream *pls)
{
    HandleEvents(pls);	/* Check for intuition messages */
}

/*----------------------------------------------------------------------*\
* amiwn_esc()
*
* Escape function.
\*----------------------------------------------------------------------*/

void 
amiwn_esc(PLStream *pls, PLINT op, char *ptr)
{
    HandleEvents(pls);	/* Check for intuition messages */
}

/*----------------------------------------------------------------------*\
* pla_InitDisplay()
*
* Initialize display and other associated parameters.
\*----------------------------------------------------------------------*/

void 
pla_InitDisplay()
{

/* Open screen & window */

    if (pla_OpenScreen())
	exit(1);

    if (pla_OpenWindow())
	exit(1);

/* Change the window pointer for requesters */

    myproc->pr_WindowPtr = pla->window;

/* Initialize amiga display state data structure */

    pla->SRPort = &(pla->screen->RastPort);
    pla->WRPort = pla->window->RPort;
    pla->VPort = &(pla->screen->ViewPort);
    pla->CMap = pla->VPort->ColorMap;

/* Current window size. */

    pla->cur_width = pla->window->Width -
	pla->window->BorderLeft - pla->window->BorderRight - 16;

    pla->cur_height = pla->window->Height -
	pla->window->BorderTop - pla->window->BorderBottom - 16;

    setpen(1);
    SetDrMd(pla->WRPort, JAM1);
}

/*----------------------------------------------------------------------*\
* pla_SetFont()
*
* Set up font for menus, screen bar.  May eventually be user-settable.
* For now, just select between topaz 8 and topaz 11 on the basis of number
* of lines in the display.
\*----------------------------------------------------------------------*/

void
pla_SetFont(void)
{
    if (pla->scr_height >= 350)
	pla->font = &topaz11;
    else
	pla->font = &topaz8;
}

/*----------------------------------------------------------------------*\
* pla_OpenScreen()
\*----------------------------------------------------------------------*/
/* INDENT OFF */

int pla_OpenScreen(void)
{
    if ( ! (pla->screen =
	    OpenScreenTags(NULL,
			   SA_Left,		pla->scr_left,
			   SA_Top,		pla->scr_top,
			   SA_Width,		pla->scr_width,
			   SA_Height,		pla->scr_height,
			   SA_Depth,		pla->scr_depth,
			   SA_Colors,		&ScreenColors[0],
			   SA_Font,		pla->font,
			   SA_Type,		pla->scr_type,
			   SA_DisplayID,	pla->scr_displayID,
			   SA_AutoScroll,	1,
			   SA_Overscan,		OSCAN_STANDARD,
			   SA_Pens,		&DriPens[0],
			   SA_Title,		"Plplot 5.0",
			   TAG_DONE)))
	return(1L);

    if ( ! (pla->visual = GetVisualInfo(pla->screen, TAG_DONE)))
	return(2L);

    return(0L);
}
/* INDENT ON */

/*----------------------------------------------------------------------*\
* pla_CloseScreen()
\*----------------------------------------------------------------------*/

void pla_CloseScreen(void)
{
    if (pla->visual) {
	FreeVisualInfo(pla->visual);
	pla->visual = NULL;
    }

    if (pla->screen) {
	CloseScreen(pla->screen);
	pla->screen = NULL;
    }
}

/*----------------------------------------------------------------------*\
* pla_OpenWindow()
\*----------------------------------------------------------------------*/
/* INDENT OFF */

int pla_OpenWindow(void)
{
    if ( ! (pla->menus = CreateMenus(PlplotNewMenu, GTMN_FrontPen, 0L, TAG_DONE)))
	return(3L);

    LayoutMenus(pla->menus, pla->visual, GTMN_TextAttr, pla->font, TAG_DONE);

    if ( ! (pla->window =
	    OpenWindowTags(NULL,
			   WA_Left,	pla->scr_left,
			   WA_Top,	pla->scr_top,
			   WA_Width,	pla->scr_width,
			   WA_Height,	pla->scr_height,
			   WA_IDCMP,	IDCMP_MENUPICK | IDCMP_VANILLAKEY | 
					IDCMP_REFRESHWINDOW,	
			   WA_Flags,	WFLG_SMART_REFRESH | WFLG_BACKDROP |
					WFLG_BORDERLESS | WFLG_ACTIVATE,
			   WA_ScreenTitle,	"Plplot 5.0",
			   WA_CustomScreen,	pla->screen,
			   TAG_DONE)))
	return(4L);

    SetMenuStrip(pla->window, pla->menus);
    GT_RefreshWindow(pla->window, NULL);

    return(0L);
}
/* INDENT ON */

/*----------------------------------------------------------------------*\
* pla_CloseWindow()
\*----------------------------------------------------------------------*/

void pla_CloseWindow(void)
{
    if (pla->menus) {
	ClearMenuStrip(pla->window);
	FreeMenus(pla->menus);
	pla->menus = NULL;
    }

    if (pla->window) {
	CloseWindow(pla->window);
	pla->window = NULL;
    }
}

/*----------------------------------------------------------------------*\
* WaitForPage()
*
* This routine waits for the user to advance the plot, while handling
* all other events.
\*----------------------------------------------------------------------*/

static void
WaitForPage(PLStream *pls)
{
    if (pls->nopause)
	return;

    while (!pla->exit_eventloop) {
	Wait(1L << pla->window->UserPort->mp_SigBit);
	HandleEvents(pls);
    }
    pla->exit_eventloop = FALSE;
}


/*----------------------------------------------------------------------*\
* HandleEvents()
*
* Just a front-end to HandlePlplotIDCMP() to make sure the stream
* pointer is saved where the event-handling code can access it.
\*----------------------------------------------------------------------*/

static void
HandleEvents(PLStream *pls)
{
    the_pls = pls;
    HandlePlplotIDCMP();
}

/*----------------------------------------------------------------------*\
* HandlePlplotIDCMP
*
* Responds to all events that PLPLOT cares about.
\*----------------------------------------------------------------------*/

int HandlePlplotIDCMP(void)
{
    struct IntuiMessage	*m;
    struct MenuItem	*n;
    BOOL		running = TRUE;

    while(m = GT_GetIMsg(pla->window->UserPort)) {

	CopyMem((char *) m, (char *) &PlplotMsg,
		(long)sizeof(struct IntuiMessage));

	GT_ReplyIMsg(m);

	switch (PlplotMsg.Class) {

	case	IDCMP_REFRESHWINDOW:
	    GT_BeginRefresh(pla->window);
	    GT_EndRefresh(pla->window, TRUE);
	    break;

	case	IDCMP_VANILLAKEY:
	    running = plamiga_KEY();
	    break;

	case	IDCMP_MENUPICK:
	    while(PlplotMsg.Code != MENUNULL) {
		n = ItemAddress(pla->menus, PlplotMsg.Code);
		func = (void *)(GTMENUITEM_USERDATA(n));
		running = func();
		if (pla->restart) {
		    pla->restart = 0;
		    return (running);
		}
		PlplotMsg.Code = n->NextSelect;
	    }
	    break;
	}
    }
    return(running);
}

/*----------------------------------------------------------------------*\
* Move and draw
\*----------------------------------------------------------------------*/

static void 
PLMove(PLINT x, PLINT y)
{
    PLINT x1, y1, xsc, ysc;

    x1 = x * pla->xscale;
    y1 = y * pla->yscale;

    xsc = pla->xoffset + x1;
    ysc = pla->yoffset + pla->cur_height - y1;

    Move(pla->WRPort, xsc, ysc);
}

static void 
PLDraw(PLINT x, PLINT y)
{
    PLINT x1, y1, xsc, ysc;

    x1 = x * pla->xscale;
    y1 = y * pla->yscale;

    xsc = pla->xoffset + x1;
    ysc = pla->yoffset + pla->cur_height - y1;

    Draw(pla->WRPort, xsc, ysc);
}

/*----------------------------------------------------------------------*\
* Color
\*----------------------------------------------------------------------*/

static void 
setpen(PLINT color)
{
    SetAPen(pla->WRPort, color);
}

/*----------------------------------------------------------------------*\
* Libraries
\*----------------------------------------------------------------------*/

void 
pla_OpenLibs(void)
{
    if (IntuitionBase =
	(struct IntuitionBase *) OpenLibrary("intuition.library", 0L)) {

	if (GfxBase =
	    (struct GfxBase *) OpenLibrary("graphics.library", 0L)) {

	    if (ReqToolsBase =
		(struct ReqToolsBase *) OpenLibrary (REQTOOLSNAME,
						     REQTOOLSVERSION))

		return;		/* All libraries successfully opened */
	    else
		myputs ("You need reqtools.library V38 or higher!\n"
			"Please install it in your Libs: drirectory.\n");

	    CloseLibrary((struct Library *) GfxBase);
	}
	else
	    puts("\nError opening Graphics library.");

	CloseLibrary((struct Library *) IntuitionBase);
    }
    else
	puts("\nError opening Intuition library.");

    pl_exit();
}

void 
pla_CloseLibs(void)
{
    CloseLibrary((struct Library *) GfxBase);
    CloseLibrary((struct Library *) IntuitionBase);
    CloseLibrary((struct Library *) ReqToolsBase);
}

