/* Auto: make
*/

IMPORT struct SnapRsrc *SnapRsrc;
IMPORT struct Image DiskImage;
IMPORT struct Gadget DiskGad;
IMPORT struct Gadget VProp, HProp;
IMPORT struct PropInfo VInfo, HInfo;
IMPORT struct Image VImage, HImage;
IMPORT struct NewWindow Nw;
IMPORT UBYTE *WindowTitle;

#define MINWIDTH (24 + SnapRsrc->GadOffset + 16)
#define MINHEIGHT 15

LONG xl; /* leftmost x position */
LONG xr; /* rightmost x position */
LONG yt; /* topmost y position */
LONG yb; /* bottommost y position */
LONG mx, my; /* Mouse position in pixels */

#define GfxFrame 4L
IMPORT Point OldFrame[];
IMPORT Point NewFrame[];
IMPORT LONG OFType;        /* Old frame type: ShortFrame/LongFrame */
IMPORT UWORD Ptrn;
IMPORT WORD Pattern[];

IMPORT struct RastPort MyRP;
IMPORT struct BitMap MyBM;
IMPORT struct Screen *theScreen;
struct Layer_Info *LockedLayerInfo;
IMPORT struct RastPort rp;

IMPORT LONGBITS cancelsignal, donesignal, movesignal, clicksignal, timersignal;
IMPORT WORD action;

LONG TitleBarHeight;
LONG ContentsFontHeight;

VOID FixHeights()
{
    struct Screen WBScreen;
    if (GetScreenData((char *)&WBScreen, (LONG)sizeof(struct Screen), WBENCHSCREEN, NULL)) {
          /* Now this is a good practice */
        TitleBarHeight = WBScreen.RastPort.TxHeight+2;
        ContentsFontHeight = WBScreen.Font->ta_YSize;
    } else {
          /* Sorry, but I don't realise how this could fail.
             Well, if we're snapping on another screen and
             WB is closed and it can't be opened by GetScreenData()...
             Anyway, IF this should happen -- Use Topaz 8 */
        TitleBarHeight = 10;
        ContentsFontHeight = 8;
    }
}

/* SnapGfx is the actual graphics snapper.
** It steals the bitmap data from the given rastport
** and puts it in a separate bitmap.
** It also prepares the NewWindow structure according
** to the snapped bitmap.
** The coordinates are assumed to be valid.
*/

struct GfxSnap *SnapGfx(rp)
struct RastPort *rp;
{
    struct GfxSnap *GS;
    LONG i;

    GS = Create(GfxSnap);
    if (!GS) {
        return NULL;
    }

    GS->x = xl;
    GS->y = yt;
    GS->width = xr - xl + 1;
    GS->height = yb - yt + 1;
    GS->depth = rp->BitMap->Depth;
    GS->viewmode = theScreen->ViewPort.Modes;
    GS->pagew = theScreen->Width;
    GS->pageh = theScreen->Height;
    i = (GS->viewmode & HAM ? 16 : 1L << GS->depth);
      /* Copy the color map in case we should need it later */
    while (i--) {
        ULONG col = GetRGB4(theScreen->ViewPort.ColorMap, i);
        GS->rgb[i][0] = ((col >> 8) & 0x0f) << 4;
        GS->rgb[i][1] = ((col >> 4) & 0x0f) << 4;
        GS->rgb[i][2] = ((col >> 0) & 0x0f) << 4;
    }
      /* Set up a nice bitmap */
    InitBitMap(&GS->BM, GS->depth, GS->width, GS->height);
      /* Get a handle on the bitmap */
    InitRastPort(&MyRP);
    MyRP.BitMap = &GS->BM;
    if (!AllocPlanes(&GS->BM, GS->width, GS->height)) {
        FreePlanes(&GS->BM, GS->width, GS->height);
        Kill(GS);
        return NULL;
    }
      /* Copy the selected part of the screen */
    ClipBlit(rp, xl, yt, &MyRP, 0L, 0L, GS->width, GS->height, 0xC0L);
    Nw.LeftEdge = xl;
    Nw.TopEdge = yt;
    Nw.Width = GS->width + 18;                   /* Two pixels on each side */
    Nw.Height = GS->height + TitleBarHeight + 9; /* Bar & scroll bar */
    Nw.Title = WindowTitle;
    Nw.MaxWidth = Nw.Width;
    Nw.MaxHeight = Nw.Height;
    CopyMem((char *)&DiskGad, (char *)&GS->DiskGad,
      (LONG)sizeof(DiskGad) + sizeof(VProp) + sizeof(HProp) +
      sizeof(VInfo) + sizeof(HInfo) + sizeof(VImage) + sizeof(HImage));
    GS->DiskGad.NextGadget = &GS->VProp;
    GS->DiskGad.LeftEdge = -(SnapRsrc->GadOffset+16);
    GS->DiskGad.Height = TitleBarHeight - 2;
    DiskImage.Height = TitleBarHeight;
    GS->VProp.NextGadget = &GS->HProp;
    GS->VProp.TopEdge = TitleBarHeight;
    GS->VProp.Height = -8 - TitleBarHeight;
    GS->VProp.GadgetRender = (APTR)&GS->VImage;
    GS->VProp.SpecialInfo = (APTR)&GS->VInfo;
    GS->HProp.NextGadget = NULL;
    GS->HProp.GadgetRender = (APTR)&GS->HImage;
    GS->HProp.SpecialInfo = (APTR)&GS->HInfo;
    return GS;
}

VOID ExtendGfx()
{
    /* Fix which row we're talking about */
    if (my-yt < yb-my) {       /* Find closest row */
        yt = my;               /* change top row */
    } else {
        yb = my;               /* change bottom row */
    }
    if (mx-xl < xr-mx) {
        xl = mx;
    } else {
        xr = mx;
    }
}

VOID gfx_frame()
{
    NewFrame[0].x = xl;  NewFrame[0].y = yt;
    NewFrame[1].x = xr;  NewFrame[1].y = yt;
    NewFrame[2].x = xr;  NewFrame[2].y = yb;
    NewFrame[3].x = xl;  NewFrame[3].y = yb;
    NewFrame[4].x = xl;  NewFrame[4].y = yt;
    draw_frame(GfxFrame);
}

WORD HandleGfx()
{
    theScreen = WhichScreen();   /* Find out where we are */
    if (!theScreen) {            /* Don't know? Forget it. */
        action = noaction;
        return 0;
    }
      /* Lock everything - find out what happens */
    LockedLayerInfo = &theScreen->LayerInfo;
    LockLayers(LockedLayerInfo);

      /* Get a copy. Don't mess with somebody else's RP. */
    CopyMem((char *)&theScreen->RastPort, (char *)&rp, (long)sizeof(struct RastPort));
    SetDrMd(&rp, COMPLEMENT);

    xl = theScreen->MouseX + theScreen->ViewPort.RasInfo->RxOffset;
    if (xl < 0) {
        xl = 0;
    }
    if (xl >= theScreen->Width) {  /* Check those corners. Check those corners. */
        xl = theScreen->Width - 1;
    }
    yt = theScreen->MouseY + theScreen->ViewPort.RasInfo->RyOffset;
    if (yt < 0) {
        yt = 0;
    }
    if (yt >= theScreen->Height) {
        yt = theScreen->Height - 1;
    }
    xr = xl;
    yb = yt;
    Ptrn = (SnapRsrc->CrawlPtrn ? SnapRsrc->CrawlPtrn : Pattern[UNIT_FRAME]);
    OFType = 0L;
    gfx_frame();

    FOREVER {
        REGISTER LONGBITS sig =
          Wait(movesignal|clicksignal|cancelsignal|donesignal|timersignal);

        if ((sig & timersignal) && (SnapRsrc->CrawlPtrn != 0xffff)) {
            crawl_frame(1L);
        }

        if (sig & movesignal || sig & clicksignal) {
            mx = theScreen->MouseX + theScreen->ViewPort.RasInfo->RxOffset;
            if (mx < 0) {
                mx = 0;
            }
            if (mx>=theScreen->Width) {
                mx = theScreen->Width - 1;
            }
            my = theScreen->MouseY + theScreen->ViewPort.RasInfo->RyOffset;
            if (my < 0) {
                my = 0;
            }
            if (my>=theScreen->Height) {
                my = theScreen->Height - 1;
            }
            ExtendGfx();
            gfx_frame();
        }
        if (sig & cancelsignal) {          /* Cancelled? */
            erase_frame();
            UnlockLayers(LockedLayerInfo);
            return 0;
        }
        if (sig & donesignal) {            /* Finished. Copy gfx. */
            struct GfxSnap *GS;

            erase_frame();
            if (xr < xl + MINWIDTH) {          /* Can't have too small windows */
                xr = xl + MINWIDTH;
            }
            if (xr >= theScreen->Width) {
                xl -= xr - theScreen->Width - 1;
                xr = theScreen->Width - 1;
            }
            if (yb < yt + MINHEIGHT) {
                yb = yt + MINHEIGHT;
            }
            if (yb >= theScreen->Height) {
                yt -= yb - theScreen->Height - 1;
                yb = theScreen->Height - 1;
            }
            FixHeights();
            GS = SnapGfx(&rp); /* Snap! */
            UnlockLayers(LockedLayerInfo);
            if (GS) {
                if (GS->window = opensharedwindow(&Nw)) {
                    GS->window->UserData = (BYTE *)GS;
                      /* Put gfx in our new window */
                    (VOID)AddGList(GS->window, &GS->DiskGad, 0L, 3L, NULL);
                    RefreshGList(&GS->DiskGad, GS->window, NULL, 3L);
                    AdjustSize(GS);
                } else {
                    FreePlanes(&GS->BM, GS->width, GS->height);
                    Kill(GS);
                }
            } else { /* Good question */
            }
            action = noaction;
            return 0;
        }
    }
}
