/*
(c) Copyright 1988-1994 Microsoft Corporation.

Microsoft Bars&Pipes Professional Source Code License

This License governs use of the accompanying Software.  
Microsoft hopes you find this Software useful.

You are licensed to do anything you want with the Software. 

In return, we simply require that you agree:

1.      Not to remove any copyright notices from the Software.

2.      That the Software comes "as is", with no warranties.  
        None whatsoever. This means no implied warranty of merchantability or 
        fitness for a particular purpose or any warranty of non-infringement.  
        Also, you must pass this disclaimer on whenever you distribute the Software.

3.      That we will not be liable for any of those types of damages known as indirect, 
        special, consequential, or incidental related to the Software or this License, 
        to the maximum extent the law permits. Also, you must pass this limitation of 
        liability on whenever you distribute the Software.

4.      That if you sue anyone over patents that you think may apply to the Software 
        for that person’s use of the Software, your license to the Software ends automatically.

5.      That the patent rights Microsoft is licensing only apply to the Software, 
        not to any derivatives you make.

6.      That your rights under the License end automatically if you breach this in any way.
*/

#include "/CompilerSpecific.h"
#include <intuition/intuition.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include "tools.h"
#include <proto/layers.h>
#include <proto/exec.h>
#include <proto/intuition.h>
/*#include <proto/graphics.h>*/
#include <dos/dos.h>
#include <string.h>
#include "all.h"

extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;
//extern struct LayersBase *LayersBase;

struct DragData {
    struct BitMap imagebitmap;
    struct BitMap tempbitmap;
    struct BitMap maskbitmap;
    struct Image *image;
    struct BitMap *bitmap;
    struct RastPort *rport;
    unsigned short maxy, maxx;
    long tempa;
    long minterm;
    long outlineterm;
    unsigned long height, width;
};

struct Window *WhichWindow(window,x,y)

struct Window *window;
short x,y;

{
    register struct Layer *layer;
    struct Screen *s = window->WScreen;
    register struct Window *result = 0;
    x = window->LeftEdge + x;
    y = window->TopEdge + y;
    layer = (struct Layer *) WhichLayer(&s->LayerInfo,x,y);
    if (layer) {
        for (result = s->FirstWindow;result;result = result->NextWindow) {
            if (result->RPort->Layer == layer) break;
        }
    }
    return(result);
}

static void drag_init(window,image,drag)

register struct Window *window;
register struct Image *image;
register struct DragInfo *drag;

{
    register short i, index, width, height, depth;
    register struct DragData *dd;
    register struct Screen *screen;
    PLANEPTR planes[8];
    screen = window->WScreen;
    width = image->Width;
    height = image->Height;
    depth = image->Depth;
    dd = (struct DragData *) AllocMem(sizeof(struct DragData),MEMF_PUBLIC);
    if (dd) {
        drag->DD = (long) dd;
        dd->maxx = screen->Width - width;
        dd->maxy = screen->Height - height;
        dd->tempa = (long) AllocMem(200,MEMF_CHIP);
        dd->rport = window->RPort;
        dd->bitmap = &screen->BitMap;
        dd->image = image;
        dd->height = height;
        dd->width = width;
        InitBitMap(&dd->tempbitmap,depth,width,height);
        InitBitMap(&dd->maskbitmap,depth,width,height);
        for (i=0; i < depth;i++) {
            index = (width - 1) / 16;
            index = index + 1;
            index = index * height;
            index = index * i;
            planes[i] = (PLANEPTR) &image->ImageData[index];
        }
        dd->maskbitmap.Planes[0] = (PLANEPTR) AllocRaster(width+1,height+1);
        if (!dd->maskbitmap.Planes[0]) {
            FreeMem((char *)dd,sizeof(*dd));
            drag->DD = 0;
            return;
        }
        for (i=1;i<depth;i++)
            dd->maskbitmap.Planes[i] = dd->maskbitmap.Planes[0];
        for (i=0; i<depth;i++) {
            dd->tempbitmap.Planes[i] = (PLANEPTR) AllocRaster(width+1,height+1);
            if (!dd->tempbitmap.Planes[i]) {
                for (i--;i>=0;i--)
                    FreeRaster(dd->tempbitmap.Planes[i],width+1,height+1);
                FreeRaster(dd->maskbitmap.Planes[0],width+1,height+1);
                FreeMem((char *)dd,sizeof(*dd));
                drag->DD = 0;
                return;
            }
        }
        InitBitMap(&dd->imagebitmap,1,width,height);
        dd->imagebitmap.Planes[0] = planes[0];
        BltBitMap(&dd->imagebitmap,0,0,&dd->maskbitmap,
            0,0,width,height,0xC0,0xFFFF,0);
        for (i=1; i<depth;i++) {
            InitBitMap(&dd->imagebitmap,1,width,height);
            dd->imagebitmap.Planes[0] = planes[i];
            WaitBlit();
            WaitBlit();
            BltBitMap(&dd->imagebitmap,0,0,&dd->maskbitmap,
                0,0,width,height,0xE0,0xFFFF,0);
        }
        InitBitMap(&dd->imagebitmap,depth,width,height);
        for (i=0; i<depth;i++) {
            dd->imagebitmap.Planes[i] = planes[i];
        }
    }
}

static void drag_release(drag)

register struct DragInfo *drag;

{
    register short i, width, height, depth;
    register struct DragData *dd = (struct DragData *) drag->DD;
    if (dd) {
        width = dd->image->Width;
        height = dd->image->Height;
        depth = dd->image->Depth;
        if (dd->tempa) FreeMem((char *)dd->tempa,200);
        FreeRaster(dd->maskbitmap.Planes[0],width+1,height+1);
        for (i=0; i<depth;i++)
            FreeRaster(dd->tempbitmap.Planes[i],width+1,height+1);
        FreeMem((char *)dd,sizeof(*dd));
        drag->DD = 0;
    }
}

static void drag_draw(g,x,y)

register struct DragInfo *g;
register unsigned long x,y;

{
    register struct DragData *dd = (struct DragData *) g->DD;
    register struct Layer *layer = dd->rport->Layer;
    register long xpos = x + layer->bounds.MinX;
    register long ypos = y + layer->bounds.MinY;
    register short mode = g->Flags;
    register long lenx, leny, inx, iny;
    lenx = dd->width;
    leny = dd->height;
    inx = 0;
    iny = 0;
    if (xpos < 0) {
        inx = -xpos;
        lenx += xpos;
        xpos = 0;
    }
    if (ypos < 0) {
        iny = -ypos;
        leny += ypos;
        ypos = 0;
    }
    if (xpos > dd->maxx) lenx = lenx - xpos + dd->maxx;
    if (ypos > dd->maxy) leny = leny - ypos + dd->maxy;
    WaitBlit();
    WaitBlit();
    WaitBlit();
    BltBitMap(dd->bitmap,xpos,ypos,
        &dd->tempbitmap,inx,iny,lenx,leny,0xC0,0xFFFF,dd->tempa);
    if (mode & DRAG_INWINDOW) {
        if (mode & DRAG_OUTLINE) {
            WaitBlit();
            WaitBlit();
            WaitBlit();
            BltBitMapRastPort(&dd->maskbitmap,0,0,dd->rport,x,y,
                dd->width,dd->height,dd->outlineterm);
        }
        WaitBlit();
        WaitBlit();
        WaitBlit();
        BltBitMapRastPort(&dd->imagebitmap,0,0,dd->rport,x,y,
            dd->width,dd->height,dd->minterm);
    }
    else {
        if (mode & DRAG_OUTLINE) {
            WaitBlit();
            WaitBlit();
            WaitBlit();
            BltBitMap(&dd->maskbitmap,inx,iny,dd->bitmap,xpos,ypos,
                lenx,leny,dd->outlineterm,0xFFFF,dd->tempa);
        }
        WaitBlit();
        WaitBlit();
        WaitBlit();
        BltBitMap(&dd->imagebitmap,inx,iny,dd->bitmap,xpos,ypos,
            lenx,leny,dd->minterm,0xFFFF,dd->tempa);
    }
}

static void drag_return(g,x,y)

register struct DragInfo *g;
register unsigned long x,y;

{
    register struct DragData *dd = (struct DragData *) g->DD;
    register struct Layer *layer = dd->rport->Layer;
    register long xpos = x + layer->bounds.MinX;
    register long ypos = y + layer->bounds.MinY;
    register long lenx, leny, inx, iny;
    lenx = dd->width;
    leny = dd->height;
    inx = 0;
    iny = 0;
    if (xpos < 0) {
        inx = -xpos;
        lenx += xpos;
        xpos = 0;
    }
    if (ypos < 0) {
        iny = -ypos;
        leny += ypos;
        ypos = 0;
    }
    if (xpos > dd->maxx) lenx = lenx - xpos + dd->maxx;
    if (ypos > dd->maxy) leny = leny - ypos + dd->maxy;
    if (g->Flags & DRAG_INWINDOW) {
        WaitBlit();
        WaitBlit();
        WaitBlit();
        BltBitMapRastPort(&dd->tempbitmap,0,0,dd->rport,x,y,
            dd->width,dd->height,0xC0);
    }
    else {
        WaitBlit();
        WaitBlit();
        WaitBlit();
        BltBitMap(&dd->tempbitmap,inx,iny,dd->bitmap,xpos,ypos,
            lenx,leny,0xC0,0xFFFF,dd->tempa);
    }
}


char stealdown = 0;

SAVEDS  void DragGadget(window,g)

register struct Window *window;
register struct DragInfo *g;

{
    register struct IntuiMessage *message;
    register struct Gadget *gadget = g->Gadget;
    register struct DragData *dd;
    register long x,y,oldx,oldy,class,code,xoffset,yoffset;
    register long oldflags = window->IDCMPFlags;
    short mode = g->Flags;
    short xmin = g->LeftEdge;
    short ymin = g->TopEdge;
    short xmax = g->RightEdge;
    short ymax = g->BottomEdge;
    register short flags = gadget->Flags;
    register long reportmouse = window->Flags & REPORTMOUSE;
    /* geta4(); */
    window->Flags |= REPORTMOUSE;
    ModifyIDCMP(window,MOUSEBUTTONS|MOUSEMOVE|GADGETUP|INTUITICKS);
    stealdown = 1;
    if ((gadget->Flags & GADGIMAGE) && gadget->GadgetRender) {
        if ((flags & GADGHIMAGE) && gadget->SelectRender)
            drag_init(window,gadget->SelectRender,g);
        else drag_init(window,gadget->GadgetRender,g);
        dd = (struct DragData *) g->DD;
        if (dd) {
            if ((flags & GADGHIMAGE) || (flags & GADGHBOX)) {
                if (mode & DRAG_OUTLINE) {
                    dd->minterm = 0xE0;
                    dd->outlineterm = 0x20;
                }
                else dd->minterm = 0xC0;
            }
            else {
                if (mode & DRAG_OUTLINE) {
                    dd->minterm = 0xB0;
                    dd->outlineterm = 0x80;
                }
                else dd->minterm = 0x30;
            }
            xoffset = window->MouseX - gadget->LeftEdge - dd->image->LeftEdge;
            yoffset = window->MouseY - gadget->TopEdge - dd->image->TopEdge;
/*          oldx = window->MouseX - xoffset; */
            oldy = window->MouseY - yoffset;
            /* purposefully make these different from those below */
            oldx = window->MouseX - xoffset + 1;
            xmin += dd->image->LeftEdge;
            ymin += dd->image->TopEdge;
            xmax += (dd->image->LeftEdge - gadget->Width);
            ymax += (dd->image->TopEdge - gadget->Height);
            if (xmax < xmin) xmax = xmin;
            if (ymax < ymin) ymax = ymin;
            drag_draw(g,oldx,oldy);
            for (;;) {
                do {
                    message = (struct IntuiMessage *)
                        GetMsg(window->UserPort);
                    if (stealdown > 1) break;
                    if (message) {
                        if (message->Class == MOUSEBUTTONS) {
                            break;
                        }
                        if (message->Class == GADGETUP) {
                            break;
                        }
                        ReplyMsg((struct Message *)message);
                    }
                } while (message);
                if (!message)
                    message = (struct IntuiMessage *) GetIntuiMessage(window);
                class = message->Class;
                code = message->Code;
/*              x = message->MouseX - xoffset;
                y = message->MouseY - yoffset;*/
                x = window->MouseX - xoffset;
                y = window->MouseY - yoffset;
                ReplyMsg((struct Message *)message);
                if (x < xmin) x = xmin;
                if (y < ymin) y = ymin;
                if (x > xmax) x = xmax;
                if (y > ymax) y = ymax;
                if ((class == MOUSEMOVE) || (class == INTUITICKS)) {
                    WaitTOF();
                    if ((oldx != x) || (oldy != y)) {
                        drag_return(g,oldx,oldy);
                        drag_draw(g,x,y);
                        oldx = x;
                        oldy = y;
                        g->XPos = x;
                        g->YPos = y;
                    }
                    if (g->UpdateRoutine) (*g->UpdateRoutine)(window,g);
                }
                else if ((class == MOUSEBUTTONS) && (code == SELECTUP)) {
                    break;
                }
                else if (class == GADGETUP) {
                    break;
                }
                if (stealdown > 1) break;
            }
            drag_return(g,oldx,oldy);
            if (mode & DRAG_MOVEGADGET) {
                if ((flags & GADGHIMAGE) && gadget->SelectRender) {
                    drag_release(g);
                    drag_init(window,gadget->GadgetRender,g);
                    dd = (struct DragData *) g->DD;
                }
                WaitBlit();
                WaitBlit();
                WaitBlit();
                /* Draw the flag that you're holding */
                BltBitMapRastPort(&dd->maskbitmap,0,0,dd->rport,
                    gadget->LeftEdge + dd->image->LeftEdge,
                    gadget->TopEdge + dd->image->TopEdge,
                    dd->width,dd->height,0);
                gadget->LeftEdge = x - dd->image->LeftEdge;
                gadget->TopEdge = y - dd->image->TopEdge;
                RefreshGList(gadget,window,0,1);
            }
            g->XPos = x;
            g->YPos = y;
        }
        drag_release(g);
    }
    stealdown = 0;
    if (!reportmouse) window->Flags &= ~REPORTMOUSE;
    ModifyIDCMP(window,oldflags);
}
