/*
 *  Copyright (c) 1994, Riley Rainey,  riley@netcon.com
 *
 *  Permission to use, copy, modify and distribute (without charge) this
 *  software, documentation, images, etc. is granted, provided that this 
 *  comment and the author's name is retained.
 *
 *  This software is provided by the author as is, and without any expressed
 *  or implied warranties, including, but not limited to, the implied
 *  warranties of merchantability and fitness for a particular purpose.  In no
 *  event shall the author be liable for any direct, indirect, incidental, or
 *  consequential damages arising in any way out of the use of this software.
 */

#include <Xm/Xm.h>
#include "gedit.h"

void WorldToWidget(), DrawGrid(), DrawRuler(), DrawWidget(), StringSize();
extern void PointToXYZ();

#define MINIMUM_GRID_SPACING	50
#define MINIMUM_RULER_SPACING	50
#define RULER_MARGIN		2
#define RULER_MAJOR_LENGTH	7
#define RULER_MINOR1_LENGTH	4
#define RULER_MINOR2_LENGTH	2
#define RULER_THICKNESS		2

void
RescaleView (w, factor)
Widget	w;
double	factor;
{
	polygon_t	*poly;
	point_t		*q;
	view_info_t	*p;
	register int	i, j, xc, yc, zc;

	XtVaGetValues (w,
		XmNuserData,	&p,
		NULL);

	switch (p->layout) {

	case VL_NXNY:
	case VL_NYX:
		xc = (p->width + 1) / 2;
		yc = (p->height + 1) / 2;
		zc = (p->other_view->height + 1) / 2;
		p->other_view->origin_x =
			p->origin_x = (p->origin_x - xc) * factor + xc;
		p->origin_y = (p->origin_y - yc) * factor + yc;
		p->other_view->origin_y =
			(p->other_view->origin_y - zc) * factor + zc;
		break;

	case VL_NXZ:
		xc = (p->width + 1) / 2;
		yc = (p->other_view->height + 1) / 2;
		zc = (p->height + 1) / 2;
		p->other_view->origin_x =
			p->origin_x = (p->origin_x - xc) * factor + xc;
		p->origin_y = (p->origin_y - zc) * factor + zc;
		p->other_view->origin_y =
			(p->other_view->origin_y - yc) * factor + yc;
		break;

	case VL_NYZ:
		xc = (p->other_view->height + 1) / 2;
		yc = (p->width + 1) / 2;
		zc = (p->height + 1) / 2;
		p->other_view->origin_x =
			p->origin_x = (p->origin_x - yc) * factor + yc;
		p->origin_y = (p->origin_y - zc) * factor + zc;
		p->other_view->origin_y =
			(p->other_view->origin_y - xc) * factor + xc;
		break;

	}

	pixel_scale /= factor;

	for (i=0, poly=polygon_list; i < polygon_max; ++i, ++poly) {
		for (j=0, q=poly->point; j < poly->num_points; ++j, ++q) {
			PointToXYZ (p, q);
		}
	}

	DrawWidget (w, False);
	DrawWidget (p->other_widget, False);
}

void
DrawWidget (w, immediate)
Widget	w;
Boolean	immediate;
{
	polygon_t	*poly;
	point_t		*q;
	view_info_t	*p;
	register int	i, j;
	int		x1, y1, x2, y2;
	Display		*dpy;
	Drawable	d;
	Pixel		fg, bg;

	XtVaGetValues (w,
		XmNuserData,	&p,
		XmNforeground,	&fg,
		XmNbackground,	&bg,
		NULL);

	dpy = XtDisplay (w);
	d = XtWindow (w);

	if (d == 0)
		immediate = False;

	XSetForeground (dpy, p->gc, bg);
	if (immediate)
		XFillRectangle (dpy, d, p->gc, 0, 0, p->width, p->height);
	XFillRectangle (dpy, p->pixmap, p->gc, 0, 0, p->width, p->height);

	XSetForeground (dpy, p->gc, fg);
	XSetLineAttributes (dpy, p->gc, app_data.line_thickness, LineSolid,
		CapButt, JoinMiter);

	for (i=unsel_polygon; i >= 0; i = polygon_list[i].next) {
		poly = &polygon_list[i];
		if (poly->num_points == 0)
			continue;
		WorldToWidget (p, &poly->point[poly->num_points-1], &x1, &y1);
		for (j=0, q=poly->point; j < poly->num_points; ++j, ++q) {
			WorldToWidget (p, q, &x2, &y2);
			if (immediate)
				XDrawLine (dpy, d, p->gc, x1, y1, x2, y2);
			XDrawLine (dpy, p->pixmap, p->gc, x1, y1, x2, y2);
			x1 = x2;
			y1 = y2;
		}
	}

	XSetForeground (dpy, p->gc, app_data.select_pixel);
	XSetLineAttributes (dpy, p->gc, app_data.selection_thickness, LineSolid,
		CapButt, JoinMiter);

	for (i=sel_polygon; i >= 0; i = polygon_list[i].next) {
		poly = &polygon_list[i];
		if (poly->num_points == 0)
			continue;
		WorldToWidget (p, &poly->point[poly->num_points-1], &x1, &y1);
		for (j=0, q=poly->point; j < poly->num_points; ++j, ++q) {
			WorldToWidget (p, q, &x2, &y2);
			if (immediate)
				XDrawLine (dpy, d, p->gc, x1, y1, x2, y2);
			XDrawLine (dpy, p->pixmap, p->gc, x1, y1, x2, y2);
			x1 = x2;
			y1 = y2;
		}
	}

	if (immediate)
		DrawGrid (p, dpy, d);
	DrawGrid (p, dpy, p->pixmap);

	if (immediate)
		DrawRuler (p, dpy, d, bg);
	DrawRuler (p, dpy, p->pixmap, bg);

	if (immediate == False && d != 0)
		XCopyArea (dpy, p->pixmap, d, p->gc, 0, 0, p->width, p->height,
			0, 0);
}

void
DrawPolygon (w, poly, immediate)
Widget	w;
polygon_t	*poly;
Boolean	immediate;
{

	point_t		*q;
	view_info_t	*p;
	register int	j;
	int		x1, y1, x2, y2;
	Display		*dpy;
	Drawable	d;
	Pixel		fg, bg;

	XtVaGetValues (w,
		XmNuserData,	&p,
		XmNforeground,	&fg,
		XmNbackground,	&bg,
		NULL);

	dpy = XtDisplay (w);
	d = XtWindow (w);

	if (d == 0)
		immediate = False;

	XSetForeground (dpy, p->gc, fg);
	XSetLineAttributes (dpy, p->gc, app_data.line_thickness, LineSolid,
		CapButt, JoinMiter);

	if (poly->num_points == 0)
		return;

	WorldToWidget (p, &poly->point[poly->num_points-1], &x1, &y1);
	for (j=0, q=poly->point; j < poly->num_points; ++j, ++q) {
		WorldToWidget (p, q, &x2, &y2);
		if (immediate)
			XDrawLine (dpy, d, p->gc, x1, y1, x2, y2);
		XDrawLine (dpy, p->pixmap, p->gc, x1, y1, x2, y2);
		x1 = x2;
		y1 = y2;
	}

	if (immediate == False && d != 0)
		XCopyArea (dpy, p->pixmap, d, p->gc, 0, 0, p->width, p->height,
			0, 0);
}

void
WorldToWidget (p, q, x, y)
view_info_t	*p;
point_t		*q;
int		*x, *y;
{
	switch (p->layout) {

	case VL_NXZ:
		*x = q->x;
		*y = q->z;
		break;

	case VL_NXNY:
		*x = q->x;
		*y = q->y;
		break;

	case VL_NYX:
		*x = q->y;
		*y = q->x;
		break;

	case VL_NYZ:
		*x = q->y;
		*y = q->z;
		break;
	}
}

void
DrawGrid (p, dpy, d)
view_info_t	*p;
Display		*dpy;
Drawable	d;
{
	int	exp, n, x, y;

	if (app_data.show_grid == False)
		return;

	XSetForeground (dpy, p->grid_gc, app_data.grid_pixel);

	for (exp = 1; ; exp *= 10) {
		if (exp / pixel_scale >= MINIMUM_GRID_SPACING)
			break;
		if (5.0 * exp / pixel_scale >= MINIMUM_GRID_SPACING) {
			exp = 5 * exp;
			break;
		}
	}

/*
 *  Y-axis lines
 */

	for (x=p->origin_x, n = 0; x > 0; ) {
		XDrawLine (dpy, d, p->grid_gc, x, 0, x, p->height);
		++ n;
		x = p->origin_x - (int) (n * exp / pixel_scale);
	}

	for (x=p->origin_x, n = 0; x < p->width; ) {
		++ n;
		x = p->origin_x + (int) (n * exp / pixel_scale);
		XDrawLine (dpy, d, p->grid_gc, x, 0, x, p->height);
	}

/*
 *  X-axis lines
 */

	for (y=p->origin_y, n = 0; y > 0; ) {
		XDrawLine (dpy, d, p->grid_gc, 0, y, p->width, y);
		++ n;
		y = p->origin_y - (int) (n * exp / pixel_scale);
	}

	for (y=p->origin_y, n = 0; y < p->height; ) {
		++ n;
		y = p->origin_y + (int) (n * exp / pixel_scale);
		XDrawLine (dpy, d, p->grid_gc, 0, y, p->width, y);
	}
}

void DrawSelectedPolygon (w, poly, immediate, erase)
Widget		w;
polygon_t	*poly;
Boolean		immediate, erase;
{
	register int	i;
	int		x1, x2, y1, y2;
	point_t		*q;
	view_info_t	*p;
	Pixel		bg;
	Display		*dpy;
	Drawable	d;

	dpy = XtDisplay (w);
	d = XtWindow (w);

	if (d == 0)
		immediate = False;

	XtVaGetValues (w,
		XmNuserData,	&p,
		XmNbackground,	&bg,
		NULL);

	if (erase)
		XSetForeground (dpy, p->gc, bg);
	else
		XSetForeground (dpy, p->gc, app_data.select_pixel);

	XSetLineAttributes (dpy, p->gc, app_data.selection_thickness, LineSolid,
		CapButt, JoinMiter);

	WorldToWidget (p, &poly->point[poly->num_points-1], &x1, &y1);
	for (i=0, q=poly->point; i < poly->num_points; ++i, ++q) {
		WorldToWidget (p, q, &x2, &y2);
		if (immediate)
			XDrawLine (dpy, d, p->gc, x1, y1, x2, y2);
		XDrawLine (dpy, p->pixmap, p->gc, x1, y1, x2, y2);
		x1 = x2;
		y1 = y2;
	}
}

void
DrawRuler (p, dpy, d, bg)
view_info_t	*p;
Display		*dpy;
Drawable	d;
Pixel		bg;
{

	int		ruler_y, font_y, nseg, exp, n, x, xw, yw, xv;
	XSegment	seg[256];
	char		s[32];

/*
 *  we'll only display the horizontal ruler in the top window
 */

	if (p->other_widget == twindow)
		return;

	if (app_data.show_ruler == False)
		return;

	for (exp = 1; ; exp *= 10) {
		if (exp / pixel_scale >= MINIMUM_RULER_SPACING)
			break;
		if (2.0 * exp / pixel_scale >= MINIMUM_RULER_SPACING) {
			exp = 2 * exp;
			break;
		}
		if (5.0 * exp / pixel_scale >= MINIMUM_RULER_SPACING) {
			exp = 5 * exp;
			break;
		}
	}

	ruler_y = p->height - (RULER_THICKNESS + 2 * RULER_MARGIN +
		RULER_MAJOR_LENGTH + app_data.ruler_font->max_bounds.ascent);

	font_y = ruler_y + RULER_THICKNESS + RULER_MARGIN +
		app_data.ruler_font->max_bounds.ascent - 2;

	XSetForeground (dpy, p->gc, bg);
	XFillRectangle (dpy, d, p->gc,
		0, ruler_y, p->width, p->height - ruler_y);

	XSetForeground (dpy, p->gc, app_data.grid_pixel);

	seg[0].x1 = 0;
	seg[0].x2 = p->width - 1;
	seg[0].y1 = seg[0].y2 = ruler_y;
	nseg = 1;

/*
 *  Major tick marks
 */

	for (x=p->origin_x, n = 0; x > 0; ) {
		seg[nseg].x1 = seg[nseg].x2 = x;
		seg[nseg].y1 = p->height - 1;
		seg[nseg].y2 = p->height - RULER_MAJOR_LENGTH;
		++ nseg;
		xv = (int) (n * exp);
		sprintf (s, "%d", xv);
		StringSize (s, &xw, &yw);
		XDrawString (dpy, d, p->gc, x - xw / 2, font_y, s, strlen(s));
		++ n;
		x = p->origin_x - (int) (n * exp / pixel_scale);
	}

	for (x=p->origin_x, n = 0; x < p->width; ) {
		++ n;
		x = p->origin_x + (int) (n * exp / pixel_scale);
		xv = (int) (n * exp);
		sprintf (s, "%d", xv);
		StringSize (s, &xw, &yw);
		XDrawString (dpy, d, p->gc, x - xw / 2, font_y, s, strlen(s));
		seg[nseg].x1 = seg[nseg].x2 = x;
		seg[nseg].y1 = p->height - 1;
		seg[nseg].y2 = p->height - RULER_MAJOR_LENGTH;
		++ nseg;
	}

	XSetLineAttributes (dpy, p->gc, RULER_THICKNESS, LineSolid,
		CapButt, JoinMiter);

	XDrawSegments (dpy, d, p->gc, seg, nseg);
}

void
StringSize (s, xw, yw)
char	*s;
int	*xw, *yw;
{

	*xw = XTextWidth (app_data.ruler_font, s, strlen (s));
	*yw = app_data.ruler_font->max_bounds.ascent;

}
