/* +-------------------------------------------------------------------+ */
/* | 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 "xpaint.h"
#include "misc.h"
#include "PaintP.h"

#define	GET_PW(w)	((PaintWidget)(((PaintWidget)w)->paint.paint == None ? w : ((PaintWidget)w)->paint.paint))

void UndoSwap(Widget w)
{
	PaintWidget	pw = GET_PW(w);

	/*
	** No undo buffers
	*/
	if (pw->paint.head == NULL)
		return;
	/*
	**  Walked off the end of the undo buffers.
	*/
	if (pw->paint.undo == NULL) {
		for (pw->paint.undo = pw->paint.head; 
		     pw->paint.undo != NULL && !pw->paint.undo->valid; 
		     pw->paint.undo = pw->paint.undo->prev);
	} else {
		pw->paint.undo = pw->paint.undo->prev;
	}

	PwUpdate(w, NULL, True);
}

/*
**
**  Consider the undo system...
**
**   UndoBuffer 
**
**        0    1    2   3
**
**        ^    ^-- pw->paint.undo
**        + pw->paint.top (base reference)
**
*/

Pixmap PwUndoStart(Widget w, XRectangle *rect)
{
	PaintWidget		pw = GET_PW(w);
	UndoStack		*cur, *nxt, *start;

	pw->paint.dirty = True;

	/*
	**  If there are no undo buffers
	*/
	if (pw->paint.head == NULL)
		return pw->paint.base;

	/*
	**  If we are in the middle, and need to start writing.
	**    it means that we've been doing some undo, and thus
	**    the next stuff in line is invalid.
	*/
	cur = pw->paint.undo;
	if (cur != pw->paint.head) {
		if (cur == NULL) 
			cur = pw->paint.tail;
		for (; cur != NULL; cur = cur->next)
			cur->valid = False;
		if (pw->paint.undo == NULL) {
			nxt = pw->paint.tail;
		} else {
			nxt = pw->paint.undo->next;
		}
		start = NULL;
	} else if (cur->valid) {
		/*
		**  If the current is valid, then everthing behind it must be as well
		**    therfor tail must be valid.
		*/
		nxt = pw->paint.tail;
		XCopyArea(XtDisplay(pw), nxt->pixmap, pw->paint.base,
				pw->paint.gc,
				nxt->box.x, nxt->box.y,
				nxt->box.width, nxt->box.height,
				nxt->box.x, nxt->box.y);

		/*
		**  Unlink the tail, and move it to the front.
		*/
		if (pw->paint.head != pw->paint.tail) {
			if (nxt->next != NULL)
				nxt->next->prev = NULL;
			pw->paint.tail = nxt->next;

			pw->paint.head->next = nxt;
			nxt->next = NULL;
			nxt->prev = pw->paint.head;
			pw->paint.head = nxt;
			start = pw->paint.tail;
		} else {
			start = nxt;
		}
	}

	if (start == NULL) {
		/*
		**  Nothing valid, copy the whole thing
		*/
		XCopyArea(XtDisplay(pw), pw->paint.base, nxt->pixmap,
				pw->paint.gc,
				0, 0,
				pw->paint.drawWidth, pw->paint.drawHeight,
				0, 0);
	} else {
		for (cur = start; cur != pw->paint.head; cur = cur->next) {
			XCopyArea(XtDisplay(pw), cur->pixmap, nxt->pixmap,
					pw->paint.gc,
					cur->box.x, cur->box.y,
					cur->box.width, cur->box.height,
					cur->box.x, cur->box.y);
		}
	}

	if (rect == NULL) {
		nxt->box.x      = 0;
		nxt->box.y      = 0;
		nxt->box.width  = 0;
		nxt->box.height = 0;
	} else {
		nxt->box = *rect;
	}
	nxt->valid      = True;

	pw->paint.undo = nxt;

	return nxt->pixmap;
}


void UndoStart(Widget w, OpInfo *info)
{
	if (info->surface != opPixmap)
		return;

	info->drawable = PwUndoStart(w, NULL);
}


void	UndoStartPoint(Widget w, OpInfo *info, int x, int y)
{
	PaintWidget	pw = GET_PW(w);

	if (info->surface != opPixmap)
		return;

	UndoStart(w, info);

	if (pw->paint.undo == NULL)
		return;

	pw->paint.undo->box.x      = x - pw->paint.lineWidth;
	pw->paint.undo->box.y      = y - pw->paint.lineWidth;
	pw->paint.undo->box.width  = 1 + pw->paint.lineWidth * 2;
	pw->paint.undo->box.height = 1 + pw->paint.lineWidth * 2;
}

void UndoGrow(Widget w, int x, int y)
{
	PaintWidget	pw = GET_PW(w);
	XRectangle	*rect;
	int		dx, dy;

	if (pw->paint.undo == NULL)
		return;

	rect = &pw->paint.undo->box;

	rect->x     += pw->paint.lineWidth;
	rect->y     += pw->paint.lineWidth;
	rect->width -= pw->paint.lineWidth * 2;
	rect->height-= pw->paint.lineWidth * 2;

	dx = x - rect->x;
	dy = y - rect->y;

	if (dx > 0) {
		rect->width = MAX(rect->width, dx);
	} else {
		rect->width = (int)rect->width - dx + 1;
		rect->x     = x;
	}

	if (dy > 0) {
		rect->height = MAX(rect->height, dy) + 1;
	} else {
		rect->height = (int)rect->height - dy + 1;
		rect->y      = y;
	}

	rect->x     -= pw->paint.lineWidth;
	rect->y     -= pw->paint.lineWidth;
	rect->width += pw->paint.lineWidth * 2;
	rect->height+= pw->paint.lineWidth * 2;
}

void PwUndoSetRectangle(Widget w, XRectangle *rect)
{
	PaintWidget	pw = GET_PW(w);

	if (pw->paint.undo == NULL)
		return;

	pw->paint.undo->box = *rect;
}
void PwUndoAddRectangle(Widget w, XRectangle *rect)
{
	PaintWidget	pw = GET_PW(w);

	if (pw->paint.undo == NULL)
		return;

	pw->paint.undo->box = *RectUnion(rect, &pw->paint.undo->box);
}

void UndoSetRectangle(Widget w, XRectangle *rect)
{
	PaintWidget	pw = GET_PW(w);

	if (pw->paint.undo == NULL)
		return;

	PwUndoSetRectangle(w, rect);

	pw->paint.undo->box.x     -= pw->paint.lineWidth;
	pw->paint.undo->box.y     -= pw->paint.lineWidth;
	pw->paint.undo->box.width += pw->paint.lineWidth * 2 + 1;
	pw->paint.undo->box.height+= pw->paint.lineWidth * 2 + 1;
}

void	UndoStartRectangle(Widget w, OpInfo *info, XRectangle *rect)
{
	UndoStart(w, info);
	UndoSetRectangle(w, rect);
}
