/*
 * M A N D E L B R O T	  C O N S T R U C T I O N   S E T
 *
 * (C) Copyright 1989 by Olaf Seibert.
 * Mandel may be freely distributed. See file 'doc/Notice' for details.
 *
 * Main Loop, and a lot of Variables.
 */

#include <exec/types.h>
#include <intuition/intuition.h>
#ifdef DEBUG
#   include <stdio.h>
#   undef STATIC
#   define STATIC		/* EMPTY */
#endif

#include "mandel.h"

extern int	Enable_Abort;

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct LayersBase *LayersBase;

struct TextAttr Topaz80 = {
    (STRPTR) "topaz.font", TOPAZ_EIGHTY, FS_NORMAL, FPF_ROMFONT
};

struct TextAttr Topaz60 = {
    (STRPTR) "topaz.font", TOPAZ_SIXTY, FS_NORMAL, FPF_ROMFONT
};

struct NewScreen MandelNScreen =
{
    0, 0, 320, 256,		/* LeftEdge, TopEdge, Width, Height */
    4,				/* Depth */
    2, 1,			/* DetailPen, BlockPen */
    NULL,			/* viewmode LORES */
    CUSTOMSCREEN,		/* type */
    &Topaz80,			/* Font (default) */
#ifdef IEEEDP
    (UBYTE *) "DP Mandelbrot Construction Set 1.3"
#else
    (UBYTE *) "FFP Mandelbrot Construction Set 1.3"
#endif
};

struct NewWindow MainNWindow =
{
    0, 0, 0, 0, 		/* LeftEdge, TopEdge, Width, Height */
    2, 1,			/* DetailPen, BlockPen */
    CLOSEWINDOW | MENUPICK | MOUSEBUTTONS | VANILLAKEY |
    SIZEVERIFY /* | MENUVERIFY */ ,
    WINDOWCLOSE | ACTIVATE | WINDOWSIZING | WINDOWDRAG |
    WINDOWDEPTH | NOCAREREFRESH | SMART_REFRESH | GIMMEZEROZERO,
    NULL,			/* FirstGadget */
    NULL,			/* default CheckMark */
    (UBYTE *) "Mandelbrot Construction Window", /* Title */
    NULL,			/* Screen */
    NULL,			/* BitMap */
    60, 25,			/* MinWidth, MinHeight */
    -1, -1,			/* MaxWidth, MaxHeight */
    CUSTOMSCREEN		/* Screen type */
};

struct BorderInfo borderinfo;

struct IntuiText PositiveText = {
    AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
    AUTOLEFTEDGE, AUTOTOPEDGE, NULL, (UBYTE *) "Continue", NULL
},

		NegativeText = {
    AUTOFRONTPEN, AUTOBACKPEN, AUTODRAWMODE,
    AUTOLEFTEDGE, AUTOTOPEDGE, &Topaz60, (UBYTE *) "Cancel", NULL
};

STATIC SHORT	XY1[] = {0, 0, 0, 13, 78, 13, 78, 0, 0, 0};
STATIC SHORT	XY2[] = {0, 0, 0, 17, 82, 17, 82, 0, 0, 0};
STATIC struct Border border[] = {
    {0, 0, 2, 0, JAM1, 5, XY1, &border[1]},
    {-2, -2, 3, 0, JAM1, 5, XY2, NULL}
};
struct Gadget	NegativeGadget = {
    NULL, -100, -20, 79, 14,	/* next, LTWH */
    GADGHCOMP | GRELBOTTOM | GRELRIGHT,
    RELVERIFY | ENDGADGET,
    BOOLGADGET | REQGADGET,
    (APTR) & border[0], NULL,
    &NegativeText, 0, NULL,
    NEGGADGETID, NULL
};
struct Gadget	PositiveGadget = {
    &NegativeGadget, 20, -20, 79, 14,	/* next, LTWH */
    GADGHCOMP | GRELBOTTOM,
    RELVERIFY | ENDGADGET,
    BOOLGADGET | REQGADGET,
    (APTR) & border[0], NULL,
    &PositiveText, 0, NULL,
    POSGADGETID, NULL
};

UBYTE		PenTable[MAXDEPTH];
unsigned	PenTableMode = MODULO;
short		RangeWidth = 2;

struct Screen  *MandelScreen = NULL;	/* Pointer for OpenScreen return
					 * value */
struct Window  *MainWindow = NULL;	/* Idem for OpenWindow */
struct Window  *XYwindow;	/* The X/Y window */
extern struct Window *ColorWindow;	/* Palette */


USHORT		WBWidth = 0;	/* Preferred size of our screen */
USHORT		WBHeight = 0;	/* in Hires/noninterlace values */

int		(*DepthFunc) () = ZQuadMinC;
UBYTE		FunctionNr = 0;

void		(*IPlotFunc) () = None;
UBYTE		IPlotNr = 0;

void		(*EPlotFunc) () = PlotIterationCount;
UBYTE		EPlotNr = 0;

double		LeftEdge = -0.800,	/* Left edge of the picture */
		RightEdge = 2.100,
		TopEdge = 1.200,
		BottomEdge = -1.200;

double		CXStep, 	/* Stepsize through the complex plane */
		CYStep;
int		PixelStep = 1,	/* Stepsize on the screen */
		MaxDepth = 100; /* Maximum iteration count */

int NumColors;			/* Number of colors on the screen */

unsigned short	FrameX1,
		FrameX2,
		FrameY1,
		FrameY2;

short		MouseStatus = NOTFRAMING;	/* Where we are in the
						 * process of selecting a
						 * frame */

bool		finished = FALSE;	/* TRUE to stop the program */
bool		Saved = TRUE;	/* Indicates the picture has been SAVEd */
bool		NameValid = FALSE;	/* Indicates the file name is
					 * valid */
bool		StillDrawing = FALSE;	/* Are we still drawing ? */

FILE	       *BatchFILE;	/* Current batch file */
int		DrawSigBit = -1;/* Signal when drawing is finished */
long		DrawSigMask;

#ifdef AREXX

/*
 *  And here is some AREXX stuff. Not very complicated, thanks to
 *  MinRexx by Tomas Rokicki (Radical Eye Software).
 */

long		RexxMask;	/* Wait signal mask */
extern short	BatchWaiting;	/* Halting all Arexx traffic as well */

#endif

/*
 * M A I N   E N T R Y	 P O I N T
 */

main(argc, argv)
int		argc;
char	      **argv;
{
    char	   *Main();
    extern struct SignalSemaphore DrawSemaphore;

    /*
     * Before we do anything, do an operation which requires the floating
     * point library. So if it is not available, we abort before we need
     * to clean up anything. Note that CXStep is not initialized at this
     * moment.
     */

    CXStep = 1.0;
    CXStep = CXStep * 2.0;

    /*
     * Make sure we aren't interrupted by any user pressing ^C while we
     * are doing I/O.
     */
    Enable_Abort = 0;

    /*
     * Open each of the libraries and check for a NULL return, which
     * indicates unavailability.
     */

    IntuitionBase = (struct IntuitionBase *)
	OpenLibrary("intuition.library", LIBRARY_VERSION);
    if (IntuitionBase == NULL)
	MyExit("Can't open Intuition V1.2 or newer!");

    GfxBase = (struct GfxBase *)
	OpenLibrary("graphics.library", LIBRARY_VERSION);
    if (GfxBase == NULL)
	MyExit("Can't open Gfx V1.2 or newer!");

    LayersBase = (struct LayersBase *)
	OpenLibrary("layers.library", LIBRARY_VERSION);
    if (LayersBase == NULL)
	MyExit("Can't open Layers V1.2 or newer!");

    DrawSigBit = AllocSignal(-1L);
    if (DrawSigBit == -1)
	MyExit("Can't allocate signal!");
    DrawSigMask = 1L << DrawSigBit;
    InitSemaphore(&DrawSemaphore);
    MandelTask = FindTask(NULL);

#ifdef AREXX
/*
 *   For rexx, we open up a Rexx port, and Batch() will send out
 *   the first command, if there was one.
 */
    RexxMask = upRexxPort("MANDEL", NULL, "mand", NULL) ;
#endif
    if (InitDisplay((bool) FALSE))
	MyExit("Can't initialise the display properly!");

    CalcCSteps();
    Options(argc, argv);        /* Maybe open the batch */
    Batch();                    /* Check the batch */

    MyExit(Main());
}

char	       *
Main()
{
    register ULONG  Class;	/* IntuiMessage class */
    register USHORT Code;	/* and Code field */
    struct IntuiMessage *message;	/* Expected message pointer */
    struct Window  *window;	/* window related to message */
    long	    signalmask;
    long	    signals;

#ifdef AREXX
/*
 *   If we're working with Rexx, we wait on the Rexx bit as well.
 *   Then, we handle any Rexx messages.
 */
    signalmask = RexxMask | DrawSigMask |
		   (1L << MainWindow->UserPort->mp_SigBit);
#else
    signalmask = DrawSigMask | (1L << MainWindow->UserPort->mp_SigBit);
#endif

again:
    finished = FALSE;
    while (!finished) {
	/*
	 * Wait for message port to become not empty, and extract the
	 * message or, alternatively, for the drawing to finish.
	 */

	signals = Wait(signalmask);
#ifdef AREXX
	if ((signals & RexxMask) && !BatchWaiting) {
	    dispRexxPort();
	}
#endif
	if (signals & DrawSigMask) {    /* drawing finished */
	    Batch();                    /* Check the batch */
#ifdef AREXX
	    Signal(MandelTask, RexxMask);/* Wake up Arexx as well */
#endif
	}
	while (message = (struct IntuiMessage *)
	       GetMsg(MainWindow->UserPort)) {
	    window = message->IDCMPWindow;

	    if (window == ColorWindow) {
		HandleColorWindow(message);
		continue;
	    }

	    Class = message->Class;
	    Code = message->Code;

	    if (Class & ~(MOUSEBUTTONS | INTUITICKS)) {
		ReplyMsg(message);      /* Pointer is not valid anymore */
	    }
	    switch (Class) {
	    case MENUPICK:
		GotMenu(Code);
		break;
	    case MOUSEBUTTONS:
	    case INTUITICKS:
		CheckMouse(message);
		ReplyMsg(message);
		break;
	    case SIZEVERIFY:
		StopFraming();  /* Fall Through to MenuVerify */
	    case MENUVERIFY:
		break;		/* Just make sure there is no half-drawn
				 * frame */
	    case CLOSEWINDOW:
		if (window == MainWindow) {
		    finished = TRUE;
		} else {
		    CloseXYwindow();
		}
		break;
	    case VANILLAKEY:
		if (Code == ('Q' & 0x1F)) {     /* Ctrl-Q; don't check to
						 * be sure */
		    skipto	    finished;
		}
		break;
		/* default: Ignore strange messages */
	    }			/* End Switch The Class Of The Message */
	}			/* End While There Is A Message */
    }				/* End While Not Finished */

    /*
     * So we are finished. And, by the way, are we SURE we are finished
     * alltogether ??
     */

    if (!Sure()) {
	backto		again;
    }
finished:
    CleanupDisplay((bool) TRUE);
    return NULL;		/* Indicate Good Exit */
}				/* End of Main */
