/*
 * 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 used to implement MandelVroom's
 * pan command.  It seems to have a bug when resizing pictures slightly
 * smaller.
 */

#include "mandp.h"

static int ScrollHotX, ScrollHotY;

UBYTE ScrollOn = 1;

#ifdef TEMPWINDOW
#define SAFEBLIT

static struct NewWindow NewTemp = {
   0,12,                     /* start position           */
   80,80,                    /* width, height            */
   (UBYTE) 0, (UBYTE) NORMALPEN,
   MOUSEBUTTONS,             /* IDCMP flags              */
                             /* MandWind flags           */
   WINDOWDRAG | 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           */
   };

static struct Window *TempWind;
#endif

ScrollPictCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Window *Window;
  static struct Picture *Pict;

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

  switch( Msg->Class ) {
    case NEWSIZE:
         if (CurPict && (CurPict->Flags & SCROLL_HAPPENED)) {
           MoveClipImage(CurPict);
         }
         break;

    case MENUPICK:
         switch( SUBNUM(Msg->Code) ) {
           case SETSCROLL:
                State = SCROLLPICTSTATE;
                ThrowTask(CurPict);
                if ( ! (CurPict->Flags & SCROLL_HAPPENED)) {

                  if ( AllocScrollTemp(CurPict) == UNSUCCESSFUL ) {
                    return;
                  }
                  SaveInitialImage(CurPict);
                }
                CurPict->Flags |= SCROLL_HAPPENED;
                SetToPointer();
                break;

           case GENSCROLL:
                if (CurPict && CurPict->Flags & SCROLL_HAPPENED) {
                  Generate(CurPict);
                }
                break;

           case CANCELSCROLL:
                ReColor(CurPict);
                break;
         }
         break;

    case MOUSEBUTTONS:
         switch( Msg->Code ) {

           case SELECTDOWN:                   /* start drag */
                if (Pict == NULL || Pict != CurPict) {
                  return;
                }
                ScrollHotX = MouseX - CurPict->ImageLoc.Left;
                ScrollHotY = MouseY - CurPict->ImageLoc.Top;
                Scroll( CurPict);
                ModifyIDCMP(Window, Window->IDCMPFlags | MOUSEMOVE);
                break;

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

    case MOUSEMOVE:
         Scroll( CurPict);
         break;
  }
}

SaveInitialImage(Pict)
  struct Picture *Pict;
{
#ifdef SAFEBLIT
  SafeClipBlit(Pict->Window,  Pict->LeftMarg, Pict->TopMarg,
               TempWind,      0,            0,
                              Pict->CountX, Pict->CountY,
                              0xc0, __LINE__);
#else
  ClipBlit(Pict->Window->RPort, Pict->LeftMarg, Pict->TopMarg,
           &Pict->ScrollRp,           0,            0,
                                Pict->CountX, Pict->CountY,
                                0xc0);
#endif
}

Scroll( Pict )
  struct Picture *Pict;
{
  CalcNewLoc(Pict,MouseX,MouseY); /* figure out location of new image */
  MoveClipImage(Pict);
}

CalcNewLoc(Pict, MouseX, MouseY)
  register struct Picture *Pict;
  int MouseX, MouseY;
{
  /* set new ImageLoc */

  Pict->ImageLoc.Top    = MouseY - ScrollHotY;
  Pict->ImageLoc.Left   = MouseX - ScrollHotX;
  Pict->ImageLoc.Bot    = Pict->ImageLoc.Top  + Pict->CountY;
  Pict->ImageLoc.Right  = Pict->ImageLoc.Left + Pict->CountX;
}

MoveClipImage(Pict)
  register struct Picture  *Pict;
{
  struct Window   *Window;
  struct RastPort *wind_Rp, *tmp_Rp;

  register int POLeft, PORight, POTop, POBot;

  struct Rect  SaveRect;
  struct Rect  DispRect;

  Window = Pict->Window;

  SaveRect = Pict->ClipImage; /* struct assign */

  DispRect.Left  = Pict->LeftMarg;
  DispRect.Top   = Pict->TopMarg;
  DispRect.Right = Window->Width - Pict->RightMarg - 1;
  DispRect.Bot   = Window->Height - Pict->BotMarg - 1;

  if (Pict->ImageLoc.Left  > DispRect.Right ||
      Pict->ImageLoc.Top   > DispRect.Bot   ||
      Pict->ImageLoc.Right < DispRect.Left  ||
      Pict->ImageLoc.Bot   < DispRect.Top   ) {

    return;
  }

  if (Pict->ImageLoc.Left < DispRect.Left) {
    Pict->ClipImage.Left =  DispRect.Left;
    POLeft = -Pict->ImageLoc.Left + Pict->LeftMarg;
  } else {
    Pict->ClipImage.Left =  Pict->ImageLoc.Left;
    POLeft =  0;
  }

  if (Pict->ImageLoc.Top < DispRect.Top) {
    Pict->ClipImage.Top = DispRect.Top;
    POTop = -Pict->ImageLoc.Top + Pict->TopMarg;
  } else {
    Pict->ClipImage.Top = Pict->ImageLoc.Top;
    POTop = 0;
  }

  if (Pict->ImageLoc.Right <= DispRect.Right) {
    Pict->ClipImage.Right = Pict->ImageLoc.Right;
    PORight = Pict->CountX-1;
  } else {
    Pict->ClipImage.Right = DispRect.Right;
    PORight = DispRect.Right - Pict->ImageLoc.Left;
  }

  if (Pict->ImageLoc.Bot <= DispRect.Bot) {
    Pict->ClipImage.Bot = Pict->ImageLoc.Bot;
    POBot = Pict->CountY - 1;
  } else {
    Pict->ClipImage.Bot = DispRect.Bot;
    POBot = DispRect.Bot - Pict->ImageLoc.Top;
  }

  wind_Rp = Window->RPort;

#ifdef TEMPWINDOW
  tmp_Rp = TempWind->RPort;
#else
  tmp_Rp = &Pict->ScrollRp;
#endif

  if (POLeft < PORight && POTop < POBot) {

  /* Now copy temp rastport data to window */

#ifdef SAFEBLIT
    SafeClipBlit(TempWind, POLeft, POTop, Window,
                       Pict->ClipImage.Left, Pict->ClipImage.Top,
                       PORight - POLeft + 1, POBot - POTop + 1, 0xc0,
                       __LINE__);
#else
    ClipBlit(tmp_Rp,   POLeft, POTop, wind_Rp,
                       Pict->ClipImage.Left, Pict->ClipImage.Top,
                       PORight - POLeft + 1, POBot - POTop + 1, 0xc0);
#endif

#if 0
    printf("l %d t %d r %d b %d\n",POLeft, POTop, PORight, POBot);
    printf("width %d height %d\n",
            PORight - POLeft + 1, POBot - POTop + 1);
#endif
  }

  Pict->DataClip.Left  = POLeft;
  Pict->DataClip.Right = PORight;
  Pict->DataClip.Top   = POTop;
  Pict->DataClip.Bot   = POBot;

  /* Undraw the old picture */

  SetAPen(wind_Rp, 0);

  if (Pict->ClipImage.Left > SaveRect.Left) {

    FillCheck(Window, SaveRect.Left, SaveRect.Top,
                      Pict->ClipImage.Left, SaveRect.Bot);
  } else
  if (Pict->ClipImage.Right < SaveRect.Right) {

    FillCheck(Window, Pict->ClipImage.Right, Pict->ClipImage.Top,
                      SaveRect.Right, SaveRect.Bot);
  }

  if (Pict->ClipImage.Top > SaveRect.Top) {

    FillCheck(Window, SaveRect.Left, SaveRect.Top,
                      SaveRect.Right, Pict->ClipImage.Top);
  } else
  if (Pict->ClipImage.Bot < SaveRect.Bot) {

    FillCheck(Window, SaveRect.Left,  Pict->ClipImage.Bot,
                      SaveRect.Right, SaveRect.Bot);
  }
}

FillCheck(Window,src_x,src_y,dst_x,dst_y)
  struct Window *Window;
  int src_x,src_y,dst_x,dst_y;
{
  if (src_x < 0 || src_x > Window->Width) {
#ifdef DEBUG
    printf("Bad Fill src_x %d Width %d\n",
            src_x,Window->Width);
#endif
    return;
  }

  if (src_y < 0 || src_y > Window->Height) {
#ifdef DEBUG
    printf("Bad Fill src_y %d Height %d\n",
            src_y,Window->Height);
#endif
    return;
  }

  if (dst_x < 0 || dst_x > Window->Width) {
#ifdef DEBUG
    printf("Bad Fill dst_x %d Width %d\n",
            dst_x,Window->Width);
#endif
    return;
  }

  if (dst_y < 0 || dst_y > Window->Height) {
#ifdef DEBUG
    printf("Bad Fill dst_y %d Height %d\n",
            dst_y,Window->Height);
#endif
    return;
  }
  if (src_x > dst_x) {
#ifdef DEBUG
    printf("inverted rect fill\n");
#endif
    return;
  }
  if (src_y > dst_y) {
#ifdef DEBUG
    printf("end for end flipped rect fill\n");
#endif
    return;
  }
  RectFill(Window->RPort, src_x,src_y,dst_x,dst_y);
}

AllocScrollTemp( Pict )
  register struct Picture *Pict;
{
  LONG Plane;
  PLANEPTR t;

  extern struct MenuItem ScrollSub[];

  static ScrollInited;

  extern struct NewScreen NewScreen;

  if ( !ScrollOn )
    return;

#ifdef TEMPWINDOW

  NewTemp.Width    = Pict->Window->Width;
  NewTemp.Height   = Pict->Window->Height;
  NewTemp.LeftEdge = NewScreen.Width - NewTemp.Width-2;
  NewTemp.TopEdge  = 0;
  NewTemp.Screen   = screen;
  TempWind         = OpenWindow( &NewTemp );
/*WindowToBack( TempWind );*/
  return( SUCCESSFUL );

#else

  if ( Pict->ScrollRp.BitMap == NULL ) {

    InitRastPort( &Pict->ScrollRp );
    Pict->ScrollRp.BitMap = &Pict->ScrollBitMap;
  }

  InitBitMap( &Pict->ScrollBitMap, (long) NewScreen.Depth,
               Pict->CountX, Pict->CountY);

  for (Plane = 0; Plane < NewScreen.Depth; Plane++) {

    t = (PLANEPTR) AllocRaster(Pict->CountX, Pict->CountY);
    Pict->ScrollBitMap.Planes[Plane] = (PLANEPTR) t;

    if (t == NULL) {
      FreeScrollTemp( Pict );
      return(UNSUCCESSFUL);
    }
  }
  return(SUCCESSFUL);
#endif
}

FreeScrollTemp( Pict )
  struct Picture *Pict;
{
  LONG Plane;

  if (Pict->Flags & SCROLL_HAPPENED) {

#ifdef TEMPWINDOW
    CloseWindow( TempWind );
#else

    for (Plane = 0; Plane < Pict->ScrollBitMap.Depth; Plane++) {

      if (Pict->ScrollBitMap.Planes[Plane]) {

        FreeRaster( (char *) Pict->ScrollBitMap.Planes[Plane],
                    Pict->CountX,Pict->CountY);
        Pict->ScrollBitMap.Planes[Plane] = NULL;
      }
    }
#endif
  }
  ResetScrollRects(Pict);
}

ResetScrollRects(Pict)
  struct Picture *Pict;
{
  Pict->Flags &= ~SCROLL_HAPPENED;

  Pict->ImageLoc.Left   = Pict->LeftMarg;
  Pict->ImageLoc.Top    = Pict->TopMarg;
  Pict->ImageLoc.Right  = Pict->LeftMarg + Pict->CountX - 1;
  Pict->ImageLoc.Bot    = Pict->TopMarg  + Pict->CountY - 1;

  Pict->ClipImage = Pict->ImageLoc; /* struct assignment */
}

#ifdef SAFEBLIT
static
SafeClipBlit( src_Wind, src_x, src_y,
              dst_Wind, dst_x, dst_y,
              width, height, min_terms,
              line_no )

  struct Window *src_Wind, *dst_Wind;
  LONG   src_x, src_y, dst_x, dst_y, width, height, min_terms;
  LONG   line_no;
{

  if (src_x < 0 || src_x > src_Wind->Width) {
    printf("Line %d Bad blit src_x %d Width %d\n",
            line_no,src_x,src_Wind->Width);
    return;
  }

  if (src_y < 0 || src_y > src_Wind->Height) {
    printf("Line %d Bad blit src_y %d Height %d\n",
            line_no, src_y,src_Wind->Height);
    return;
  }

  if (width < 1 || width > src_Wind->Width) {
    printf("Line %d Bad blit width %d\n",line_no, width);
    return;
  }

  if (src_x + width > src_Wind->Width) {
    printf("Line %d Blit outside %d Width %d \n",
            line_no,src_x+width,src_Wind->Width);
    return;
  }

  if (height < 1 || height > src_Wind->Height) {
    printf("Line %d Bad blit height %d\n",line_no,height);
    return;
  }

  if (src_y + height > src_Wind->Height) {
    printf("Line %d Blit outside %d Height %d \n",
            line_no,src_y+height,src_Wind->Height);
    return;
  }

  ClipBlit( src_Wind->RPort, src_x, src_y,
            dst_Wind->RPort, dst_x, dst_y,
            width, height, min_terms);
}
#endif

ScrollComplex(Pict)
  struct Picture *Pict;
{
  int left, top;
  int count_x, count_y;
  SHORT *NewCounts;

  count_x = Pict->Window->Width  - Pict->LeftMarg - Pict->RightMarg;
  count_y = Pict->Window->Height - Pict->BotMarg  - Pict->TopMarg;

  NewCounts = (SHORT *) safeAllocMem(count_x*count_y*sizeof(SHORT),MEMF_CLEAR);

  if (NewCounts == NULL) {
    DispErrMsg("Can't scroll. Out of RAM.",0);
    return;
  }

  ScrollData(Pict, NewCounts, count_x, count_y);

  FreeCounts( Pict );

  left  = Pict->ImageLoc.Left  - Pict->LeftMarg;
  top   = Pict->ImageLoc.Top   - Pict->TopMarg;

  Pict->RealLow  -= Pict->RealGap * left;
  Pict->ImagLow  -= Pict->ImagGap * top;
  Pict->RealHigh  = Pict->RealLow + Pict->RealGap * count_x;
  Pict->ImagHigh  = Pict->ImagLow + Pict->ImagGap * count_y;

  FreeScrollTemp(Pict);

  Pict->Counts = NewCounts;
  Pict->CountX = count_x;
  Pict->CountY = count_y;
  Pict->CountsSize = count_x * count_y * sizeof(*Pict->Counts);
  Pict->CurLine = 0;
}

ScrollData(Pict, NewCounts, width, height)
  register struct Picture *Pict;
  SHORT *NewCounts;
  int width, height;
{
  int sr,sc;
  register SHORT *s_rowp, *d_rowp, *sp, *dp;

  s_rowp = Pict->Counts +  Pict->DataClip.Top * Pict->CountX;
  d_rowp = NewCounts    + (Pict->ClipImage.Top-Pict->TopMarg) * width;

  for (sr = Pict->DataClip.Top; sr < Pict->DataClip.Bot; sr++) {

    dp = d_rowp + Pict->ClipImage.Left - Pict->LeftMarg;
    sp = s_rowp + Pict->DataClip.Left;

    for (sc = Pict->DataClip.Left; sc < Pict->DataClip.Right; sc++) {

      *dp++ = *sp++;
    }
    s_rowp += Pict->CountX;
    d_rowp += width;
  }
}

