/*
 * MandelVroom 2.0
 *
 * (c) Copyright 1987,1989  Kevin L. Clague, San Jose, CA
 *
 * All rights reserved.
 *
 * Permission is hereby granted to distribute this program's source
 * executable, and documentation for non-comercial purposes, so long as the
 * copyright notices are not removed from the sources, executable or
 * documentation.  This program may not be distributed for a profit without
 * the express written consent of the author Kevin L. Clague.
 *
 * This program is not in the public domain.
 *
 * Fred Fish is expressly granted permission to distribute this program's
 * source and executable as part of the "Fred Fish freely redistributable
 * Amiga software library."
 *
 * Permission is expressly granted for this program and it's source to be
 * distributed as part of the Amicus Amiga software disks, and the
 * First Amiga User Group's Hot Mix disks.
 *
 * contents: this file contains funtions to create, maintain and delete
 * Mandelbrot and Julia projects (including spawning off child generator
 * tasks.)
 */

#include "mandp.h"

struct NewWindow NewMand = {
   0,12,                     /* start position           */
   80,80,                    /* width, height            */

   (UBYTE) 0, (UBYTE) NORMALPEN,
   NULL,                     /* IDCMP flags */
   /* MandWind flags */
   WINDOWCLOSE | WINDOWDEPTH | WINDOWSIZING | WINDOWDRAG | ACTIVATE |
   REPORTMOUSE | NOCAREREFRESH | SMART_REFRESH,
   (struct Gadget *) NULL,   /* first gadget             */
   (struct Image *) NULL,    /* user checkmark           */
   (UBYTE *) NULL,           /* Title                    */
   (struct Screen *) NULL,   /* pointer to screen        */
   (struct BitMap *) NULL,   /* pointer to superbitmap   */
   20,20,-1,-1,              /* sizing                   */
   CUSTOMSCREEN              /* type of screen           */
   };

LONG TopMarg   = 10;
LONG BotMarg   =  2;
LONG LeftMarg  =  2;
LONG RightMarg;

ULONG CalcTime;

extern LONG TaskPri, MainPri;
extern LONG pSigMask;

extern struct ExecBase *ExecBase;

static struct Picture *GenNPict;

/* Toggle generation of project */

GenerateCmd(Msg)
  struct IntuiMessage *Msg;
{
  switch(Msg->Class) {

    case GADGETDOWN:
         ToggleGen((struct Picture *) Msg->IDCMPWindow->UserData);
         break;

    case MENUPICK:
         switch( SUBNUM(Msg->Code) ) {
           case STARTGEN:

             CurPict->GenState = GENPENDSTATE;
             Generate( CurPict );
             break;

           case STOPGEN:

             ThrowTask(CurPict);
             break;

           case CONTGEN:

             CurPict->GenState = CONTINUESTATE;
             Generate( CurPict );
             break;
         }
         break;
  }
}

ToggleGen( Pict )
  struct Picture *Pict;
{
  if ( Pict->gTask ) {

    ThrowTask( Pict );
  } else {

    Pict->GenState = GENPENDSTATE;
    Generate( Pict );
  }
  SetGenGad( Pict );
}

/*
 * Generate It
 */
Generate( Pict )
  register struct Picture *Pict;
{

  register char *t;

  int GenTask();

  extern struct MenuItem LensSub[];

  if ( Pict->gTask ) {
    return;
  }

  if (Pict->Flags & SCROLL_HAPPENED) {

    /* create a new counts array with unclipped data in it */
    /* adjust the real and imaginary upper left */

    ScrollComplex(Pict);

    if (Pict->Counts == NULL)
      return;

  } else {

    FreeScrollTemp(Pict);

    if (Pict->GenState == CONTINUESTATE ){
      if ( Pict->CurLine < Pict->CountY ) {
        if (Pict->Counts == NULL) return;
      } else {
        return;
      }
    }

    if (Pict->GenState == GENPENDSTATE) {

      InitNewGen( Pict );
      if (Pict->Counts == NULL) return;
    }
  }
  ResetScrollRects(Pict);

  Pict->GenState = GENERATESTATE;

  if ( Pict->MathMode == INTIIGENERATOR &&
      (ExecBase->AttnFlags & (1 << AFB_68020)) == 0 ) {

    Pict->MathMode = INTGENERATOR;
  }

  if ( Pict->MathMode == _81GENERATOR &&
       (ExecBase->AttnFlags & (1 << AFB_68881)) == 0 ) {

    Pict->MathMode = IEEEGENERATOR;
  }

  MakeColorXlate( Pict );

  if (Pict->MathMode == 1) {

    if (OpenFFPLibs() != 0)
      return;
  }

  /* Spawn off the generator as a task */

  DateStamp( Pict->TimeStamp );

  /* GenNPict is pointer to Picture that task needs to generate */

  GenNPict = Pict;

#define KAY (1024L)

#ifdef MULTI
  Pict->gTask = CreateTask( Pict->Title+2, MainPri, GenTask, KAY);

  if ( Pict->gTask == NULL ) {
    DispErrMsg("Could not create task",0);
  }

  SetGenGad( Pict );

  /* Do this so we are sure child is done with GenNPict */

  Wait( pSigMask );

#else
  GenTask();
#endif
}

InitNewGen( Pict )
  register struct Picture *Pict;
{

  if (Pict->Flags & BORDERLESS_PROJ) {

    Pict->LeftMarg  = 0;
    Pict->RightMarg = 0;
    Pict->TopMarg   = 0;
    Pict->BotMarg   = 0;
  }

  /* figure out new picture size */

  Pict->CountX = Pict->Window->Width-(Pict->LeftMarg+Pict->RightMarg);
  Pict->CountY = Pict->Window->Height-(Pict->TopMarg+Pict->BotMarg);

  /* free up old counts memory, get new picture size, get new counts memory
   */

  GetCountsMemory( Pict );

  if (Pict->Counts == NULL) {
    DispErrMsg("Can't generate. Out of RAM!!",0);
    return;
  }

  if (Pict->Flags & NO_RAM_GENERATE ) {
    DispErrMsg("Can't save counts. Out of RAM!!",0);
  }

  /* calculate new picture's coordinates from zoom box */

  ZoomIn( Pict );
  Pict->CurLine = 0;

  /* clear the picture area */

  if (!(Pict->Flags & LENS_DISPLAYED)) {

    SetAPen(  Pict->Window->RPort, NORMALPEN );
    RectFill( Pict->Window->RPort, Pict->LeftMarg, Pict->TopMarg,
              Pict->Window->Width - Pict->RightMarg - 1,
              Pict->Window->Height - Pict->BotMarg - 1);
  }
}

GenTask() {

  register struct Picture *Pict;

  int MandelbrotInt32();
  int MandelbrotInt32II();
  int MandelbrotIEEE();
  int MandelbrotFFP();

  int JuliaInt32();
  int JuliaIEEE();
  int JuliaFFP();

  LONG SPFieee();

#ifdef MULTI
  geta4();
#endif

  Pict = GenNPict;

  /* Signal Parent that we have accessed GenNPict */

  Signal( mTask, pSigMask );

  /* Lower our priority while we generate */

  SetTaskPri( FindTask(0), TaskPri );

  Pict->GenChildState = GENINCOMPLETE;

  if (GetTaskSig( Pict ) == UNSUCCESSFUL) {
    Pict->GenChildState = NOSIGSTATE;

  } else {
    Pict->GenChildState = GENINCOMPLETE;

    if ( Pict->pNode.ln_Type == MANDPICT ) {

      switch ( Pict->MathMode ) {
        case 0:
             MandelbrotInt32( Pict );
             break;

        case 2:
        case 4:
             MandelbrotIEEE( Pict );
             break;

        case 3:
             MandelbrotInt32II( Pict );
             break;

        case 1: {
               /*
                * Constraints for IEEE to FFP conversion
                *
                *  1. SPFieee() expects 32 bit IEEE floats
                *  2. The C language always promotes floats to doubles for
                *     parameter passing.
                *  3. doubles are 64 bits wide
                *
                * Therefore I must do the following:
                *
                *  1. Convert double parameters to float
                *  2. Take the address of the floats and cast them to *ULONG
                *  3. Indirect off the ULONG pointers that are pointing to
                *     32 IEEE format floats (gross eh?) as parameters to FFP
                *     Mandelbrot function.
                */

             /* 32 bit IEEE float variables */

             float StartX_float, StartY_float, GapX_float, GapY_float;

             /* convert 64 bit IEEE variables into 32 bit IEEE variables */

             StartX_float = Pict->RealLow;
             StartY_float = Pict->ImagLow + Pict->CurLine*Pict->RealGap;
             GapX_float   = Pict->RealGap;
             GapY_float   = Pict->ImagGap;

             /*
              * calculate pointers and convert them to pointers to ULONG
              * so that when we indirect off of these and pass the results
              * as parameters they are not promoted to doubles.
              */


             MandelbrotFFP( Pict,
                            *( (ULONG *) &StartX_float ),
                            *( (ULONG *) &StartY_float ),
                            *( (ULONG *) &GapX_float   ),
                            *( (ULONG *) &GapY_float   ));
             }
             break;
      }
    } else {

      switch ( Pict->MathMode ) {

        case 0:
        case 3:
             JuliaInt32( Pict );
             break;

        case 2:
        case 4:
             JuliaIEEE( Pict );
             break;

        case 1: {
             /*
              * Constraints for IEEE to FFP conversion
              *
              *  1. SPFieee() expects 32 bit IEEE float
              *  2. The C language always promotes floats to doubles for
              *     parameter passing.
              *  3. doubles are 64 bits wide
              *
              * Therefore I must do the following:
              *
              *  1. Convert double parameters to float
              *  2. Take the address of the floats and cast them to *ULONG
              *  3. Indirect off the ULONG pointers that are pointing to
              *     32 IEEE format floats (gross eh?) as parameters to FFP
              *     Julia function.
              */

             /* 32 bit IEEE float variables */

             float JuliaX_float, JuliaY_float;
             float StartX, StartY, GapX, GapY;

             /* convert 64 bit IEEE variables into 32 bit IEEE variables */

             JuliaX_float = Pict->Real;
             JuliaY_float = Pict->Imag;
             StartX = Pict->RealLow;
             StartY = Pict->ImagLow;
             GapX   = Pict->RealGap;
             GapY   = Pict->ImagGap;

             /*
              * calculate pointers and convert them to pointers to ULONG
              * so that when we indirect off of these and pass the results
              * as parameters they are not promoted to doubles.
              */

             JuliaFFP( Pict, *( (ULONG *) &JuliaX_float ),
                             *( (ULONG *) &JuliaY_float ),
                             *( (ULONG *) &StartX ),
                             *( (ULONG *) &StartY ),
                             *( (ULONG *) &GapX ),
                             *( (ULONG *) &GapY ) );
             }
             break;
      }
    }
    Pict->GenChildState = GENCOMPLETE;
  }

#ifdef MULTI

  /* Indicate that generation has finished for this task */

  Signal( mTask, mSigMask );               /* signal parent as to change */

  while (Pict->GenState != KILLSTATE) {    /* Wait for task to be removed */

    while (Pict->GenState == GENERATESTATE) {   /* spin 'till parent kills us */
    }

    /* accept any pauses that the parent may send too late */

    if (Pict->GenState == PAUSESTATE) {
      ChildSignal(Pict);  /* signal back so parent doesn't hang */
    }
  }

  /* We've been informed that we will die, so free up resources */

  FreeSignal( Pict->gSigBit );
  Signal( mTask, pSigMask );               /* signal death O.K. */
  Wait( 0L );                              /* stop here forever */
#endif
}

InitMand( Pict )
  register struct Picture *Pict;
{

  Pict->Real   = 0.0;
  Pict->Imag   = 0.0;
}

SetWindowActive( Window, Active )
  register struct Window *Window;
  char Active;
{
  struct Picture *Pict;

  if ( Window ) {

    Pict = (struct Picture *) Window->UserData;

    if ( ! (Pict->Flags & BORDERLESS_PROJ)) {
      Window->Title[0] = Active;
      SetWindowTitles( Window, Window->Title, -1 );
    }
  }
}

/*
 * Free up old memory and get memory for new picture
 */
GetCountsMemory( Pict )
  register struct Picture *Pict;
{
  /* free up old pictures iteration count pile */

  FreeCounts( Pict );

  /* Allocate memory for new picture */

  Pict->CountsSize = Pict->CountX * Pict->CountY * sizeof(SHORT);
  Pict->Counts     = (SHORT *) safeAllocMem(Pict->CountsSize, MEMF_CLEAR );

  if (Pict->Counts == NULL) {

    Pict->CountsSize = Pict->CountX * sizeof(SHORT);
    Pict->Counts     = (SHORT *) safeAllocMem(Pict->CountsSize, MEMF_CLEAR);

    if (Pict->Counts != NULL)
      Pict->Flags     |= NO_RAM_GENERATE;

  } else {

    Pict->Flags     &= ~NO_RAM_GENERATE;
  }
}

FreeCounts( Pict )
  register struct Picture *Pict;
{
  if ( Pict->Counts ) {

    FreeMem( Pict->Counts, Pict->CountsSize );
    Pict->Counts = NULL;
  }
}

struct Gadget *
MakePictGads( Type )
  register int Type;
{
  register struct Gadget    *gadget;
  register struct Gadget *Newgadget;
  register struct Gadget *Firstgadget;
  register struct IntuiText  *Intui;
  register char *str;

  struct Border *Border, *MakeShadow();

  register int i,j,k;

  struct IntuiText *ShadowIntui();

  j = TOPMARG;

  if ( Type == MANDPICT ) {
    k = 4;
  } else {
    k = 5;
  }

  Firstgadget = NULL;

  for (i = 0; i < k; i++) {

    Newgadget = MakeBool(-15,j, 12,12, 0,(PICTTYPE<<WINDTYPEBITS)+i,NULL);

    if (Newgadget) {

      if ( i == 0 ) {

        Firstgadget = Newgadget;

      } else {

        gadget->NextGadget = Newgadget;
      }

      Newgadget->Flags      |= GRELRIGHT;
      Newgadget->Activation |= RIGHTBORDER;

      gadget = Newgadget;

      j += 14;

      switch( i ) {
        case  0:  str = "C"; break;
        case  1:  str = "G"; break;
        case  2:  str = "I"; break;
        case  3:  str = "O"; break;
        case  4:  str = "J"; break;
      }

      Intui = ShadowIntui( str, 3, 3);

      if (Intui == NULL) {
        FreeGadgets( Firstgadget );
      }

      gadget->GadgetText = Intui;

#define NUMPATCHCORNERS 5

      Border = MakeShadow( NORMALPEN, NUMPATCHCORNERS );

      if ( Border ) {
        InitPatch( Border );
        Border->NextBorder = (struct Border *) gadget->GadgetRender;
        gadget->GadgetRender = (APTR) Border;
      }

    } else {

      if (Firstgadget)
        FreeGadgets( Firstgadget );

      return( NULL );
    }
  }
  return( Firstgadget );
}

SetGenGad( Pict )
  register struct Picture *Pict;
{
  register struct Window *Window;
  register struct Gadget *Gadget;
  register struct IntuiText *Intui;
  register char c;
  register int  place;

  if (Pict == NULL)
    return;

  if (Pict->Flags & BORDERLESS_PROJ)
    return;

  Window = Pict->Window;

  if (Window) {

    if ( Pict->gTask )
      c = 'S';
    else
      c = 'G';

    Gadget = Pict->Gadgets->NextGadget;

    place = RemoveGadget( Window, Gadget );

    Intui = Gadget->GadgetText;

    Intui = Intui->NextText;
    Intui->IText[0] = c;

    Intui = Intui->NextText;
    Intui->IText[0] = c;

    AddGadget( Window, Gadget, place );

    RefreshGadgets( Pict->Gadgets, Window, NULL );
  }
}

InitPatch( Border )
  register struct Border *Border;
{
  register SHORT *NewCounts;

  NewCounts = Border->XY;

  *NewCounts++ = 3;       *NewCounts++ = 11;
  *NewCounts++ = 2;       *NewCounts++ = 11;
  *NewCounts++ = 2;       *NewCounts++ = 2;
  *NewCounts++ = 11;      *NewCounts++ = 2;
  *NewCounts++ = 11;      *NewCounts++ = 3;

} /* InitPatch */

/*
 * Open the Picture window
 */
OpenPicture( Pict )
  struct Picture    *Pict;
{
  extern struct Window *OpenMyWind();

  register struct Window    *Window;
  register struct NewWindow *NewWind;

  register LONG width, height;
  register LONG extrax,extray;

  struct Gadget *gadgets, *MakePictGads();

  if ( Pict->Window == NULL ) {

    width  = Pict->CountX;
    height = Pict->CountY;

    NewWind = Pict->NewWind;

    if (Pict->Flags & BORDERLESS_PROJ) {

      NewWind->Flags = ACTIVATE | REPORTMOUSE | NOCAREREFRESH |
                       SMART_REFRESH | BORDERLESS;
      NewWind->Title = NULL;
      Pict->Gadgets  = gadgets = NULL;

      if (Pict->LeftEdge+Pict->CountX+LEFTMARG+RIGHTMARG == screen->Width &&
          Pict->TopEdge +Pict->CountY+TOPMARG+BOTMARG    == screen->Height) {

        Pict->LeftMarg  = LEFTMARG;
        Pict->RightMarg = RIGHTMARG;
        Pict->TopMarg   = TOPMARG;
        Pict->BotMarg   = BOTMARG;
      } else {

        Pict->LeftMarg  = 0;
        Pict->RightMarg = 0;
        Pict->TopMarg   = 0;
        Pict->BotMarg   = 0;
      }
    } else {

      NewWind->Flags = WINDOWCLOSE | WINDOWDEPTH | WINDOWSIZING |
                       WINDOWDRAG | ACTIVATE | REPORTMOUSE | NOCAREREFRESH |
                       SMART_REFRESH;
      NewWind->Title = (UBYTE *) Pict->Title;
      Pict->Gadgets  = gadgets = MakePictGads( Pict->pNode.ln_Type );

      Pict->LeftMarg  = LEFTMARG;
      Pict->RightMarg = RIGHTMARG;
      Pict->TopMarg   = TOPMARG;
      Pict->BotMarg   = BOTMARG;
    }

    width  += Pict->LeftMarg + Pict->RightMarg;
    height += Pict->TopMarg  + Pict->BotMarg;

    extrax = width - screen->Width;
    extray = height - screen->Height;

    if ( extrax > 0 || extray > 0 ) {

      ThrowTask(  Pict);
      FreeCounts( Pict);
      DispErrMsg("Picture too big. Regenerate",0);

      if ( extrax > 0 ) {
        Pict->CountX -= extrax;
        width -= extrax;
      }

      if ( extray > 0 ) {
        Pict->CountY -= extray;
        height -= extray;
      }
    }

    NewWind->LeftEdge = Pict->LeftEdge;
    NewWind->TopEdge  = Pict->TopEdge;

    Window = OpenMyWind(NewWind, screen, gadgets, width, height );

    Pict->Window  = Window;

    if (Window == NULL) {

      DispErrMsg("Can't open picture window",0);
      FreeGadgets( Pict->Gadgets );
      ThrowPict(Pict);
      return( -1 );

    } else {

      CurWind = Window;
      DisplayMsg();

      SetAPen(  Window->RPort, NORMALPEN );

      if (Pict->Flags & BORDERLESS_PROJ) {
        RectFill( Window->RPort, Pict->LeftMarg, Pict->TopMarg,
                  NewWind->Width-Pict->RightMarg-1,
                  NewWind->Height-Pict->BotMarg-1);
      } else {
        RectFill( Window->RPort, Pict->LeftMarg, Pict->TopMarg,
                  NewWind->Width, NewWind->Height);
      }

      Window->UserData = (BYTE *) Pict;

      MoveResize( Pict->Window, &Pict->SizingGadget );
      BorderWindow( Window );

      if (Pict->CycleOn) {
        CreateCycle();
      }
    }
  }
  SetGenGad( Pict );
  return( 0 );
} /* OpenPicture */

MoveResize( Window, RetGadget )
  register struct Window *Window;
  register struct Gadget **RetGadget;
{
  register struct Gadget *Gadget;
  register int place;

  Gadget = Window->FirstGadget;

  while ( Gadget && !(Gadget->GadgetType & SIZING)) {
    Gadget = Gadget->NextGadget;
  }

  if (Gadget) {

    *RetGadget = Gadget;

    place = RemoveGadget( Window, Gadget );

    if (XScale == 0)
      Gadget->LeftEdge -= 2;

    if (YScale == 0)
      Gadget->TopEdge  -= 3;
    else
      Gadget->TopEdge  -= 1;

    AddGadget( Window, Gadget, place );

    RefreshGList( Gadget, Window, NULL, 1);
  }
}

/*
 * Close the Mandelbrot Window
 */
ClosePicture( Pict )
  register struct Picture   *Pict;
{
  register struct Window    *Window;
  register struct NewWindow *NewWindow;

  if ( (Window = Pict->Window) != NULL) {

    KillCycle();
    FreeScrollTemp(Pict);

    Pict->LeftEdge = Window->LeftEdge;
    Pict->TopEdge = Window->TopEdge;

    CloseMyWind(Window, Pict->Gadgets );

    Pict->Window = NULL;
    Pict->Gadgets = NULL;
    Pict->Flags &= ~LENS_DISPLAYED;
  }
}
