/* +-------------------------------------------------------------------+ */
/* | Copyright 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/StringDefs.h>
#include <X11/Intrinsic.h>
#include <X11/Xatom.h>
#include "Paint.h"
#include "xpaint.h"
#include "misc.h"
#include "menu.h"
#include "palette.h"
#include "image.h"


Image *ImageSmooth(Image *);
Image *ImageSharpen(Image *);
Image *ImageInvert(Image *);
Image *ImageEdge(Image *);
Image *ImageEmbose(Image *);
Image *ImageOilPaint(Image *);

extern void 	*GraphicGetData(Widget);
static Boolean	selectionOwner = False;

/*
**  This really should be a "local" or malloced variable
*/
typedef struct {
	Pixmap	pixmap;
	int	count;
	int	width, height, depth;
} selectInfo;

static void setToSelectOp()
{
	static String	names[2] = { "selectBox", "selectArea" };
	OperationSet(names, 2);
}

static void selectionLost(Widget w, Atom *selection)
{
	selectionOwner = False;
}
static void selectionDone(Widget w, Atom *selection, Atom *target)
{
	/* empty */
}
static Boolean selectionConvert(Widget w, Atom *selection, Atom *target,
			Atom *type, XtPointer *value, unsigned long *len,
			int *format)
{
	Pixmap	src = Global.region.pix;
	Pixmap	*pix, np;
	GC	gc;
	int	wth, hth, dth;

	if (src == None)
		return False;

	if (*target != XA_PIXMAP && *target != XA_BITMAP)
		return False;

	pix = XtNew(Pixmap);

	GetPixmapWHD(XtDisplay(w), src, &wth, &hth, &dth);
	np = XCreatePixmap(XtDisplay(w), XtWindow(w), wth, hth, dth);
	gc = XCreateGC(XtDisplay(w), np, 0, 0);
	XCopyArea(XtDisplay(w), src, np, gc, 0, 0, wth, hth, 0, 0);
	XFreeGC(XtDisplay(w), gc);

	*pix = np;

	*type   = XA_PIXMAP;
	*len    = 1;
	*format = 32;
	*value  = (XtPointer)pix;

	return True;
}

void StdCopyCallback(Widget w, XtPointer paintArg, String *nm, XEvent *event)
{
	Widget		paint = (Widget)paintArg;
	Pixmap		pix, mask;
	int		width, height;

	if (!PwRegionGet(paint, &pix, &mask))
		return;

	GetPixmapWHD(XtDisplay(paint), pix, &width, &height, NULL);

	if (Global.region.pix != None)
		XFreePixmap(XtDisplay(paint), Global.region.pix);
	if (Global.region.mask != None)
		XFreePixmap(XtDisplay(paint), Global.region.mask);

	Global.region.pix    = pix;
	Global.region.mask   = mask;
	Global.region.width  = width;
	Global.region.height = height;

	XtVaGetValues(paint, XtNcolormap, &Global.region.cmap, NULL);

	selectionOwner = XtOwnSelection(paint, XA_PRIMARY, Global.currentTime,
			selectionConvert, selectionLost, selectionDone);
}

static void	stdPasteCB(Widget paint, XtPointer infoArg, Atom *selection, Atom *type,
				XtPointer value, unsigned long *len, int *format)
{
	selectInfo	*info = (selectInfo*)infoArg;
	Display		*dpy = XtDisplay(paint);
	XRectangle	rect;
	Pixmap		pix;
	Colormap	cmap;
	Pixmap		newMask = None;
	GC	 	gc;

	if (type != NULL) {
		info->count--;
		if (*type == XA_BITMAP) {
			int	wth, hth, dth;
			Pixmap	pix = *(Pixmap*)value;

			GetPixmapWHD(dpy, pix, &wth, &hth, &dth);
			if (info->pixmap == None ||
			    info->depth < dth) {
				info->pixmap = pix;
				info->width  = wth;
				info->height = hth;
				info->depth  = dth;
			}
		} else if (*type == XA_PIXMAP) {
			int	wth, hth, dth;
			Pixmap	pix = *(Pixmap*)value;

			GetPixmapWHD(dpy, pix, &wth, &hth, &dth);
			if (info->pixmap == None ||
			    info->depth < dth) {
				info->pixmap = pix;
				info->width  = wth;
				info->height = hth;
				info->depth  = dth;
			}
		}
		/*
		**  Are there more possible selections comming?
		*/
		if (info->count != 0)
			return;

		/*
		**  Now that we have gotten all of the selections
		**    use the best one.
		*/
		if (info->pixmap != None) {
			Pixmap	np;
			GC	gc;

			if (Global.region.pix != None)
				XFreePixmap(dpy, Global.region.pix);
			if (Global.region.mask != None)
				XFreePixmap(dpy, Global.region.mask);
			if (Global.region.image != NULL)
				ImageDelete((Image*)Global.region.image);

			Global.region.pix   = None;
			Global.region.mask  = None;
			Global.region.image = NULL;

			np = XCreatePixmap(dpy, XtWindow(paint), 
					info->width, info->height, info->depth);
			gc = XCreateGC(dpy, np, 0, 0);
			XCopyArea(dpy, info->pixmap, np, gc, 
						0, 0, 
						info->width, info->height, 
						0, 0);

			XFreeGC(dpy, gc);

			Global.region.width  = info->width;
			Global.region.height = info->height;
			Global.region.pix    = np;

			if (info->depth == 1)
				Global.region.cmap = -1;
			else
				Global.region.cmap = DefaultColormapOfScreen(XtScreen(paint));
		}

		XtFree((XtPointer)info);
	}

	/*
	**  No valid selections anywhere or we own the selection.
	*/
	if (Global.region.pix == None && Global.region.image == NULL)
		return;
	
	rect.x = 0;
	rect.y = 0;
	rect.width = Global.region.width;
	rect.height = Global.region.height;

	if (Global.region.mask != None) {
		newMask = XCreatePixmap(dpy, XtWindow(paint), rect.width, rect.height, 1);
		gc = XCreateGC(dpy, newMask, 0, 0);
		XCopyArea(dpy, Global.region.mask, newMask, gc, 0, 0, rect.width, rect.height, 0, 0);
		XFreeGC(dpy, gc);
	} else if (Global.region.image != NULL && ((Image*)Global.region.image)->maskData != NULL) {
		newMask = ImageMaskToPixmap(paint, (Image*)Global.region.image);
	}

	XtVaGetValues(paint, XtNcolormap, &cmap, NULL);
	if (cmap != Global.region.cmap) {
		Image	*image;
		Pixmap	npix = None;

		if (rect.width * rect.height > 1024)
			StateSetBusy(True);

		if (Global.region.pix == None) {
			image = (Image*)Global.region.image;
			image->refCount++;
		} else  {
			image = PixmapToImage(paint, Global.region.pix, Global.region.cmap);
		}

		ImageToPixmapCmap(image, paint, &npix, cmap);

		PwRegionSet(paint, &rect, npix, newMask);

		if (rect.width * rect.height > 1024)
			StateSetBusy(False);
	} else {
		int	depth;

		XtVaGetValues(paint, XtNdepth, &depth, NULL);
		pix = XCreatePixmap(dpy, XtWindow(paint), rect.width, rect.height, depth);
		gc = XtGetGC(paint, 0, 0);
		XCopyArea(dpy, Global.region.pix, pix, gc, 0, 0, rect.width, rect.height, 0, 0);
		XtReleaseGC(paint, gc);

		PwRegionSet(paint, &rect, pix, newMask);
	}

	setToSelectOp();
}
void StdPasteCallback(Widget w, XtPointer paintArg, XtPointer junk)
{
	Widget		paint = (Widget)paintArg;
	int		flg = True;

	if (!selectionOwner) {
		static Atom	targets[2] = { XA_PIXMAP, XA_BITMAP };
		XtPointer	data[2];
		selectInfo	*info = XtNew(selectInfo);

		info->count = XtNumber(targets);
		info->pixmap = None;

		data[0] = (XtPointer)info;
		data[1] = (XtPointer)info;

		XtGetSelectionValues(paint, XA_PRIMARY, targets, 2,
				stdPasteCB, data, Global.currentTime);
	} else {
		stdPasteCB(paint, NULL, NULL, NULL, 0, NULL, NULL);
	}
}

void StdClearCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget	paint = (Widget)paintArg;

	PwRegionClear(paint);
}

void StdCutCallback(Widget w, XtPointer paintArg, String *nm, XEvent *event)
{
	StdCopyCallback(w, paintArg, nm, event);
	StdClearCallback(w, paintArg, NULL);
}

void StdDuplicateCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
	XRectangle	rect;
	Widget		paint = (Widget)paintArg;
	Pixmap		pix, mask;
	int		width, height;

	if (!PwRegionGet(paint, &pix, &mask))
		return;

	GetPixmapWHD(XtDisplay(paint), pix, &width, &height, NULL);

	rect.x = 0;
	rect.y = 0;
	rect.width = width;
	rect.height = height;

	PwRegionSet(paint, &rect, pix, mask);
}

void StdSelectAllCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget		paint = (Widget)paintArg;
	XRectangle	rect;
	int		dw, dh;

	XtVaGetValues(paint, XtNdrawWidth, &dw, XtNdrawHeight, &dh, NULL);

	rect.x = 0;
	rect.y = 0;
	rect.width  = dw;
	rect.height = dh;

	PwRegionSet(paint, &rect, None, None);
	setToSelectOp();
}
void StdUndoCallback(Widget w, XtPointer paintArg, XtPointer junk2)
{
	UndoSwap((Widget)paintArg);
}

void ClipboardSetImage(Widget w, Image *image)
{
	if (Global.region.pix != None)
		XFreePixmap(XtDisplay(w), Global.region.pix);
	if (Global.region.mask != None)
		XFreePixmap(XtDisplay(w), Global.region.mask);
	if (Global.region.image != NULL)
		ImageDelete(Global.region.image);

	Global.region.pix    = None;
	Global.region.mask   = None;
	Global.region.cmap   = None;
	Global.region.image  = (void*)image;
	Global.region.width  = image->width;
	Global.region.height = image->height;
}

/*
**  End of "edit" menu function, start region menu functions.
**
*/

void StdRegionFlipX(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget	paint = (Widget)paintArg;
	float	v = -1.0;

	PwRegionAddScale(paint, &v, NULL);
}

void StdRegionFlipY(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget	paint = (Widget)paintArg;
	float	v = -1.0;

	PwRegionAddScale(paint, NULL, &v);
}

/*
**  
*/
static void stdImgProcess(Widget paint, Image *(*func)(Image *))
{
	Pixmap		pix;
	Colormap	cmap;
	Image		*in, *out;

	StateSetBusy(True);

	if (!PwRegionGet(paint, &pix, None)) {
		StateSetBusy(False);
		return;
	}

	PwRegionTear(paint);
	XtVaGetValues(paint, XtNcolormap, &cmap, NULL);

	in = PixmapToImage(paint, pix, cmap);
	out = func(in);
	ImageDelete(in);
	ImageToPixmapCmap(out, paint, &pix, cmap);

	PwRegionSetRawPixmap(paint, pix);

	StateSetBusy(False);
}

void StdRegionInvert(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget		paint = (Widget)paintArg;

	stdImgProcess(paint, ImageInvert);
}
void StdRegionSharpen(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget		paint = (Widget)paintArg;

	stdImgProcess(paint, ImageSharpen);
}
void StdRegionSmooth(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget		paint = (Widget)paintArg;

	stdImgProcess(paint, ImageSmooth);
}
void StdRegionEdge(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget		paint = (Widget)paintArg;

	stdImgProcess(paint, ImageEdge);
}
void StdRegionEmbose(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget		paint = (Widget)paintArg;

	stdImgProcess(paint, ImageEmbose);
}
void StdRegionOilPaint(Widget w, XtPointer paintArg, XtPointer junk2)
{
	Widget		paint = (Widget)paintArg;

	stdImgProcess(paint, ImageOilPaint);
}

/*
**  Start callback functions
*/
static void prCallback(Widget paint, Widget w, Boolean flag)
{
	XtVaSetValues(w, XtNsensitive, flag, NULL);
}
static void addFunction(Widget item, Widget paint, XtCallbackProc func)
{
	XtAddCallback(item, XtNcallback, func, (XtPointer)paint);
	XtAddCallback(paint, XtNregionCallback, (XtCallbackProc)prCallback, (XtPointer)item);
	XtVaSetValues(item, XtNsensitive, (XtPointer)False, NULL);
}

void ccpAddUndo(Widget w, Widget paint)
{
	XtAddCallback(w, XtNcallback, StdUndoCallback, (XtPointer)paint);
}
void ccpAddCut(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdCutCallback);
}

void ccpAddCopy(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdCopyCallback);
}

void ccpAddPaste(Widget w, Widget paint)
{
	XtVaSetValues(w, XtNsensitive, True, NULL);
	XtAddCallback(w, XtNcallback, 
		(XtCallbackProc)StdPasteCallback, (XtPointer)paint);
}
void ccpAddClear(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdClearCallback);
}
void ccpAddDuplicate(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdDuplicateCallback);
}

/*
**  Region functions
*/
void ccpAddSaveRegion(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdSaveRegionFile);
}
void ccpAddRegionFlipX(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdRegionFlipX);
}
void ccpAddRegionFlipY(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdRegionFlipY);
}
void ccpAddRegionInvert(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdRegionInvert);
}
void ccpAddRegionSharpen(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdRegionSharpen);
}
void ccpAddRegionSmooth(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdRegionSmooth);
}
void ccpAddRegionEdge(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdRegionEdge);
}
void ccpAddRegionEmbose(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdRegionEmbose);
}
void ccpAddRegionOilPaint(Widget w, Widget paint)
{
	addFunction(w, paint, (XtCallbackProc)StdRegionOilPaint);
}

/*
**  Standard poup menu
*/
static PaintMenuItem	popupMenu[] = {
	MI_SEPERATOR(),
#define P_UNDO_ITEM	1
	MI_SIMPLE("undo"),
	MI_SEPERATOR(),
#define P_CUT_ITEM	3
	MI_SIMPLE("cut"),
#define P_COPY_ITEM	4
	MI_SIMPLE("copy"),
#define P_PASTE_ITEM	5
	MI_SIMPLE("paste"),
#define P_CLEAR_ITEM	6
	MI_SIMPLE("clear"),
};

void ccpAddStdPopup(Widget paint)
{
	MenuPopupCreate(XtParent(paint), XtNumber(popupMenu), popupMenu);

	ccpAddUndo(popupMenu[P_UNDO_ITEM].widget, paint);
	ccpAddCut(popupMenu[P_CUT_ITEM].widget, paint);
	ccpAddCopy(popupMenu[P_COPY_ITEM].widget, paint);
	ccpAddPaste(popupMenu[P_PASTE_ITEM].widget, paint);
	ccpAddClear(popupMenu[P_CLEAR_ITEM].widget, paint);
}
