/* +-------------------------------------------------------------------+ */
/* | 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/Intrinsic.h>
#include <X11/StringDefs.h>
#include <math.h>
#include "xpaint.h"
#include "misc.h"
#include "Paint.h"

#define	FILL		0x02
#define CIRCLE		0x01
#define	ISFILL(x)	((x) & FILL)
#define ISCIRCLE(x)	((x) & CIRCLE)

typedef struct {
	Boolean	typeFlag, swap;
	int	rx, ry, startX, startY, endX, endY, drawn, flag;
	GC	gcx;
} LocalInfo;

static int radiusMode = False;

static XRectangle *draw(Widget w, Drawable d, GC lgc, GC fgc, LocalInfo *l)
{
	static XRectangle	rect;
	XArc		arc;
	int		sx = l->startX, sy = l->startY;
	int		ex = l->endX, ey = l->endY;

	if (l->startX == l->endX && l->startY == l->endY)
		return NULL;

	arc.x      = MIN(sx, ex);
	arc.y      = MIN(sy, ey);
	arc.width  = MAX(sx, ex) - arc.x;
	arc.height = MAX(sy, ey) - arc.y;

	if (radiusMode) {
		int	r2 = arc.width * arc.width + arc.height * arc.height;
		int	r;
		
		r = sqrt((double)r2);
		arc.x = l->startX - r;
		arc.y = l->startY - r;
		arc.width  = r * 2;
		arc.height = r * 2;
	} else {
		if (ISCIRCLE(l->typeFlag))
			arc.height = arc.width = MAX(arc.width, arc.height);
		if (arc.x != sx)
			arc.x = sx - arc.width;
		if (arc.y != sy)
			arc.y = sy - arc.height;
	}
	arc.angle1 = 0;
	arc.angle2 = 360*64;

	if (fgc != None)
		XFillArcs(XtDisplay(w), d, fgc, &arc, 1);
	if (lgc != None)
		XDrawArcs(XtDisplay(w), d, lgc, &arc, 1);

	rect.x = arc.x;
	rect.y = arc.y;
	rect.width = arc.width + 1;
	rect.height = arc.height + 1;

	return &rect;
}

static void	press(Widget w, LocalInfo *l, 
			XButtonEvent *event, OpInfo *info) 
{
	/*
	**  Check to make sure all buttons are up, before doing this
	*/
	if ((event->state & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)) != 0)
		return;

	l->rx   = info->x;
	l->ry   = info->y;
	l->endX = l->startX = event->x;
	l->endY = l->startY = event->y;

	l->swap = (event->button == Button2);

	l->typeFlag = (event->state & ShiftMask) ? CIRCLE : 0;

	l->drawn = False;
}

static void	motion(Widget w, LocalInfo *l, 
			XMotionEvent *event, OpInfo *info) 
{
	if (l->drawn) 
		draw(w, info->drawable, l->gcx, None, l);

	l->endX = event->x;
	l->endY = event->y;

	/*
	**  Really set this flag in the if statement
	*/
	l->typeFlag = (event->state & ShiftMask) ? CIRCLE : 0;
	l->drawn = (draw(w, info->drawable, l->gcx, None, l) != NULL);
}

static void	release(Widget w, LocalInfo *l, 
			XButtonEvent *event, OpInfo *info) 
{
	XRectangle	*undo;
	int		mask;
	GC		fgc, lgc;

	/*
	**  Check to make sure all buttons are up, before doing this
	*/
	mask = Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask;
	switch (event->button) {
	case Button1:	mask ^= Button1Mask; break;
	case Button2:	mask ^= Button2Mask; break;
	case Button3:	mask ^= Button3Mask; break;
	case Button4:	mask ^= Button4Mask; break;
	case Button5:	mask ^= Button5Mask; break;
	}
	if ((event->state & mask) != 0)
		return;

	if (l->drawn && info->surface == opWindow) 
		draw(w, info->drawable, l->gcx, None, l);

	if (info->surface == opWindow && info->isFat)
		return;

	UndoStart(w, info);

	l->startX = l->rx;
	l->startY = l->ry;
	l->endX = event->x;
	l->endY = event->y;

	if (l->swap) {
		fgc = info->first_gc;
		lgc = info->second_gc;
	} else {
		fgc = info->second_gc;
		lgc = info->first_gc;
	}

	if (!ISFILL(l->flag))
		fgc = None;

	undo = draw(w, info->drawable, lgc, fgc, l);

	if (info->surface == opPixmap && undo != NULL) {
		UndoSetRectangle(w, undo);
		PwUpdate(w, undo, False);
	}
}

/*
**  Those public functions
*/
void *CircleAdd(Widget w)
{
	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
	l->flag = CIRCLE;
	l->gcx  = GetGCX(w);
        XtVaSetValues(w, XtNcompress, True, NULL);
	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, (OpEventProc)press, l);
	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, (OpEventProc)motion, l);
	OpAddEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, (OpEventProc)release, l);
	SetCrossHairCursor(w);
	return l;
}
void CircleRemove(Widget w, LocalInfo *l)
{
	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, (OpEventProc)press, l);
	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, (OpEventProc)motion, l);
	OpRemoveEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, (OpEventProc)release, l);
	XtFree((XtPointer)l);
}
void *FCircleAdd(Widget w)
{
	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
	l->flag = CIRCLE|FILL;
	l->gcx  = GetGCX(w);
        XtVaSetValues(w, XtNcompress, True, NULL);
	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, (OpEventProc)press, l);
	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, (OpEventProc)motion, l);
	OpAddEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, (OpEventProc)release, l);
	SetCrossHairCursor(w);
	return l;
}
void FCircleRemove(Widget w, LocalInfo *l)
{
	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, (OpEventProc)press, l);
	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, (OpEventProc)motion, l);
	OpRemoveEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, (OpEventProc)release, l);
	XtFree((XtPointer)l);
}
void *OvalAdd(Widget w)
{
	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
	l->flag = 0;
	l->gcx  = GetGCX(w);
        XtVaSetValues(w, XtNcompress, True, NULL);
	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, (OpEventProc)press, l);
	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, (OpEventProc)motion, l);
	OpAddEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, (OpEventProc)release, l);
	SetCrossHairCursor(w);
	return l;
}
void OvalRemove(Widget w, LocalInfo *l)
{
	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, (OpEventProc)press, l);
	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, (OpEventProc)motion, l);
	OpRemoveEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, (OpEventProc)release, l);
	XtFree((XtPointer)l);
}
void *FOvalAdd(Widget w)
{
	LocalInfo	*l = (LocalInfo*)XtMalloc(sizeof(LocalInfo));
	l->flag = FILL;
	l->gcx  = GetGCX(w);
        XtVaSetValues(w, XtNcompress, True, NULL);

	OpAddEventHandler(w, opWindow, ButtonPressMask, FALSE, (OpEventProc)press, l);
	OpAddEventHandler(w, opWindow, ButtonMotionMask, FALSE, (OpEventProc)motion, l);
	OpAddEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, (OpEventProc)release, l);
	SetCrossHairCursor(w);
	return l;
}
void FOvalRemove(Widget w, LocalInfo *l)
{
	OpRemoveEventHandler(w, opWindow, ButtonPressMask, FALSE, (OpEventProc)press, l);
	OpRemoveEventHandler(w, opWindow, ButtonMotionMask, FALSE, (OpEventProc)motion, l);
	OpRemoveEventHandler(w, opWindow|opPixmap, ButtonReleaseMask, FALSE, (OpEventProc)release, l);
	XtFree((XtPointer)l);
}

void CircleSetStyle(Boolean value)
{
	radiusMode = value;
}
Boolean CircleGetStyle()
{
	return radiusMode;
}
