/*
 * 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 the functions to handle MandelVroom's
 * Zoom box which is used for navigation around in the complex plane.
 */

#include "mandp.h"

SHORT BoxSizeX = 3, BoxSizeY = 3, DragSize = 3;

static SHORT HotSpotX, HotSpotY;

struct Picture *ZoomedPict;

#define MENUCODE(m,i,s) (SHIFTMENU(m)|SHIFTITEM(i)|SHIFTSUB(s))

/*
 * Picture (project) related commands
 */

ZoomInCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Picture *Pict;

  Pict = (struct Picture *) Msg->IDCMPWindow->UserData;

  switch( Msg->Class ) {

    case GADGETDOWN:
         ZoomedPict = Pict;

    case MENUPICK:
         Pict->ZoomType = ZOOMIN;
         if ( ! (Pict->Flags & SCROLL_HAPPENED)) {
           CloseZoomBox(Pict);
           SetToPointer();
           State = ZOOMINSTATE;
         }
         break;

    case MOUSEBUTTONS:
         if (Msg->Code == SELECTDOWN) {
           if (ZoomedPict->pNode.ln_Type == Pict->pNode.ln_Type) {
             StartZoomBox(ZoomedPict, Pict);
             ResizeZoomCmd(Msg); /* Pass control off to resize routine */
           }
         }
         break;
  }
}

/*
 * StartZoomOut
 */
ZoomOutCmd( Msg )
  struct IntuiMessage *Msg;
{
  register struct Picture *Pict;

  if (Msg->Class == MENUPICK) {
    Pict = CurPict;
  } else {
    Pict = (struct Picture *) Msg->IDCMPWindow->UserData;
  }
  Pict->ZoomType = ZOOMOUT;

  AddHead( &Pict->zList, &Pict->zNode );

  Pict->Flags   |= ZOOM_BOX_OPEN;
  Pict->DrawPict = Pict;

  Pict->NavLeft  = Pict->LeftMarg;
  Pict->NavRight = Pict->CountX + Pict->NavLeft - 1;
  Pict->NavTop   = Pict->TopMarg;
  Pict->NavBot   = Pict->CountY + Pict->NavTop - 1;

  ZoomOnOff( Pict );
}

ResizeZoomCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Window  *Window;
  struct Picture *Pict;

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

  switch( Msg->Class ) {

    case MOUSEBUTTONS:
         switch (Msg->Code) {

           case SELECTDOWN:

                if (State != ZOOMINSTATE)
                  ZoomExtras(ZoomedPict);
                AllocLensTemp( ZoomedPict );
                ModifyIDCMP(Window, Window->IDCMPFlags |  MOUSEMOVE);
                State = RESIZEZOOMSTATE;
                break;

           case SELECTUP:
                ModifyIDCMP(Window, Window->IDCMPFlags & ~MOUSEMOVE);
                FreeLensTemp();
                FinishResize(ZoomedPict);
                State = IDLESTATE;
                break;
         }
         break;

    case MOUSEMOVE:
         StretchZoomBox( ZoomedPict );
         break;
  }
}

ZoomDragCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Window *Window;
  struct Picture *Pict;

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

  switch( Msg->Class ) {

    case MOUSEBUTTONS:
         ZoomExtras( ZoomedPict );            /* draw/undraw the extras */
         switch( Msg->Code ) {

           case SELECTDOWN:                   /* start drag */
                AllocLensTemp( ZoomedPict );
                ModifyIDCMP(Window, Window->IDCMPFlags | MOUSEMOVE);
                State = ZOOMDRAGSTATE;
                break;

           case SELECTUP:                     /* stop slide */
                FreeLensTemp();
                ModifyIDCMP(Window, Window->IDCMPFlags & ~MOUSEMOVE);
                State = IDLESTATE;
                break;
         }
         break;

    case MOUSEMOVE:
         DragZoomBox( ZoomedPict );
         Lens( ZoomedPict );
         break;
  }
}

PropResizeCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Window *Window;
  struct Picture *Pict;

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

  switch( Msg->Class ) {

    case MOUSEBUTTONS:
         ZoomExtras( ZoomedPict );            /* draw/undraw the extras */
         switch( Msg->Code ) {

           case SELECTDOWN:                   /* start resize */
                AllocLensTemp( ZoomedPict );
                StartPropStrech( ZoomedPict );
                ModifyIDCMP(Window, Window->IDCMPFlags | MOUSEMOVE);
                State = PROPRESIZESTATE;
                break;

           case SELECTUP:                     /* stop resize */
                FreeLensTemp();
                ModifyIDCMP(Window, Window->IDCMPFlags & ~MOUSEMOVE);
                State = IDLESTATE;
                break;
         }
         break;

    case MOUSEMOVE:
         PropStretchBox( ZoomedPict );
         Lens( ZoomedPict );
         break;
  }
}

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

    case MENUPICK:
         SetToPointer();
         ZoomedPict = CurPict;
         State = SETJULIASTATE;
         break;

    case GADGETDOWN:
         SetToPointer();
         ZoomedPict = (struct Picture *) Msg->IDCMPWindow->UserData;
         State = SETJULIASTATE;
         break;

    case MOUSEBUTTONS:
         if (Msg->Code == SELECTDOWN) {

           SetJuliaPt(ZoomedPict,
                      (struct Picture *) Msg->IDCMPWindow->UserData);
           State = IDLESTATE;
         }
         break;
  }
}

QueryHeightCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Window *Window;
  struct Picture *Pict;

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

  if (Pict->Flags & SCROLL_HAPPENED)
    return;

  switch( Msg->Class ) {

    case MOUSEBUTTONS:
         switch( Msg->Code ) {
                                              /* start query */
           case SELECTDOWN:
                if (Pict->Counts && !(Pict->Flags & NO_RAM_GENERATE)) {
                  ShowContour( MouseX, MouseY, 1 );
                  ModifyIDCMP(Window, Window->IDCMPFlags | MOUSEMOVE);
                  State = QUERYHEIGHTSTATE;
                }
                break;

           case SELECTUP:                     /* stop query */
                ModifyIDCMP(Window, Window->IDCMPFlags & ~MOUSEMOVE);
                State = IDLESTATE;
                break;
         }
         break;

    case MOUSEMOVE:
         ShowContour( MouseX, MouseY, 0);
         break;
  }
}

static int OldHeight; /* used to detect title change */

ShowContour(MouseX,MouseY,Flag)
  register int MouseX,MouseY;
  register int Flag;
{
  register struct Picture *Pict;

  static char ScreenTitle[80];

  int Height;
  float r, i;

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

  if (Pict == NULL)
    return;

  if (MouseX >= Pict->LeftMarg &&
      MouseY >= Pict->TopMarg  &&
      MouseX <  CurWind->Width  - Pict->RightMarg &&
      MouseY <  CurWind->Height - Pict->BotMarg) {

    Height = HeightPicked(Pict,MouseX,MouseY);
    r = Pict->RealLow + (MouseX - Pict->LeftMarg) * Pict->RealGap;
    i = Pict->ImagLow + (MouseY - Pict->TopMarg) * Pict->ImagGap;

    sprintf(ScreenTitle,"Height %3d r %f i %f",
                         Height, r, i);

    SetWindowTitles( CurWind, (long) -1, ScreenTitle );
  }
}

/*
 * Start Zoom box
 */
StartZoomBox( NavPict, DrawPict )
  register struct Picture *NavPict;
  register struct Picture *DrawPict;
{
  register struct Window  *Window = DrawPict->Window;

  CloseZoomBox( NavPict);

  AddHead( &DrawPict->zList, &NavPict->zNode );

  NavPict->Flags    |= ZOOM_BOX_OPEN;
  NavPict->DrawPict  = DrawPict;

  /* Draw first box */
  NavPict->NavTop   = NavPict->NavBot   = MouseY;
  NavPict->NavLeft  = NavPict->NavRight = MouseX;

  ZoomBox( NavPict );
}

StretchZoomBox( Pict )
  register struct Picture *Pict;
{
  register struct Picture  *DrawPict = Pict->DrawPict;
  register LONG Left = DrawPict->CountX + DrawPict->LeftMarg;
  register LONG Top  = DrawPict->CountY + DrawPict->TopMarg;

  ZoomBox( Pict );

  if ( MouseX < Pict->NavLeft )
    MouseX = Pict->NavLeft;

  if ( MouseY < Pict->NavTop )
    MouseY = Pict->NavTop;

  if ( MouseX > Left )
    MouseX = Left-1;

  if ( MouseY > Top )
    MouseY = Top-1;

  Pict->NavBot   = MouseY;
  Pict->NavRight = MouseX;

  Lens( Pict );

  ZoomBox( Pict );
}

static double ZoomAspectRatio;

StartPropStrech( Pict )
  register struct Picture *Pict;
{
  ZoomAspectRatio = (double) (Pict->NavRight - Pict->NavLeft) /
                   (double) (Pict->NavBot - Pict->NavTop);
}

PropStretchBox( Pict )
  register struct Picture *Pict;
{
  register LONG Left = Pict->LeftMarg;
  register LONG Top  = Pict->DrawPict->CountY + Pict->TopMarg;

  register LONG CenterX = Pict->NavRight - Pict->NavLeft;
  register LONG CenterY = Pict->NavBot - Pict->NavTop;

  LONG NewLeft,NewRight,NewTop;

  ZoomBox( Pict );

  CenterX = Pict->NavLeft + CenterX / 2;
  CenterY = Pict->NavTop + CenterY / 2;

  if ( MouseX > CenterX-4 )  MouseX = CenterX-4;
  if ( MouseY < CenterY+4 )  MouseY = CenterY+4;

#if 0
  if ( MouseX < Left ) MouseX = Left;  /* don't let it flip */
  if ( MouseY > Top )  MouseY = Top;
#endif

  Top = MouseY - CenterY;
  Left = (LONG) ((float) Top * ZoomAspectRatio);

  NewLeft = CenterX - Left;
  NewRight = CenterX + Left;
  NewTop = CenterY - Top;

  if (NewLeft  >= Pict->LeftMarg &&
      NewRight <  Pict->DrawPict->Window->Width - Pict->RightMarg &&
      NewTop   >= Pict->TopMarg ) {

    Pict->NavBot = MouseY;
    Pict->NavTop = NewTop;
    Pict->NavLeft  = NewLeft;
    Pict->NavRight = NewRight;
  }

  Lens( Pict );

  ZoomBox( Pict );
}

#define MINXBOX (8 << XScale)
#define MINYBOX (7 << YScale)

FinishResize( Pict )
  struct Picture *Pict;
{
  register int t;

  if (Pict->NavBot < Pict->NavTop) {
    t            = Pict->NavBot;
    Pict->NavBot = Pict->NavTop;
    Pict->NavTop = t;
  }
  if (Pict->NavRight < Pict->NavLeft) {
    t              = Pict->NavRight;
    Pict->NavRight = Pict->NavLeft;
    Pict->NavLeft  = t;
  }

  ZoomBox( Pict );

  if (Pict->NavRight - Pict->NavLeft < MINXBOX )
    Pict->NavRight = Pict->NavLeft + MINXBOX;

  if (Pict->NavBot - Pict->NavTop < MINYBOX )
    Pict->NavBot = Pict->NavTop + MINYBOX;

  ZoomOnOff( Pict );
}

DragZoomBox( Pict )
  register struct Picture *Pict;
{
  register struct Picture *DrawPict = Pict->DrawPict;
  register LONG Width, Height;

  ZoomBox( Pict );

  Width  = Pict->NavRight - Pict->NavLeft - HotSpotX;
  Height = Pict->NavBot -   Pict->NavTop - HotSpotY;

  if ( MouseY < 1 )
    MouseY = 1;

  if ( MouseX - HotSpotX < DrawPict->LeftMarg )
    MouseX = DrawPict->LeftMarg + HotSpotX;

  if ( MouseY - HotSpotY < DrawPict->TopMarg )
    MouseY = DrawPict->TopMarg + HotSpotY;

  if ( MouseX + Width >= DrawPict->CountX + DrawPict->LeftMarg )
    MouseX = DrawPict->CountX + DrawPict->LeftMarg - Width;

  if ( MouseY + Height >= DrawPict->CountY + DrawPict->TopMarg )
    MouseY = DrawPict->CountY + DrawPict->TopMarg - Height;

  Pict->NavLeft  = MouseX - HotSpotX;
  Pict->NavTop   = MouseY - HotSpotY;

  Pict->NavRight = MouseX + Width;
  Pict->NavBot   = MouseY + Height;

  Lens( Pict );

  ZoomBox( Pict );
}

CloseZoomBox( Pict )
  struct Picture *Pict;
{
  if (Pict) {
    if (Pict->Flags & ZOOM_BOX_OPEN) {
      ZoomOnOff( Pict );
      Remove( &Pict->zNode );
      Pict->Flags &= ~ZOOM_BOX_OPEN;
    }
    Pict->DrawPict = NULL;
  }
}

ClearZoomBox(Pict)
  struct Picture *Pict;
{
  CloseZoomBox( Pict );
  ZoomedPict = NULL;
}

ZoomBox( Pict )
  register struct Picture *Pict;
{
  if ( Pict && Pict->DrawPict && Pict->DrawPict->Window) {

    ObtainSemaphore( &Pict->WindowSemi );
    DrawBox( Pict->DrawPict->Window,
             Pict->NavLeft,  Pict->NavTop,
             Pict->NavRight, Pict->NavBot );
    ReleaseSemaphore( &Pict->WindowSemi );
  }
}

ZoomExtras( Pict )
  register struct Picture *Pict;
{
  if ( Pict && Pict->DrawPict && Pict->DrawPict->Window) {


    ObtainSemaphore( &Pict->WindowSemi );
    DrawExtras( Pict->DrawPict->Window,
                Pict->NavLeft,  Pict->NavTop,
                Pict->NavRight, Pict->NavBot );
    ReleaseSemaphore( &Pict->WindowSemi );
  }
}

ZoomOnOff( Pict )
  register struct Picture *Pict;
{
  ZoomBox( Pict );
  ZoomExtras( Pict );
}

DrawBox( Window, PLeft, PTop, PRight, PBottom)
  struct Window *Window;
  SHORT PTop, PLeft, PBottom, PRight;
{
  register struct RastPort *Rp;
  register LONG Top, Left, Right, Bottom;

  Rp = Window->RPort;

  Top = PTop;
  Left = PLeft;
  Right = PRight;
  Bottom = PBottom;

  SetDrMd(Rp, COMPLEMENT);
  /*
   * Draw the new box
   */
  Move(Rp, Left,  Top   );
  Draw(Rp, Right, Top   );
  Draw(Rp, Right, Bottom);
  Draw(Rp, Left,  Bottom);
  Draw(Rp, Left,  Top+1 );

  SetDrMd(Rp, JAM1);
} /* DrawBox */

DrawExtras( Window, Left, Top, Right, Bottom)
  struct Window *Window;
  SHORT Top, Left, Bottom, Right;
{
  register struct RastPort *Rp = Window->RPort;
  register LONG ResizeTop  = Bottom - ( BoxSizeY << YScale );
  register LONG ResizeLeft = Right  - ( BoxSizeX << XScale );
  register LONG BotDrag = Top + ( DragSize << YScale );

  SetDrMd(Rp, COMPLEMENT);
  /*
   * Draw Normal Resize gadget
   */
  Move(Rp, (long) Right - 1,  ResizeTop );
  Draw(Rp, ResizeLeft, ResizeTop );
  Draw(Rp, ResizeLeft, (long) Bottom - 1);

  /*
   * Draw Proportional Resize gadget
   */
  ResizeLeft = Left + ( BoxSizeX << XScale );

  Move(Rp, (long) Left + 1,  ResizeTop );
  Draw(Rp, ResizeLeft, ResizeTop );
  Draw(Rp, ResizeLeft, (long) Bottom - 1);

  /*
   * Drag bar bar / close gadget separator
   */
  Move(Rp, (long) Left  + 1,  BotDrag );
  Draw(Rp, (long) Right - 1,  BotDrag );

  Move(Rp, (long) Left + (4 << XScale), (long) Top + 1);
  Draw(Rp, (long) Left + (4 << XScale), BotDrag - 1 );

  SetDrMd(Rp, JAM1);
} /* DrawExtras */

/*
 * ZoomIn
 */
ZoomIn( NavPict )
  register struct Picture *NavPict;
{
  register struct Picture *DrawPict = NavPict->DrawPict;
  double left,  top;
  double right, bot;

  double AspectRatio();

  double Gap;

  if (DrawPict) {

    left  = (double) (NavPict->NavLeft  - DrawPict->LeftMarg);
    top   = (double) (NavPict->NavTop   - DrawPict->TopMarg);
    right = (double) (NavPict->NavRight - DrawPict->LeftMarg);
    bot   = (double) (NavPict->NavBot   - DrawPict->TopMarg);

    switch( NavPict->ZoomType ) {
      case  ZOOMIN:

            NavPict->RealHigh = DrawPict->RealLow + DrawPict->RealGap*right;
            NavPict->ImagHigh = DrawPict->ImagLow + DrawPict->ImagGap*bot;
            NavPict->RealLow  = DrawPict->RealLow + DrawPict->RealGap*left;
            NavPict->ImagLow  = DrawPict->ImagLow + DrawPict->ImagGap*top;

            NavPict->Real     = DrawPict->Real;
            NavPict->Imag     = DrawPict->Imag;
            break;

      case  ZOOMOUT:

            Gap = (DrawPict->ImagHigh - DrawPict->ImagLow) / (bot - top);

            NavPict->ImagLow  -= top * Gap;
            NavPict->ImagHigh = NavPict->ImagLow +
                                (double) NavPict->CountY*Gap;

            NavPict->ImagGap = Gap;
            NavPict->RealGap = Gap *= AspectRatio(NavPict);

            NavPict->RealLow  -= left * Gap;
            NavPict->RealHigh = NavPict->RealLow +
                                (double) NavPict->CountX*Gap;
    }

    CloseZoomBox( NavPict );
  }
  CalculateGaps( NavPict );
} /* ZoomIn */

/*
 * Calculate Gaps
 */
CalculateGaps( Pict )
  register struct Picture *Pict;
{
  double AspectRatio();

  Pict->ImagGap = (Pict->ImagHigh - Pict->ImagLow) / (double) Pict->CountY;
  Pict->RealGap = Pict->ImagGap * AspectRatio( Pict );
}

/*
 * Apsect Ratio - IEEE
 */
double
AspectRatio( Pict )
  register struct Picture *Pict;
{
  double aspectratio;

  if (Pict->ViewModes & HIRES)
    if (Pict->ViewModes & INTERLACE)
      aspectratio = 0.88;
    else
      aspectratio = 0.44;
  else
    if (Pict->ViewModes & INTERLACE)
      aspectratio = 1.76;
    else
      aspectratio = 0.88;

  return( aspectratio );
}

/*
 * Simulate Zoom box gadgets
 */
int
CheckPictZoomBox( NavPict )
  register struct Picture  *NavPict;
{
  register struct Picture *DrawPict = NavPict->DrawPict;
  register struct Window  *Window = DrawPict->Window;

  register LONG BoxX;

  BoxX = BoxSizeX << XScale;

  /* is it in the window box? */
  if (MouseX >= NavPict->NavLeft && MouseX <= NavPict->NavRight &&
      MouseY >= NavPict->NavTop  && MouseY <= NavPict->NavBot) {

    /* is it in the top part? */
    if (MouseY <= NavPict->NavTop + (DragSize << YScale) ) {

      /* is it the drag bar? */
      if (MouseX > NavPict->NavLeft + BoxX ) {

        HotSpotX = MouseX - NavPict->NavLeft;
        HotSpotY = MouseY - NavPict->NavTop;
        return(ZOOMDRAGHIT);

      } else {                     /* We got the close gadget */

        return(ZOOMCLOSEHIT);
      }
    } else {

      /* is it the Resize Gadget? */
      if (MouseY >= NavPict->NavBot - ((BoxSizeY + 1) << YScale) ) {

        if ( MouseX > NavPict->NavRight - BoxX ) {

          return(ZOOMRESIZEHIT);

        } else
        if ( MouseX <= NavPict->NavLeft + BoxX ) {

          return(PROPRESIZEHIT);
        }
      }
    }
  }
  return(NOTHINGHIT);
}

SetJuliaPt( ZoomedPict, Pict )
  register struct Picture *ZoomedPict, *Pict;
{
  register LONG x,y;

  if (ZoomedPict && Pict &&
      ZoomedPict->pNode.ln_Type != Pict->pNode.ln_Type) {

    x = MouseX - Pict->LeftMarg;
    y = MouseY - Pict->TopMarg;

    ZoomedPict->Real = Pict->RealLow + x * Pict->RealGap;
    ZoomedPict->Imag = Pict->ImagLow + y * Pict->ImagGap;
  }
}
