/* +-------------------------------------------------------------------+ */
/* | Copyright 1992, 1993, David Koblas (koblas@netcom.com)            | */
/* |                                                                   | */
/* | Permission to use, copy, modify, and to distribute this software  | */
/* | and its documentation for any purpose is hereby granted without   | */
/* | fee, provided that the above copyright notice appear in all       | */
/* | copies and that both that copyright notice and this permission    | */
/* | notice appear in supporting documentation.  There is no           | */
/* | representations about the suitability of this software for        | */
/* | any purpose.  this software is provided "as is" without express   | */
/* | or implied warranty.                                              | */
/* |                                                                   | */
/* +-------------------------------------------------------------------+ */

#include <X11/IntrinsicP.h>
#include "PaintP.h"
#include "xpaint.h"

typedef void	(*func_t)(Widget, void *, XEvent *, OpInfo *);

typedef struct data_s {
	Widget		w;
	func_t		func;
        void            *data;
        int             mask, flag;
	int		surfMask;
        struct data_s   *next;
	OpInfo		*info;
} data_t;

static data_t	*list = NULL;

#if 0
static data_t	**widgetEvent(Widget w, data_t *look)
{
	static data_t	*d[10];
	data_t		*cur = list;
	int		n = 0;

	while (cur != NULL) {
		if (cur->mask == look->mask &&
		    cur->flag == look->flag &&
		    cur->func == look->func &&
		    cur->w    == w) 
			d[n++] = cur;
		cur = cur->next;
	}

	d[n] = NULL;

	return n == 0 ? NULL : d;
}
#endif

static void opHandleEvent(Widget w, XtPointer dataArg, XEvent *event, Boolean *junk)
{
	data_t		*data = (data_t*)dataArg;
	int		snap;
	PaintWidget	paint = (PaintWidget)w;
	OpInfo		*info = data->info;
	static Window	lastWindow  = None;
	static Display	*lastDisplay = None;
	static int	lastType;
	static int	lastX, lastY;
	Boolean		same;
	int		zoom;

#if 0
	XEvent		myEvent;

	memcpy(&myEvent, event, sizeof(XEvent));
	event = &myEvent;
#endif

	/*
	**  Figure out if there is a snap, either by
	**   choice or by zooming
	*/
	if ((info->zoom = GET_ZOOM(paint)) == 0)
		info->zoom = 1;
	if (info->isFat = ((zoom = GET_ZOOM(paint)) > 1))
		snap = GET_ZOOM(paint);
	else if (paint->paint.snapOn)
		snap = paint->paint.snap;
	else
		snap = 1;

	same = (event->xany.window == lastWindow) && 
	       (event->xany.display == lastDisplay) && 
               (event->xany.type == lastType);
	lastWindow  = event->xany.window;
	lastDisplay = event->xany.display;
	lastType    = event->xany.type;

	/*
	**  Snap events to the snap
	*/
	if (event->type == ButtonPress || event->type == ButtonRelease) {
		info->realX = event->xbutton.x;
		info->realY = event->xbutton.y;
	} else if (event->type == MotionNotify) {
		info->realX = event->xmotion.x;
		info->realY = event->xmotion.y;
	}

	if (((event->type == ButtonPress) ||
	     (event->type == ButtonRelease) ||
	     (event->type == MotionNotify)) && snap > 1) {
		switch (event->type) {
		case ButtonPress:
		case ButtonRelease:
			event->xbutton.x = event->xbutton.x - 
						(event->xbutton.x % snap);
			event->xbutton.y = event->xbutton.y - 
						(event->xbutton.y % snap);
			if (same && lastX == event->xbutton.x && lastY == event->xbutton.y)
				return;
			break;
		case MotionNotify:
			event->xmotion.x = event->xmotion.x - 
						(event->xmotion.x % snap);
			event->xmotion.y = event->xmotion.y - 
						(event->xmotion.y % snap);
			if (same && lastX == event->xmotion.x && lastY == event->xmotion.y)
				return;
			break;
		}
		lastX = event->xmotion.x;
		lastY = event->xmotion.y;
	} else {
		lastX = lastY = -1;
	}

	if (event->type == MotionNotify) {
		info->x = (event->xmotion.x / zoom) + paint->paint.zoomX;
		info->y = (event->xmotion.y / zoom) + paint->paint.zoomY;
	} else if (event->type == ButtonPress || event->type == ButtonRelease) {
		info->x = (event->xbutton.x / zoom) + paint->paint.zoomX;
		info->y = (event->xbutton.y / zoom) + paint->paint.zoomY;
	}
	/*
	**  In fatbits we can't tell the difference 
	**   between snaping an fat bits.
	*/
	if (info->isFat) {
		info->realX = info->x;
		info->realY = info->y;
	} else {
		info->realX = info->realX + paint->paint.zoomX;
		info->realY = info->realY + paint->paint.zoomY;
	}

	/*
	**  If this is button down, save it for reference
	*/
	if (event->type == ButtonPress) {
		paint->paint.downX = info->realX;
		paint->paint.downY = info->realY;
	}

	if (paint->paint.paint != None) {
		PaintWidget	pw = (PaintWidget)paint->paint.paint;
		info->base = pw->paint.sourcePixmap;
	} else {
		info->base = paint->paint.sourcePixmap;
	}

	if (data->surfMask & opWindow) {
		int	z = GET_ZOOM(paint) / 2;

		if (z != 0) {
			if (event->type == MotionNotify) {
				event->xmotion.x += z;
				event->xmotion.y += z;
			} else if (event->type == ButtonPress || event->type == ButtonRelease) {
				event->xbutton.x += z;
				event->xbutton.y += z;
			}
		}

		info->surface  = opWindow;
		info->drawable = event->xany.window;
		data->func(w, data->data, event, info);
	}
	if (event->type == MotionNotify) {
		event->xmotion.x = info->x;
		event->xmotion.y = info->y;
	} else if (event->type == ButtonPress || event->type == ButtonRelease) {
		event->xbutton.x = info->x;
		event->xbutton.y = info->y;
	}

	if (data->surfMask & opPixmap) {
		info->surface = opPixmap;
		info->drawable = GET_PIXMAP(paint);
		data->func(w, data->data, event, info);
	}
}

void OpAddEventHandler(Widget w, int surfMask, int mask, Boolean flag, 
		void (*func)(Widget,void *, XEvent*, OpInfo *), void *data)
{
	PaintWidget	paint = (PaintWidget)w;
	PaintWidget	pp    = (PaintWidget)paint->paint.paint;
	data_t		*new = (data_t *)XtMalloc(sizeof(data_t));
	OpInfo		*info;

	if (new == NULL)
		return;

	new->w        = w;
	new->func     = func;
	new->data     = data;
	new->mask     = mask;
	new->flag     = flag;
	new->surfMask = surfMask;

	info = new->info = (OpInfo*)XtMalloc(sizeof(OpInfo));
	info->refCount = 1;
	info->realX = 0;
	info->realY = 0;

	/*
	**  Now build the approprate info structure
	*/
	if (pp == None) {
		/*
		** If this is a paint widget, this is easy
		*/
		info->first_gc  = paint->paint.fgc;
		info->second_gc = paint->paint.sgc;
		info->base_gc   = paint->paint.igc;
	} else {
		/*
		** For a fatbits paint widget get paint information
		*/
		info->first_gc  = pp->paint.fgc;
		info->second_gc = pp->paint.sgc;
		info->base_gc   = pp->paint.igc;
	}

	new->next = list;
	list      = new;

        XtAddEventHandler(w, mask, flag, opHandleEvent, (XtPointer)new);
}

void OpRemoveEventHandler(Widget w, int surfMask, int mask, Boolean flag, 
		void (*func)(Widget, void *, XEvent*, OpInfo *), void *data)
{
	data_t	*cur   = list;
	data_t	**prev = &list;

	while (cur != NULL) {
		if (cur->w        == w    &&
		    cur->data     == data &&
		    cur->mask     == mask &&
		    cur->flag     == flag &&
		    cur->surfMask == surfMask &&
		    cur->func     == func)
			break;
		prev = &cur->next;
		cur = cur->next;
	}

	if (cur == NULL)
		return;

        XtRemoveEventHandler(w, mask, flag, opHandleEvent, (XtPointer)cur);

	*prev = cur->next;

	cur->info->refCount--;
	if (cur->info->refCount == 0)
		XtFree((XtPointer)cur->info);

	XtFree((XtPointer)cur);
}
