/*
 *--------------------------------------------------------------
 *
 * PPCbenoit.c --
 *
 *      A mandelbrot/julia fractal generator for the Amiga PowerPC
 *
 *      (c) 1997 by phase 5 digital products
 *
 *--------------------------------------------------------------
 */

#include <proto/asl.h>
#include <proto/cybergraphics.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/gadtools.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <cybergraphics/cybergraphics.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/types.h>
#include <libraries/configvars.h>
#include <gtlayout.h>
#include <PowerUP/pragmas/ppc_pragmas.h>
#include <PowerUP/clib/ppc_protos.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ppc.h"

#define PORTMASK(port)  (1 << (port)->mp_SigBit)
#define VERSION         1
#define titel           "Benoit V1.9 (20.08.97) © by phase5 digital products"
char                    BenoitVer[] = "$VER: " titel;

STRPTR myCpuTypes[] = {
                        "PPC",
                        "M68k",
                        NULL
};
STRPTR myStyleTypes[] = {
                        "Mandelbrot style",
                        "Julia style",
                        NULL
};
STRPTR myFracTypes[] = {
                        "Mandelbrot",
                        "Julia",
                        "Dragon",
                        "Salamander",
                        "Meteors",
                        "Pearls",
                        "Formula",
                        NULL
};

STRPTR myFracFormulas[] = {
                        "((z^n)+c)",
                        "((z^n)+c)",
                        "((z*(1-z))*c)",
                        "(((z^2)*c)-1)",
                        "((z-(1/z))*c)",
                        "((z-(1/z))*c)",
                        "User defined",
                        NULL
};
ULONG myFracFormStyles[] = {
                        0,      /* Mandelbrot style */
                        1,      /* Julia style */
                        1,
                        1,
                        0,
                        1,
                        NULL
};

enum {
                        CPU_PPC,
                        CPU_M68K
};
enum {
                        SCR_PUB,
                        SCR_CUSTOM
};
enum {
                        L_DUMMY, L_XMIN, L_YMIN, L_PICKMIN, L_XMAX, L_YMAX, L_PICKMAX, L_XSEED, L_YSEED, L_PICKSEED,
                        L_CPU, L_TYPE, L_FORMEL, L_STYLE, L_EXPONENT, L_MAXITER, L_EVENCOL, L_ODDCOL, L_REPEAT,
                        L_CALC, L_LOAD, L_SAVE, L_RESET, L_QUIT
};

extern struct ExecBase  *SysBase;
struct Library          *AslBase;
struct Library          *CyberGfxBase;
struct Library          *GadToolsBase;
struct Library          *GTLayoutBase;
struct Process          *ThisProcess;
struct Screen           *myScreen,
                        *pubScreen,
                        *actScreen;
struct Window           *myWindow,
                        *ctrlWindow;
struct LayoutHandle     *myLayout;
UBYTE                   pubScreenName[80]       = "Workbench";
ULONG                   screenType              = SCR_PUB,
                        width                   = 600,
                        height                  = 450;
double                  xcoord,
                        ycoord;

/* Template */
#define TEMPLATE        "WIDTH/N,HEIGHT/N,SCR=SCREEN/S,PUB=PUBSCREEN/K"
enum                    {T_WIDTH, T_HEIGHT, T_SCR, T_PUB};
LONG                    *Array[4];
struct RDArgs           *Args;

/* Prototype */
ULONG calcmain          (struct ppcIO *ppcIO);

ULONG Layout(struct Screen *myScreen)
{
        if (myLayout = LT_CreateHandleTags(myScreen,
                TAG_DONE)) {
                        LT_New(myLayout, LA_Type, VERTICAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout, LA_Type, HORIZONTAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout, LA_Type, VERTICAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout, LA_Type, VERTICAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                POPUP_KIND,
                                LA_LabelText,           "C_PU:",
                                GTCY_Labels,            myCpuTypes,
                                LA_ID,                  L_CPU,
                                TAG_DONE);
                        LT_New(myLayout, LA_Type, XBAR_KIND, TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                POPUP_KIND,
                                LA_LabelText,           "_Fractal type:",
                                GTCY_Labels,            myFracTypes,
                                LA_ID,                  L_TYPE,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                STRING_KIND,
                                LA_LabelText,           "Formula:",
                                LA_Chars,               18,
                                LA_ID,                  L_FORMEL,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                POPUP_KIND,
                                LA_LabelText,           "Style:",
                                GTCY_Labels,            myStyleTypes,
                                LA_ID,                  L_STYLE,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                SLIDER_KIND,
                                LA_LabelText,           "Expo_nent (n):",
                                GTSL_Min,               2,
                                GTSL_Max,               16,
                                GTSL_Level,             2,
                                LA_ID,                  L_EXPONENT,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                INTEGER_KIND,
                                LA_LabelText,           "_Max iter:",
                                GTIN_Number,            100,
                                GTIN_MaxChars,          5,
                                LA_ID,                  L_MAXITER,
                                TAG_DONE);
                        LT_New(myLayout, LA_Type, XBAR_KIND, TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                SLIDER_KIND,
                                LA_LabelText,           "_Even color:",
                                GTSL_Min,               0,
                                GTSL_Max,               5,
                                GTSL_Level,             0,
                                LA_ID,                  L_EVENCOL,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                SLIDER_KIND,
                                LA_LabelText,           "_Odd color:",
                                GTSL_Min,               0,
                                GTSL_Max,               5,
                                GTSL_Level,             0,
                                LA_ID,                  L_ODDCOL,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                INTEGER_KIND,
                                LA_LabelText,           "Color _repeat:",
                                GTIN_Number,            100,
                                GTIN_MaxChars,          10,
                                LA_ID,                  L_REPEAT,
                                TAG_DONE);
                        LT_EndGroup(myLayout);
                        LT_EndGroup(myLayout);
                        LT_New(myLayout, LA_Type, YBAR_KIND, TAG_DONE);
                        LT_New(myLayout, LA_Type, VERTICAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout, LA_Type, HORIZONTAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout, LA_Type, VERTICAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                STRING_KIND,
                                LA_LabelText,           "x min:",
                                LA_Chars,               18,
                                LA_ID,                  L_XMIN,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                STRING_KIND,
                                LA_LabelText,           "y min:",
                                LA_Chars,               18,
                                LA_ID,                  L_YMIN,
                                TAG_DONE);
                        LT_EndGroup(myLayout);
                        LT_New(myLayout,
                                LA_Type,                BUTTON_KIND,
                                LA_LabelText,           "Pick",
                                LA_ID,                  L_PICKMIN,
                                TAG_DONE);
                        LT_EndGroup(myLayout);
                        LT_New(myLayout, LA_Type, XBAR_KIND, TAG_DONE);
                        LT_New(myLayout, LA_Type, HORIZONTAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout, LA_Type, VERTICAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                STRING_KIND,
                                LA_LabelText,           "x max:",
                                LA_Chars,               18,
                                LA_ID,                  L_XMAX,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                STRING_KIND,
                                LA_LabelText,           "y max:",
                                LA_Chars,               18,
                                LA_ID,                  L_YMAX,
                                TAG_DONE);
                        LT_EndGroup(myLayout);
                        LT_New(myLayout,
                                LA_Type,                BUTTON_KIND,
                                LA_LabelText,           "Pick",
                                LA_ID,                  L_PICKMAX,
                                TAG_DONE);
                        LT_EndGroup(myLayout);
                        LT_New(myLayout, LA_Type, XBAR_KIND, TAG_DONE);
                        LT_New(myLayout, LA_Type, HORIZONTAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout, LA_Type, VERTICAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                STRING_KIND,
                                LA_LabelText,           "x seed:",
                                LA_Chars,               18,
                                LA_ID,                  L_XSEED,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                STRING_KIND,
                                LA_LabelText,           "y seed:",
                                LA_Chars,               18,
                                LA_ID,                  L_YSEED,
                                TAG_DONE);
                        LT_EndGroup(myLayout);
                        LT_New(myLayout,
                                LA_Type,                BUTTON_KIND,
                                LA_LabelText,           "Pick",
                                LA_ID,                  L_PICKSEED,
                                TAG_DONE);
                        LT_EndGroup(myLayout);
                        LT_EndGroup(myLayout);

                        LT_EndGroup(myLayout);
                        LT_New(myLayout, LA_Type, XBAR_KIND, TAG_DONE);
                        LT_New(myLayout, LA_Type, HORIZONTAL_KIND, LAGR_Spread, TRUE, TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                BUTTON_KIND,
                                LA_LabelText,           "_Calculate",
                                LA_ID,                  L_CALC,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                BUTTON_KIND,
                                LA_LabelText,           "_Load coords",
                                LA_ID,                  L_LOAD,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                BUTTON_KIND,
                                LA_LabelText,           "_Save coords",
                                LA_ID,                  L_SAVE,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                BUTTON_KIND,
                                LA_LabelText,           "_Reset",
                                LA_ID,                  L_RESET,
                                TAG_DONE);
                        LT_New(myLayout,
                                LA_Type,                BUTTON_KIND,
                                LA_LabelText,           "_Quit",
                                LA_ID,                  L_QUIT,
                                TAG_DONE);
                        LT_EndGroup(myLayout);
                        LT_EndGroup(myLayout);

                        if (ctrlWindow = LT_Build(myLayout,
                                LAWN_Left,              0,
                                LAWN_Top,               myScreen->BarHeight + 1,
                                LAWN_Zoom,              TRUE,
                                LAWN_Title,             (ULONG) "Benoit status window",
                                LAWN_IDCMP,             IDCMP_CLOSEWINDOW | IDCMP_GADGETDOWN | IDCMP_GADGETUP,
                                WA_Flags,               WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_ACTIVATE | WFLG_SIZEBBOTTOM | RMBTRAP,
                                WA_AutoAdjust,          TRUE,
                                WA_CustomScreen,        (ULONG) myScreen,
                                TAG_DONE)) {
                                        return TRUE;
                        }
        }

        return FALSE;
}

VOID SetLayout(struct ppcIO *ppcIO)
{
        UBYTE   buff[80];

        LT_SetAttributes(myLayout, L_TYPE,     GTCY_Active, ppcIO->type,     TAG_DONE);
        if (ppcIO->type == TYPE_FORMULA) {
                LT_SetAttributes(myLayout, L_FORMEL, GTST_String, ppcIO->formel, TAG_DONE);
                LT_SetAttributes(myLayout, L_STYLE,  GTCY_Active, ppcIO->style,  TAG_DONE);
        } else {
                LT_SetAttributes(myLayout, L_FORMEL, GTST_String, myFracFormulas[ppcIO->type],   TAG_DONE);
                LT_SetAttributes(myLayout, L_STYLE,  GTCY_Active, myFracFormStyles[ppcIO->type], TAG_DONE);
        }
        LT_SetAttributes(myLayout, L_EXPONENT, GTSL_Level,  ppcIO->exponent, TAG_DONE);
        LT_SetAttributes(myLayout, L_MAXITER,  GTIN_Number, ppcIO->maxiter,  TAG_DONE);
        LT_SetAttributes(myLayout, L_EVENCOL,  GTSL_Level,  ppcIO->evencol,  TAG_DONE);
        LT_SetAttributes(myLayout, L_ODDCOL,   GTSL_Level,  ppcIO->oddcol,   TAG_DONE);
        LT_SetAttributes(myLayout, L_REPEAT,   GTIN_Number, ppcIO->repeat,   TAG_DONE);
        sprintf(buff, "% .17f", ppcIO->xmin);
        LT_SetAttributes(myLayout, L_XMIN, GTST_String, buff, TAG_DONE);
        sprintf(buff, "% .17f", ppcIO->ymin);
        LT_SetAttributes(myLayout, L_YMIN, GTST_String, buff, TAG_DONE);
        sprintf(buff, "% .17f", ppcIO->xmax);
        LT_SetAttributes(myLayout, L_XMAX, GTST_String, buff, TAG_DONE);
        sprintf(buff, "% .17f", ppcIO->ymax);
        LT_SetAttributes(myLayout, L_YMAX, GTST_String, buff, TAG_DONE);
        sprintf(buff, "% .17f", ppcIO->xseed);
        LT_SetAttributes(myLayout, L_XSEED, GTST_String, buff, TAG_DONE);
        sprintf(buff, "% .17f", ppcIO->yseed);
        LT_SetAttributes(myLayout, L_YSEED, GTST_String, buff, TAG_DONE);
}

VOID GetLayout(struct ppcIO *ppcIO)
{
        ppcIO->xmin     = atof((UBYTE *) LT_GetAttributes(myLayout, L_XMIN,  TAG_DONE));
        ppcIO->ymin     = atof((UBYTE *) LT_GetAttributes(myLayout, L_YMIN,  TAG_DONE));
        ppcIO->xmax     = atof((UBYTE *) LT_GetAttributes(myLayout, L_XMAX,  TAG_DONE));
        ppcIO->ymax     = atof((UBYTE *) LT_GetAttributes(myLayout, L_YMAX,  TAG_DONE));
        ppcIO->xseed    = atof((UBYTE *) LT_GetAttributes(myLayout, L_XSEED, TAG_DONE));
        ppcIO->yseed    = atof((UBYTE *) LT_GetAttributes(myLayout, L_YSEED, TAG_DONE));
        ppcIO->type     = LT_GetAttributes(myLayout, L_TYPE,     TAG_DONE);
        ppcIO->formel   = (UBYTE *) LT_GetAttributes(myLayout, L_FORMEL, TAG_DONE);
        ppcIO->style    = LT_GetAttributes(myLayout, L_STYLE,    TAG_DONE);
        ppcIO->exponent = LT_GetAttributes(myLayout, L_EXPONENT, TAG_DONE);
        ppcIO->maxiter  = LT_GetAttributes(myLayout, L_MAXITER,  TAG_DONE);
        ppcIO->evencol  = LT_GetAttributes(myLayout, L_EVENCOL,  TAG_DONE);
        ppcIO->oddcol   = LT_GetAttributes(myLayout, L_ODDCOL,   TAG_DONE);
        ppcIO->repeat   = LT_GetAttributes(myLayout, L_REPEAT,   TAG_DONE);
}

ULONG OpenDisplayWindow(struct Screen *myScreen)
{
        if (myWindow = OpenWindowTags(NULL,
                WA_Left,        (myScreen->Width - width) >> 1,
                WA_Top,         (myScreen->Height - height) >> 1,
                WA_InnerWidth,  width,
                WA_InnerHeight, height,
                WA_Flags,       WFLG_SIZEGADGET | WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_SIZEBBOTTOM | WFLG_GIMMEZEROZERO | WFLG_ACTIVATE | WFLG_RMBTRAP,
                WA_IDCMP,       IDCMP_NEWSIZE | IDCMP_MOUSEBUTTONS | IDCMP_CLOSEWINDOW | IDCMP_RAWKEY,
                WA_AutoAdjust,  TRUE,
                WA_CustomScreen,(ULONG) myScreen,
                WA_MinWidth,    40,
                WA_MinHeight,   30,
                WA_MaxWidth,    STDSCREENWIDTH,
                WA_MaxHeight,   STDSCREENHEIGHT,
                TAG_DONE)) {
                        return (Layout(myScreen));
        }

        return FALSE;
}

ULONG OpenDisplay(ULONG width, ULONG height)
{
        ULONG   modeID, depth;

        if (screenType == SCR_PUB) {
                pubScreen = LockPubScreen(pubScreenName);
                if ((!pubScreen) || (GetBitMapAttr(pubScreen->RastPort.BitMap, BMA_DEPTH) < 15)) {
                        UnlockPubScreen(NULL, pubScreen);
                        screenType = SCR_CUSTOM;
                } else {
                        ScreenToFront(pubScreen);

                        return (OpenDisplayWindow(pubScreen));
                }
        }

        if (screenType == SCR_CUSTOM) {
                modeID = CModeRequestTags(NULL,
                        CYBRMREQ_WinTitle,      (LONG) "Select screenmode:",
                        CYBRMREQ_MinWidth,      width,
                        CYBRMREQ_MinHeight,     height,
                        CYBRMREQ_MinDepth,      16,
                        TAG_DONE);
                if ((modeID == INVALID_ID) || (modeID == FALSE)) {
                        return FALSE;
                }
                if (GetCyberIDAttr(CYBRIDATTR_WIDTH, modeID) < width) {
                        return FALSE;
                }
                if (GetCyberIDAttr(CYBRIDATTR_HEIGHT, modeID) < height) {
                        return FALSE;
                }
                if (depth = GetCyberIDAttr(CYBRIDATTR_DEPTH, modeID) < 16) {
                        return FALSE;
                }
                if (myScreen = OpenScreenTags(NULL,
                        SA_Width,               STDSCREENWIDTH,
                        SA_Height,              STDSCREENHEIGHT,
                        SA_Depth,               depth,
                        SA_DisplayID,           modeID,
                        SA_Title,               (ULONG) titel,
                        SA_SharePens,           TRUE,
                        SA_AutoScroll,          TRUE,
                        SA_LikeWorkbench,       TRUE,
                        TAG_DONE)) {
                                return (OpenDisplayWindow(myScreen));
                }
        }

        return FALSE;
}

VOID CloseDisplay(VOID)
{
        if (myLayout) {
                LT_DeleteHandle(myLayout);
        }
        if (myWindow) {
                CloseWindow(myWindow);
        }
        if (myScreen) {
                CloseScreen(myScreen);
        }
        if (pubScreen) {
                UnlockPubScreen(NULL, pubScreen);
        }
}

VOID DoDisplay(ULONG width, ULONG height, int *buffer)
{
        WritePixelArray(buffer, 0, 0, width << 2, myWindow->RPort, 0, 0, width, height, RECTFMT_ARGB);
}

VOID GetCoords(struct ppcIO *ppcIO)
{
        struct IntuiMessage     *myMsg;
        ULONG                   msgClass, msgCode, valid;
        UWORD                   mx,
                                my;

        while (myMsg = (struct IntuiMessage *) GetMsg(myWindow->UserPort)) {
                ReplyMsg((struct Message *) myMsg);
        }

        SetWindowTitles(myWindow, "Pick coordinate:", titel);

        WaitPort(myWindow->UserPort);
        valid = FALSE;
        while ((myMsg = (struct IntuiMessage *) GetMsg(myWindow->UserPort)) && (!valid)) {
                msgClass = myMsg->Class;
                msgCode  = myMsg->Code;
                mx       = myMsg->MouseX - myWindow->BorderLeft;
                my       = myMsg->MouseY - myWindow->BorderTop;
                ReplyMsg((struct Message *) myMsg);
                if ((msgClass == IDCMP_MOUSEBUTTONS) && (msgCode == SELECTDOWN)) {
                        xcoord = ppcIO->xmin + ((ppcIO->xmax - ppcIO->xmin) * (double) mx / (double) ppcIO->width);
                        ycoord = ppcIO->ymin + ((ppcIO->ymax - ppcIO->ymin) * (double) my / (double) ppcIO->height);
                        valid = TRUE;
                }
        }

        while (myMsg = (struct IntuiMessage *) GetMsg(myWindow->UserPort)) {
                ReplyMsg((struct Message *) myMsg);
        }

        SetWindowTitles(myWindow, "Ok.", titel);
}

VOID RemSpaces(UBYTE *s)
{
        UBYTE   buf[80];
        ULONG   i,
                j;

        j = 0;
        for (i = 0; i < strlen(s); i++) {
                if (s[i] != (UBYTE) 0x20) {
                        buf[j++] = s[i];
                }
        }
        buf[j] = 0;
        strcpy(s, (char *) &buf);
}

ULONG LoadCoords(struct ppcIO *ppcIO)
{
        struct FileRequester    *FileRequester;
        FILE                    *myFile;
        UBYTE                   filename[80];
        UBYTE                   formel[80];
        ULONG                   parse_ok,
                                version,
                                type,
                                style,
                                exponent,
                                maxiter,
                                evencol,
                                oddcol,
                                repeat;
        double                  xmin,
                                ymin,
                                xmax,
                                ymax,
                                xseed,
                                yseed;

        filename[0] = 0;
        if (!(FileRequester = AllocAslRequestTags(ASL_FileRequest, ASLFR_RejectIcons, TRUE, TAG_DONE))) {
                return FALSE;
        }
        if (AslRequestTags(FileRequester,
                ASLFR_TitleText,        "Load coords file",
                ASLFR_PositiveText,     "Load",
                ASLFR_InitialTopEdge,   actScreen->BarHeight + 1,
                ASLFR_InitialHeight,    (actScreen->Height * 3) >> 2,
                ASLFR_InitialDrawer,    "PROGDIR:Coords",
                ASLFR_InitialPattern,   "#?.coords",
                ASLFR_Screen,           (ULONG) actScreen,
                ASLFR_DoPatterns,       TRUE,
                TAG_DONE)) {
                        if (FileRequester->fr_File[0]) {
                                strcpy(filename, FileRequester->fr_Drawer);
                                AddPart(filename, FileRequester->fr_File, 80);
                        } else {
                                return FALSE;
                        }
        }
        if (filename[0] == 0) {
                return FALSE;
        }

        myFile = fopen(filename, "rb");
        fread(filename, 8, 1, myFile);
        filename[8] = 0;
        if (strcmpi(filename, "#Benoit\n")) {
                SetWindowTitles(myWindow, "Not a benoit coords file.", titel);
                parse_ok = FALSE;
        } else {
                parse_ok = TRUE;
                if (fscanf(myFile, "Version:      %ld\n", &version) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "Fractal type: %ld\n", &type) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "Formula:      %s\n",  &formel) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "Style:        %ld\n", &style) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "Exponent:     %ld\n", &exponent) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "Max iter:     %ld\n", &maxiter) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "Even color:   %ld\n", &evencol) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "Odd color:    %ld\n", &oddcol) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "Color repeat: %ld\n", &repeat) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "x min:        %lf\n", &xmin) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "y min:        %lf\n", &ymin) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "x max:        %lf\n", &xmax) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "y max:        %lf\n", &ymax) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "x seed:       %lf\n", &xseed) != 1) {
                        parse_ok = FALSE;
                }
                if (fscanf(myFile, "y seed:       %lf\n", &yseed) != 1) {
                        parse_ok = FALSE;
                }
                ppcIO->type     = type;
                if (ppcIO->type == TYPE_FORMULA) {
                        ppcIO->formel = strdup((char *) &formel);
                        ppcIO->style  = style;
                }
                ppcIO->exponent = exponent;
                ppcIO->maxiter  = maxiter;
                ppcIO->evencol  = evencol;
                ppcIO->oddcol   = oddcol;
                ppcIO->repeat   = repeat;
                ppcIO->xmin     = xmin;
                ppcIO->ymin     = ymin;
                ppcIO->xmax     = xmax;
                ppcIO->ymax     = ymax;
                ppcIO->xseed    = xseed;
                ppcIO->yseed    = yseed;
        }
        fclose(myFile);

        return parse_ok;
}

ULONG SaveCoords(struct ppcIO *ppcIO)
{
        struct FileRequester    *FileRequester;
        FILE                    *myFile;
        UBYTE                   filename[80];

        filename[0] = 0;
        if (!(FileRequester = AllocAslRequestTags(ASL_FileRequest, ASLFR_RejectIcons, TRUE, TAG_DONE)))
                return FALSE;
        if (AslRequestTags(FileRequester,
                ASLFR_TitleText,        "Save coords file",
                ASLFR_PositiveText,     "Save",
                ASLFR_InitialTopEdge,   actScreen->BarHeight + 1,
                ASLFR_InitialHeight,    (actScreen->Height * 3) >> 2,
                ASLFR_InitialDrawer,    "PROGDIR:Coords",
                ASLFR_InitialPattern,   "*.coords",
                ASLFR_Screen,           (ULONG) actScreen,
                ASLFR_DoPatterns,       TRUE,
                ASLFR_DoSaveMode,       TRUE,
                TAG_DONE)) {
                        if (FileRequester->fr_File[0]) {
                                strcpy(filename, FileRequester->fr_Drawer);
                                AddPart(filename, FileRequester->fr_File, 80);
                        } else {
                                return FALSE;
                        }
        }

        if (filename[0] == 0)
                return FALSE;
        if (strstr((char *) &filename, ".coords") == NULL)
                strcat((char *) &filename, ".coords");

        RemSpaces(ppcIO->formel);
        myFile = fopen(filename, "wb");
        fprintf(myFile, "#Benoit\n");
        fprintf(myFile, "Version:      %ld\n", VERSION);
        fprintf(myFile, "Fractal type: %ld\n", ppcIO->type);
        fprintf(myFile, "Formula:      %s\n",  ppcIO->formel);
        fprintf(myFile, "Style:        %ld\n", ppcIO->style);
        fprintf(myFile, "Exponent:     %ld\n", ppcIO->exponent);
        fprintf(myFile, "Max iter:     %ld\n", ppcIO->maxiter);
        fprintf(myFile, "Even color:   %ld\n", ppcIO->evencol);
        fprintf(myFile, "Odd color:    %ld\n", ppcIO->oddcol);
        fprintf(myFile, "Color repeat: %ld\n", ppcIO->repeat);
        fprintf(myFile, "x min:        % .17f\n", ppcIO->xmin);
        fprintf(myFile, "y min:        % .17f\n", ppcIO->ymin);
        fprintf(myFile, "x max:        % .17f\n", ppcIO->xmax);
        fprintf(myFile, "y max:        % .17f\n", ppcIO->ymax);
        fprintf(myFile, "x seed:       % .17f\n", ppcIO->xseed);
        fprintf(myFile, "y seed:       % .17f\n", ppcIO->yseed);
        fclose(myFile);
}

ULONG CheckFormula(struct ppcIO *ppcIO)
{
        ULONG   i, co, ca, cz;
        UBYTE   *form;

        form = ppcIO->formel;
        co = ca = cz = 0;
        for (i = 0; i < strlen(form); i++) {
                switch (form[i]) {
                        case 0x2b:
                        case 0x2d:
                        case 0x2a:
                        case 0x2f:
                        case 0x5e:
                                co++;
                                break;
                        case 0x28:
                                ca++;
                                break;
                        case 0x29:
                                cz++;
                                break;
                }
        }
        if (ca == cz)
                if (co == ca) {
                        SetWindowTitles(myWindow, "Formula accepted", titel);
                        return TRUE;
                } else {
                        SetWindowTitles(myWindow, "Operator not in parenthesis", titel);
                        return FALSE;
                }
        else {
                SetWindowTitles(myWindow, "Unbalanced parenthesis", titel);
                return FALSE;
        }
}

double ReadSysClock(VOID)
{
        struct DateStamp stamp;

        DateStamp(&stamp);

        return (((double) stamp.ds_Minute * 60.0) + ((double) stamp.ds_Tick / 50.0));
}

VOID main(ULONG argc, UBYTE *argv[])
{
        struct Library          *PPCLibBase;
        struct ppcIO            *ppcIO;
        struct IntuiMessage     *myMsg;
        struct Gadget           *msgGadget;
        ULONG                   ppcAvailable,
                                validFormula,
                                signals,
                                msgQualifier,
                                msgClass;
        UWORD                   msgCode,
                                mx,
                                my;
        APTR                    ppcObject = NULL;
        ULONG                   nwidth,
                                nheight,
                                quit,
                                calc;
        double                  aux,
                                auxx,
                                auxy,
                                zoomed,
                                time_start,
                                time_spent;
        char                    *time_str = "";

        ppcAvailable = FALSE;
        if (PPCLibBase = OpenLibrary("ppc.library", 44))
                if (ppcObject = PPCLoadObject("PROGDIR:benoit.elf"))
                        ppcAvailable = TRUE;
        if (ppcAvailable)
                ppcIO = PPCAllocVec(sizeof(struct ppcIO), MEMF_CLEAR | MEMF_PUBLIC);
        else
                ppcIO = AllocVec(sizeof(struct ppcIO), MEMF_CLEAR | MEMF_PUBLIC);

        ThisProcess = (struct Process *) FindTask(NULL);
        if (ThisProcess->pr_CLI) {
                if (Args = ReadArgs(TEMPLATE, (LONG *) &Array[0], NULL)) {
                        if (Array[T_WIDTH])
                                width = *Array[T_WIDTH];
                        if (Array[T_HEIGHT])
                                height = *Array[T_HEIGHT];
                        if (Array[T_SCR])
                                screenType = SCR_CUSTOM;
                        if (Array[T_PUB]) {
                                screenType = SCR_PUB;
                                strcpy(pubScreenName, (UBYTE *) Array[T_PUB]);
                        }
                        FreeArgs(Args);
                }
        }

        if (!(AslBase = (APTR) OpenLibrary("asl.library", 38))) {
                printf("Could not open asl.library v38+\n");
                exit(20);
        }
        if (!(CyberGfxBase = (APTR) OpenLibrary("cybergraphics.library", 40))) {
                printf("Could not open cybergraphics.library v40+\n");
                exit(20);
        }
        if (!(GadToolsBase = (APTR) OpenLibrary("gadtools.library", 38))) {
                printf("Could not open gadtools.library v 38+\n");
                exit(20);
        }
        if (!(GTLayoutBase = (APTR) OpenLibrary("gtlayout.library", 38))) {
                printf("Could not open gtlayout.library v 38+\n");
                exit(20);
        }

        ppcIO->xmin     = -3.0;
        ppcIO->ymin     = -2.1;
        ppcIO->xmax     =  3.0;
        ppcIO->ymax     =  2.1;
        ppcIO->xseed    =  0.0;
        ppcIO->yseed    =  0.0;
        ppcIO->type     = TYPE_MANDELBROT;
        ppcIO->exponent = 2;
        ppcIO->maxiter  = 100;
        ppcIO->evencol  = 0;
        ppcIO->oddcol   = 0;
        ppcIO->repeat   = 100;

        if (!OpenDisplay(width, height)) {
                CloseDisplay();
                exit(20);
        }
        actScreen = (screenType == SCR_PUB) ? pubScreen : myScreen;
        if (!ppcAvailable) {
                LT_SetAttributes(myLayout, L_CPU, GTCY_Active, 1, TAG_DONE);
        }
        SetLayout(ppcIO);

        ppcIO->width  = width;
        ppcIO->height = height;
        if (ppcAvailable)
                ppcIO->buffer = PPCAllocVec(width * height << 2, MEMF_CLEAR | MEMF_PUBLIC);
        else
                ppcIO->buffer = AllocVec(width * height << 2, MEMF_CLEAR | MEMF_PUBLIC);
        validFormula = TRUE;

        quit = FALSE;
        while (!quit) {
                GetLayout(ppcIO);
                if (ppcIO->xmin > ppcIO->xmax) {
                        aux = ppcIO->xmax;
                        ppcIO->xmax = ppcIO->xmin;
                        ppcIO->xmin = aux;
                }
                if (ppcIO->ymin > ppcIO->ymax) {
                        aux = ppcIO->ymax;
                        ppcIO->ymax = ppcIO->ymin;
                        ppcIO->ymin = aux;
                }
                SetLayout(ppcIO);

                LT_LockWindow(ctrlWindow);
                if (ppcAvailable && (LT_GetAttributes(myLayout, L_CPU, TAG_DONE) == CPU_PPC)) {
                        SetWindowTitles(myWindow, "PPC calculating, please wait...", titel);
                        time_start = ReadSysClock();
                        PPCRunObject(ppcObject, ppcIO);
                        time_spent = ReadSysClock() - time_start;
                } else {
                        SetWindowTitles(myWindow, "M68k calculating, please wait...", titel);
                        time_start = ReadSysClock();
                        calcmain(ppcIO);
                        time_spent = ReadSysClock() - time_start;
                }
                LT_UnlockWindow(ctrlWindow);
                zoomed = -log(((ppcIO->xmax - ppcIO->xmin) / 6.0 + (ppcIO->ymax - ppcIO->ymin) / 4.2) / 2.0) / log(2.0);
                if (time_spent < 60)
                        sprintf(time_str, "Time: %d.%02d secs.   Avg. iters: %.2f   Zoomed in: %.2f x", (ULONG) time_spent, (ULONG) (time_spent * 100.0) % 100, ppcIO->average, zoomed);
                else
                        sprintf(time_str, "Time: %d:%02d.%02d mins.   Avg. iters: %.2f   Zoomed in: %.2f x", (ULONG) (time_spent / 60.0), (ULONG) time_spent % 60, (ULONG) (time_spent * 100.0) % 100, ppcIO->average, zoomed);
                SetWindowTitles(myWindow, time_str, titel);
                DoDisplay(width, height, ppcIO->buffer);

                calc = FALSE;
                while ((!calc) && (!quit)) {
                        signals = Wait(PORTMASK(ctrlWindow->UserPort) | PORTMASK(myWindow->UserPort));
                        if (signals & PORTMASK(myWindow->UserPort)) {
                                while (myMsg = (struct IntuiMessage *) GetMsg(myWindow->UserPort)) {
                                        msgClass  = myMsg->Class;
                                        msgCode   = myMsg->Code;
                                        msgGadget = myMsg->IAddress;
                                        mx        = myMsg->MouseX - myWindow->BorderLeft;
                                        my        = myMsg->MouseY - myWindow->BorderTop;
                                        ReplyMsg((struct Message *) myMsg);
                                        switch (msgClass) {
                                                case IDCMP_MOUSEBUTTONS:
                                                case IDCMP_RAWKEY:
                                                        switch (msgCode) {
                                                                case KEYCODE_Q:
                                                                        quit = TRUE;
                                                                        break;
                                                                case SELECTDOWN:
                                                                case KEYCODE_GREATER:
                                                                        GetLayout(ppcIO);
                                                                        auxx = (ppcIO->xmax - ppcIO->xmin) / 4.0;
                                                                        auxy = (ppcIO->ymax - ppcIO->ymin) / 4.0;
                                                                        xcoord = ppcIO->xmin + ((ppcIO->xmax - ppcIO->xmin) / (double) ppcIO->width  * (double) mx);
                                                                        ycoord = ppcIO->ymin + ((ppcIO->ymax - ppcIO->ymin) / (double) ppcIO->height * (double) my);
                                                                        ppcIO->xmin = xcoord - auxx;
                                                                        ppcIO->ymin = ycoord - auxy;
                                                                        ppcIO->xmax = xcoord + auxx;
                                                                        ppcIO->ymax = ycoord + auxy;
                                                                        SetLayout(ppcIO);
                                                                        calc = TRUE;
                                                                        break;
                                                                case MENUDOWN:
                                                                case KEYCODE_LESS:
                                                                        GetLayout(ppcIO);
                                                                        auxx = (ppcIO->xmax - ppcIO->xmin);
                                                                        auxy = (ppcIO->ymax - ppcIO->ymin);
                                                                        xcoord = ppcIO->xmin + ((ppcIO->xmax - ppcIO->xmin) / (double) ppcIO->width  * (double) mx);
                                                                        ycoord = ppcIO->ymin + ((ppcIO->ymax - ppcIO->ymin) / (double) ppcIO->height * (double) my);
                                                                        ppcIO->xmin = xcoord - auxx;
                                                                        ppcIO->ymin = ycoord - auxy;
                                                                        ppcIO->xmax = xcoord + auxx;
                                                                        ppcIO->ymax = ycoord + auxy;
                                                                        SetLayout(ppcIO);
                                                                        calc = TRUE;
                                                                        break;
                                                                case MIDDLEDOWN:
                                                                        GetLayout(ppcIO);
                                                                        xcoord = ppcIO->xmin + ((ppcIO->xmax - ppcIO->xmin) / (double) ppcIO->width  * (double) mx);
                                                                        ycoord = ppcIO->ymin + ((ppcIO->ymax - ppcIO->ymin) / (double) ppcIO->height * (double) my);
                                                                        ppcIO->xseed = xcoord;
                                                                        ppcIO->yseed = ycoord;
                                                                        SetLayout(ppcIO);
                                                                        break;
                                                                case CURSORUP:
                                                                        GetLayout(ppcIO);
                                                                        auxy = (ppcIO->ymax - ppcIO->ymin) / 4.0;
                                                                        ppcIO->ymin -= auxy;
                                                                        ppcIO->ymax -= auxy;
                                                                        SetLayout(ppcIO);
                                                                        calc = TRUE;
                                                                        break;
                                                                case CURSORLEFT:
                                                                        GetLayout(ppcIO);
                                                                        auxx = (ppcIO->xmax - ppcIO->xmin) / 4.0;
                                                                        ppcIO->xmin -= auxx;
                                                                        ppcIO->xmax -= auxx;
                                                                        SetLayout(ppcIO);
                                                                        calc = TRUE;
                                                                        break;
                                                                case CURSORRIGHT:
                                                                        GetLayout(ppcIO);
                                                                        auxx = (ppcIO->xmax - ppcIO->xmin) / 4.0;
                                                                        ppcIO->xmin += auxx;
                                                                        ppcIO->xmax += auxx;
                                                                        SetLayout(ppcIO);
                                                                        calc = TRUE;
                                                                        break;
                                                                case CURSORDOWN:
                                                                        GetLayout(ppcIO);
                                                                        auxy = (ppcIO->ymax - ppcIO->ymin) / 4.0;
                                                                        ppcIO->ymin += auxy;
                                                                        ppcIO->ymax += auxy;
                                                                        SetLayout(ppcIO);
                                                                        calc = TRUE;
                                                                        break;
                                                        }
                                                        break;
                                                case IDCMP_NEWSIZE:
                                                        GetLayout(ppcIO);
                                                        nwidth = myWindow->GZZWidth;
                                                        nheight = myWindow->GZZHeight;
                                                        if ((nwidth != width) || (nheight != height)) {
                                                                ppcIO->width  = width = nwidth;
                                                                ppcIO->height = height = nheight;
                                                                if (ppcAvailable) {
                                                                        if (ppcIO->buffer)
                                                                                PPCFreeVec(ppcIO->buffer);
                                                                        ppcIO->buffer = PPCAllocVec(width * height << 2, MEMF_CLEAR | MEMF_PUBLIC);
                                                                } else {
                                                                        if (ppcIO->buffer)
                                                                                FreeVec(ppcIO->buffer);
                                                                        ppcIO->buffer = AllocVec(width * height << 2, MEMF_CLEAR | MEMF_PUBLIC);
                                                                }
                                                                calc = TRUE;
                                                        }
                                                        break;
                                                case IDCMP_CLOSEWINDOW:
                                                        quit = TRUE;
                                                        break;
                                        }
                                }
                        }
                        if (signals & PORTMASK(ctrlWindow->UserPort)) {
                                while (myMsg = GT_GetIMsg(ctrlWindow->UserPort)) {
                                        msgClass     = myMsg->Class;
                                        msgCode      = myMsg->Code;
                                        msgQualifier = myMsg->Qualifier;
                                        msgGadget    = myMsg->IAddress;
                                        GT_ReplyIMsg(myMsg);
                                        LT_HandleInput(myLayout, msgQualifier, &msgClass, &msgCode, &msgGadget);
                                        switch (msgClass) {
                                                case IDCMP_CLOSEWINDOW:
                                                        quit = TRUE;
                                                        break;
                                               case IDCMP_GADGETUP:
                                                        switch (msgGadget->GadgetID) {
                                                                case L_CPU:
                                                                        if (!ppcAvailable)
                                                                                if (LT_GetAttributes(myLayout, L_CPU, TAG_DONE) == CPU_PPC) {
                                                                                        LT_SetAttributes(myLayout, L_CPU, GTCY_Active, 1, TAG_DONE);
                                                                                        DisplayBeep(actScreen);
                                                                                }
                                                                        break;
                                                                case L_TYPE:
                                                                        ppcIO->type = LT_GetAttributes(myLayout, L_TYPE, TAG_DONE);
                                                                        if (ppcIO->type != TYPE_FORMULA) {
                                                                                LT_SetAttributes(myLayout, L_FORMEL, GTST_String, myFracFormulas[ppcIO->type],   TAG_DONE);
                                                                                LT_SetAttributes(myLayout, L_STYLE,  GTCY_Active, myFracFormStyles[ppcIO->type], TAG_DONE);
                                                                        } else
                                                                                validFormula = TRUE;
                                                                        break;
                                                                case L_FORMEL:
                                                                        LT_SetAttributes(myLayout, L_TYPE, GTCY_Active, TYPE_FORMULA, TAG_DONE);
                                                                        validFormula = CheckFormula(ppcIO);
                                                                        break;
                                                                case L_STYLE:
                                                                        ppcIO->type = LT_GetAttributes(myLayout, L_TYPE, TAG_DONE);
                                                                        if (ppcIO->type != TYPE_FORMULA) {
                                                                                LT_SetAttributes(myLayout, L_FORMEL, GTST_String, myFracFormulas[ppcIO->type],   TAG_DONE);
                                                                                LT_SetAttributes(myLayout, L_STYLE,  GTCY_Active, myFracFormStyles[ppcIO->type], TAG_DONE);
                                                                                DisplayBeep(actScreen);
                                                                        }
                                                                        break;
                                                                case L_PICKMIN:
                                                                        GetLayout(ppcIO);
                                                                        GetCoords(ppcIO);
                                                                        ppcIO->xmin = xcoord;
                                                                        ppcIO->ymin = ycoord;
                                                                        SetLayout(ppcIO);
                                                                        break;
                                                                case L_PICKMAX:
                                                                        GetLayout(ppcIO);
                                                                        GetCoords(ppcIO);
                                                                        ppcIO->xmax = xcoord;
                                                                        ppcIO->ymax = ycoord;
                                                                        SetLayout(ppcIO);
                                                                        break;
                                                                case L_PICKSEED:
                                                                        GetLayout(ppcIO);
                                                                        GetCoords(ppcIO);
                                                                        ppcIO->xseed = xcoord;
                                                                        ppcIO->yseed = ycoord;
                                                                        SetLayout(ppcIO);
                                                                        break;
                                                                case L_CALC:
                                                                        if (validFormula)
                                                                                calc = TRUE;
                                                                        break;
                                                                case L_LOAD:
                                                                        LT_LockWindow(ctrlWindow);
                                                                        if (LoadCoords(ppcIO)) {
                                                                                SetLayout(ppcIO);
                                                                                validFormula = CheckFormula(ppcIO);
                                                                                if (validFormula)
                                                                                        calc = TRUE;
                                                                        }
                                                                        LT_UnlockWindow(ctrlWindow);
                                                                        break;
                                                                case L_SAVE:
                                                                        LT_LockWindow(ctrlWindow);
                                                                        SaveCoords(ppcIO);
                                                                        LT_UnlockWindow(ctrlWindow);
                                                                        break;
                                                                case L_RESET:
                                                                        GetLayout(ppcIO);
                                                                        ppcIO->xmin = -3.0;
                                                                        ppcIO->ymin = -2.1;
                                                                        ppcIO->xmax =  3.0;
                                                                        ppcIO->ymax =  2.1;
                                                                        SetLayout(ppcIO);
                                                                        calc = TRUE;
                                                                        break;
                                                                case L_QUIT:
                                                                        quit = TRUE;
                                                                        break;
                                                        }
                                                        break;
                                        }
                                }
                        }
                }
        }
        CloseDisplay();

        if (ppcAvailable) {
                if (ppcIO->buffer)
                        PPCFreeVec(ppcIO->buffer);
                if (ppcIO)
                        PPCFreeVec(ppcIO);
                PPCUnLoadObject(ppcObject);
                CloseLibrary(PPCLibBase);
        } else {
                if (ppcIO->buffer)
                        FreeVec(ppcIO->buffer);
                if (ppcIO)
                        FreeVec(ppcIO);
        }
        if (GTLayoutBase)
                CloseLibrary((APTR) GTLayoutBase);
        if (GadToolsBase)
                CloseLibrary((APTR) GadToolsBase);
        if (CyberGfxBase)
                CloseLibrary((APTR) CyberGfxBase);
        if (AslBase)
                CloseLibrary((APTR) AslBase);
}
