#line 39 "xpm-w3a.c-nw"
#include <config.h>
#include <w3a.h>
#include <str.h>
#include <Xm/Xm.h>
#include <X11/extensions/shape.h>
#include <X11/xpm.h>

/* Defined in w3a.h: */
/* #define POINT_SELECT 5000                    /* W3A event type */

#define own_widget

typedef struct {                                /* A viewer instance */
    char *buf;                                  /* Image source data */
    size_t buflen;                              /* Length of buf in bytes */
    Widget canvas;                              /* The widget to draw into */
    XpmAttributes attr;                         /* Holds size of image */
    Pixmap image;                               /* Holds decoded image */
    Pixmap mask;                                /* For transparency */
    GC gc;                                      /* GC for image */
} *Buffer;

#define abs(a) ((a) < 0 ? -(a) : (a))
#define min(a, b) ((a) < (b) ? (a) : (b))

typedef struct _Assoc {long id; Buffer b; struct _Assoc *next;} *Assoc;

static Assoc assoclist = NULL;

/* store -- store an ID/Buffer combination */
static void store(Buffer b, long id)
{
    Assoc h;
    new(h); h->id = id; h->b = b; h->next = assoclist; assoclist = h;
}

/* delete -- delete an ID/Buffer combination */
static void delete(long id)
{
    Assoc g, h;
    assert(assoclist);
    if (assoclist->id == id) {
        h = assoclist; assoclist = assoclist->next; dispose(h);
    } else {
        assert(assoclist->next);
        for (h = assoclist; h->next->id != id; h = h->next) assert(h->next);
        g = h->next; h->next = g->next; dispose(g);
    }
}

/* find -- find the Buffer associated with an ID */
static Buffer find(long id)
{
    Assoc h;
    assert(assoclist);
    for (h = assoclist; h->id != id; h = h->next) assert(h->next);
    return h->b;
}




/* parseXPM -- decode XPM image data and create a Pixmap from it */
static int parseXPM(Buffer b)
{
    int status;

    /* Call XPM routine to parse data */
    b->buf[b->buflen] = '\0';
    b->attr.valuemask = 0;
    b->mask = None;
    status = XpmCreatePixmapFromBuffer
        (XtDisplay(b->canvas), RootWindowOfScreen(XtScreen(b->canvas)),
         b->buf, &b->image, &b->mask, &b->attr);
    if (status != XpmSuccess) return status;

    /* Try to set widget size & shape; draw image */
    XtVaSetValues(b->canvas, XtNwidth, b->attr.width,
                  XtNheight, b->attr.height, NULL);
    if (XtIsRealized(b->canvas) && b->mask != None) {
        XShapeCombineMask(XtDisplay(b->canvas), XtWindow(b->canvas),
                          ShapeBounding, 0, 0, b->mask, ShapeSet);
        XFreePixmap(XtDisplay(b->canvas), b->mask);
        b->mask = None;
    }
    if (XtIsRealized(b->canvas))
        XCopyArea(XtDisplay(b->canvas), b->image, XtWindow(b->canvas), b->gc,
                  0, 0, b->attr.width, b->attr.height, 0, 0);

    dispose(b->buf);                            /* Remove source data */
    return status;
}


/* expose -- redraw the exposed area of an XPM image */
static void redraw(Widget w, XEvent *ev, String *parms, Cardinal *nparms)
{
    Buffer b;
    int wd, ht, x, y;

    /* Decode string argument as a Buffer pointer */
    assert(*nparms == 1);
    if (sscanf(parms[0], "%ld", &b) != 1) assert(!"Missing parameter");
    if (! XtIsRealized(w) || b->image == None) return;

    /* Only on the first ExposeEvent: set the window shape */
    if (b->mask != None) {
        XShapeCombineMask(XtDisplay(w), XtWindow(w),
                              ShapeBounding, 0, 0, b->mask, ShapeSet);
            XFreePixmap(XtDisplay(w), b->mask);
            b->mask = None;
    }
    /* Draw image to exposed rectangle */
    x = ev->xexpose.x;
    y = ev->xexpose.y;
    wd = min(b->attr.width, ev->xexpose.width);
    ht = min(b->attr.height, ev->xexpose.height);
    XCopyArea(XtDisplay(w), b->image, XtWindow(w), b->gc, x, y, wd, ht, x, y);
}

/* click -- action for mouse clicks on canvas */
/* ARGSUSED */
static void click(Widget w, XEvent *ev, String *params, Cardinal *nparams)
{
    long b;
    struct {int x, y;} coords;

    assert(*nparams == 1);
    if (sscanf(params[0], "%ld", &b) != 1) assert(!"Missing parameter");
        coords.x = ev->xbutton.x;
        coords.y = ev->xbutton.y;
    W3Aevent(b, POINT_SELECT, &coords);
}


static XtActionsRec actions[] = {
    {"XPM_expose", redraw},
    {"XPM_click", click}
};

static char translations[] =
    "<Expose>: XPM_expose(%ld)\n\
    <Btn1Down>,<Btn1Up>: XPM_click(%ld)";



/* initXPM -- initialize the XPM viewer class */
EXPORT Bool initXPM(char ***mime_types, int *nrtypes, float **prefs)
{
    static char *types[] = {"image/x-xpixmap"};
    static float pref[] = {1.0};

    *mime_types = types;
    *nrtypes = 1;
    *prefs = pref;
    XtAppAddActions(XtWidgetToApplicationContext(W3Atoplevel()),
        actions, XtNumber(actions));
    return TRUE;
}


/* openXPM -- start a new XPM viewer, return its ID */
EXPORT Bool openXPM(const W3ADocumentInfo doc, W3AWindow window, long id)
{
    int screen = XScreenNumberOfScreen(XtScreen(window));
    Display *dpy = XtDisplay(window);
    Buffer b;
    char s[256];

    /* Create buffer for data */
    new(b);
    store(b, id);
    b->buf = NULL;
    b->buflen = 0;
    b->mask = None;
    b->image = None;
#ifdef own_widget
    b->canvas = XtVaCreateManagedWidget
        ("xpm", coreWidgetClass, window,
         XtNwidth, 40, XtNheight, 40, NULL);    /* Initial size != 0 */
#else
    b->canvas = window;
#endif
    b->gc = DefaultGC(dpy, screen);

    /* Install our own event handlers on the window */
    sprintf(s, translations, (long) b, (long) b);
    XtOverrideTranslations(b->canvas, XtParseTranslationTable(s));
    return TRUE;
}


/* writeXPM -- add image data to the buffer, decode when complete */
EXPORT int writeXPM(long id, const char *buf, size_t nbytes)
{
    Buffer b = find(id);

    if (nbytes != 0) {                          /* Add data to buffer */
        renewarray(b->buf, b->buflen + nbytes + 1);
        memcpy(b->buf + b->buflen, buf, nbytes);
        b->buflen += nbytes;
        return nbytes;                          /* All bytes processed */
    } else if (parseXPM(b) == XpmSuccess) {     /* Parse data */
        return 0;                               /* Data OK */
    } else {                                    /* Error while parsing */
        errno = EFORMAT;
        return -1;
    }
}


/* closeXPM -- close an XPM viewer */
EXPORT Bool closeXPM(long id)
{
    Buffer b = find(id);

    XpmFreeAttributes(&b->attr);
    XFreePixmap(XtDisplay(b->canvas), b->mask);
    XFreePixmap(XtDisplay(b->canvas), b->image);
#ifdef own_widget
    XtDestroyWidget(b->canvas);
#else
    /* Remove shape mask */
    XShapeCombineMask(XtDisplay(b->canvas), XtWindow(b->canvas),
                      ShapeBounding, 0, 0, None, ShapeSet);
#endif
    return TRUE;
}


/* eventXPM -- react to events happening elsewhere */
/* ARGSUSED */
EXPORT void eventXPM(long id, long source, long eventtype, void *params)
{
    /* Doesn't handle events */
}


/* infoXPM -- a chance to modify the document info based on the contents */
/* ARGSUSED */
EXPORT Bool infoXPM(long id, W3ADocumentInfo *doc)
{
    /* No modifications */
    return TRUE;
}