/*
 *	(c) Copyright 1991 Sun Microsystems, Inc.  All rights reserved.
 *	See LEGAL_NOTICE file for terms and restrictions.
 */

#ifndef lint
#ifdef sccs
static char     sccsid[] = "@(#)drawline.c 1.20 91/05/06";
#endif
#endif

#include <math.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <xview/rect.h>
#include <xview/xv_xrect.h>
#include <xview/win_input.h>
#include <sspkg/drawobj.h>
#include "do_impl.h"
#include "r_impl.h"
#include <sspkg/canshell.h>

Pkg_private int 	drawline_init();
Pkg_private Xv_opaque	drawline_set_avlist();
Pkg_private Xv_opaque	drawline_get_attr();
Pkg_private int 	drawline_destroy();

void	drawline_paint_proc();
void	drawline_calc_arrow_tips();
void	drawline_calc_rect();

#define DEG64_TO_RAD( degrees )	( (degrees) * M_PI / (360 * 64) )

/*ARGSUSED*/
Pkg_private int
drawline_init(parent, drawline, avlist)
	Xv_opaque	parent;
	Drawline		drawline;
	Attr_avlist	avlist;
{
	Drawline_info	*dinfo;
	Drawline_struct	*drawline_object;
	Rectobj_info *rinfo = RECTOBJ_PRIVATE(drawline);

	dinfo = xv_alloc(Drawline_info);
	dinfo->public_self = drawline;
	drawline_object = (Drawline_struct*) drawline;
	drawline_object->private_data = (Xv_opaque) dinfo;

	dinfo->arrow[0].style = ARROW_NONE;
	dinfo->arrow[0].length = 10;
	dinfo->arrow[0].inset_length = 7;

	dinfo->arrow[0].angle_degrees = 30*64;
	dinfo->arrow[0].angle = M_PI/6;

	dinfo->arrow[1] = dinfo->arrow[0];

	rinfo->paint_proc = drawline_paint_proc;
	rinfo->map_event_proc = NULL;

	return(XV_OK);
}


Pkg_private Xv_opaque
drawline_set_avlist(drawline, avlist)
	Drawline		drawline;
	register Attr_avlist	avlist;
{
        register Drawline_attr attr;
        register Drawline_info *dinfo = DRAWLINE_PRIVATE(drawline);
	Rectobj_info *rinfo = RECTOBJ_PRIVATE(drawline);
	Rectobj_info *parent_rinfo;
	register int	arg_int;
	int		state_changed = FALSE;
	int		end;
	int		before;
	Arrow_style	new_style;

	if(*avlist != XV_END_CREATE) {
		Xv_opaque set_result;
		set_result =
		    xv_super_set_avlist(drawline, &drawline_pkg, avlist);
		if(set_result != XV_OK) {
			rectobj_reset_set_info(drawline);
			return(set_result);
		}
	}

	while (attr = (Drawline_attr) * avlist++)
	  switch (attr) {

		case DRAWLINE_X0:
			arg_int = (int) *avlist++;
			before = dinfo->x[0];
			dinfo->x[0] = arg_int;
			if(before != arg_int)
				state_changed = TRUE;
			break;

		case DRAWLINE_Y0:
			arg_int = (int) *avlist++;
			before = dinfo->y[0];
			dinfo->y[0] = arg_int;
			if(before != arg_int)
				state_changed = TRUE;
			break;

		case DRAWLINE_X1:
			arg_int = (int) *avlist++;
			before = dinfo->x[1];
			dinfo->x[1] = arg_int;
			if(before != arg_int)
				state_changed = TRUE;
			break;

		case DRAWLINE_Y1:
			arg_int = (int) *avlist++;
			before = dinfo->y[1];
			dinfo->y[1] = arg_int;
			if(before != arg_int)
				state_changed = TRUE;
			break;

		case DRAWLINE_X:
			end = (int) *avlist++;
			arg_int = (int) *avlist++;

			/* add range checking */

			before = dinfo->x[end];

			if(!rinfo->parent)
				dinfo->x[end] = arg_int;
			else {
				parent_rinfo = RECTOBJ_PRIVATE(rinfo->parent);
				dinfo->x[end] = 
					parent_rinfo->rect.r_left + arg_int;
			}
			if(before != arg_int)
				state_changed = TRUE;
			break;

		case DRAWLINE_Y:
			end = (int) *avlist++;
			arg_int = (int) *avlist++;

			/* add range checking */

			before = dinfo->y[end];

			if(!rinfo->parent)
				dinfo->y[end] = arg_int;
			else {
				parent_rinfo = RECTOBJ_PRIVATE(rinfo->parent);
				dinfo->y[end] = 
#if 0
					parent_rinfo->rect.r_left + arg_int;
#else
/*
 * [Sun 21 Jun 1992, Neil W. Van Dyke <neil@cadre.com>]
 * When Drawline is a child of a Rectobj instead of the Canvas_shell, the
 * coordinates should be offset from the top Y value of the parent Rectobj
 * (parent_rinfo->rec.r_top), not the left X value (parent_rinfo->rect.r_left)
 * as in the disabled code above.
 */
					parent_rinfo->rect.r_top + arg_int;
#endif
			}
			if(before != arg_int)
				state_changed = TRUE;
			break;

		case DRAWLINE_ARROW_STYLE:
			end = (int) *avlist++;
			new_style = (Arrow_style) *avlist++;
			if((end != 0) && (end !=1))
				break;
			if(new_style != dinfo->arrow[end].style)
				state_changed = TRUE;
			dinfo->arrow[end].style = new_style;
			break;

		case DRAWLINE_ARROW_ANGLE:
			end = (int) *avlist++;
			arg_int = (int) *avlist++;
			if((end != 0) && (end !=1))
				break;
			if(arg_int != dinfo->arrow[end].angle_degrees)
				state_changed = TRUE;
			dinfo->arrow[end].angle_degrees = arg_int;
			dinfo->arrow[end].angle = DEG64_TO_RAD(arg_int);
			break;

		case DRAWLINE_ARROW_LENGTH:
			end = (int) *avlist++;
			arg_int = (int) *avlist++;
			if((end != 0) && (end !=1))
				break;
			if(arg_int != dinfo->arrow[end].length)
				state_changed = TRUE;
			dinfo->arrow[end].length = arg_int;
			break;

		case DRAWLINE_ARROW_INSET_LENGTH:
			end = (int) *avlist++;
			arg_int = (int) *avlist++;
			if((end != 0) && (end !=1))
				break;
			if(arg_int != dinfo->arrow[end].inset_length)
				state_changed = TRUE;
			dinfo->arrow[end].inset_length = arg_int;
			break;

		default:
			avlist = attr_skip(attr, avlist);

	  }

	if(state_changed) {
		drawline_calc_arrow_tips(&dinfo->arrow[0],
				dinfo->x[0], dinfo->y[0],
				dinfo->x[1], dinfo->y[1]);
		drawline_calc_arrow_tips(&dinfo->arrow[1],
				dinfo->x[1], dinfo->y[1],
				dinfo->x[0], dinfo->y[0]);
		if(rinfo->no_manage_child)
			rectobj_repaint_rect(drawline, NULL, TRUE);
		drawline_calc_rect(dinfo, rinfo);
		rectobj_repaint_rect(drawline, NULL, TRUE);
	}

	rectobj_finish_set(drawline);
	return(XV_SET_DONE);
}


void
drawline_calc_arrow_tips(ahead, x0, y0, x1, y1)
	Arrow_head	*ahead;
	short		x0, y0;
	short		x1, y1;
{
	double	line_angle;
	double	sine, cosine;

	if(ahead->style == ARROW_NONE)
		return;

	if(((y1 - y0) == 0) && ((x1 -x0) == 0))
		line_angle = M_PI;
	else
		line_angle = atan2( (double)(y1 - y0), (double)(x1 - x0) );

	sincos(line_angle + ahead->angle, &sine, &cosine);
	ahead->tip_ax = cosine * ahead->length + x0;
	ahead->tip_ay = sine * ahead->length + y0;

	sincos(line_angle - ahead->angle, &sine, &cosine);
	ahead->tip_bx = cosine * ahead->length + x0;
	ahead->tip_by = sine * ahead->length + y0;

	if(ahead->style == ARROW_SIMPLE) {
		ahead->interior_x = x0;
		ahead->interior_y = y0;
	} else {
		sincos(line_angle, &sine, &cosine);
		ahead->interior_x = cosine * ahead->inset_length + x0;
		ahead->interior_y = sine * ahead->inset_length + y0;
	}
}


void
drawline_calc_rect(dinfo, rinfo)
	Drawline_info	*dinfo;
	Rectobj_info	*rinfo;
{
/* calculate the bounding box of a line, including arrow heads */
	short	xmin, ymin;
	short	xmax, ymax;

	xmin = MIN(dinfo->x[0], dinfo->x[1]);
	ymin = MIN(dinfo->y[0], dinfo->y[1]);
	xmax = MAX(dinfo->x[0], dinfo->x[1]);
	ymax = MAX(dinfo->y[0], dinfo->y[1]);

	if(dinfo->arrow[0].style != ARROW_NONE) {

		xmin =	MIN(dinfo->arrow[0].tip_ax, 
			MIN(dinfo->arrow[0].tip_bx, 
			MIN(dinfo->arrow[0].interior_x, 
				xmin)));

		ymin =	MIN(dinfo->arrow[0].tip_ay, 
			MIN(dinfo->arrow[0].tip_by, 
			MIN(dinfo->arrow[0].interior_y, 
				ymin)));

		xmax =	MAX(dinfo->arrow[0].tip_ax, 
			MAX(dinfo->arrow[0].tip_bx, 
			MAX(dinfo->arrow[0].interior_x, 
				xmax)));

		ymax =	MAX(dinfo->arrow[0].tip_ay, 
			MAX(dinfo->arrow[0].tip_by,
			MAX(dinfo->arrow[0].interior_y, 
				ymax)));
	}

	if(dinfo->arrow[1].style != ARROW_NONE) {

		xmin =	MIN(dinfo->arrow[1].tip_ax, 
			MIN(dinfo->arrow[1].tip_bx, 
			MIN(dinfo->arrow[1].interior_x, 
				xmin)));

		ymin =	MIN(dinfo->arrow[1].tip_ay, 
			MIN(dinfo->arrow[1].tip_by, 
			MIN(dinfo->arrow[1].interior_y, 
				ymin)));

		xmax =	MAX(dinfo->arrow[1].tip_ax, 
			MAX(dinfo->arrow[1].tip_bx, 
			MAX(dinfo->arrow[1].interior_x, 
				xmax)));

		ymax =	MAX(dinfo->arrow[1].tip_ay, 
			MAX(dinfo->arrow[1].tip_by,
			MAX(dinfo->arrow[1].interior_y, 
				ymax)));
	}

	rinfo->rect.r_left  = xmin;
	rinfo->rect.r_top   = ymin;
	/* zero width lines are nasty */
	rinfo->rect.r_width = MAX((xmax - xmin)+1, 1);
	rinfo->rect.r_height= MAX((ymax - ymin)+1, 1);
}


/*ARGSUSED*/
Pkg_private Xv_opaque
drawline_get_attr(drawline, status, which_attr, avlist)
	Drawline		drawline;
	int		*status;
	register Attr_attribute which_attr;
	Attr_avlist	avlist;
{
	Drawline_info	*dinfo = DRAWLINE_PRIVATE(drawline);
	Rectobj_info	*rinfo;
	Rectobj_info	*parent_rinfo;
	int		end;
	short		parent_offset;

	switch (which_attr) {

		case DRAWLINE_X0:
			return (Xv_opaque) dinfo->x[0];

		case DRAWLINE_Y0:
			return (Xv_opaque) dinfo->y[0];

		case DRAWLINE_X1:
			return (Xv_opaque) dinfo->x[1];

		case DRAWLINE_Y1:
			return (Xv_opaque) dinfo->y[1];

		case DRAWLINE_X:
			end = (int)*avlist;
			if((end != 0) && (end != 1))
				break;

			rinfo = RECTOBJ_PRIVATE(drawline);
			if(!rinfo->parent)
				parent_offset = 0;
			else {
				parent_rinfo = RECTOBJ_PRIVATE(rinfo->parent);
				parent_offset = parent_rinfo->rect.r_left;
			}
			return (Xv_opaque) dinfo->x[end] - parent_offset;

		case DRAWLINE_Y:
			end = (int)*avlist;
			if((end != 0) && (end != 1))
				break;

			rinfo = RECTOBJ_PRIVATE(drawline);
			if(!rinfo->parent)
				parent_offset = 0;
			else {
				parent_rinfo = RECTOBJ_PRIVATE(rinfo->parent);
				parent_offset = parent_rinfo->rect.r_top;
			}
			return (Xv_opaque) dinfo->y[end] - parent_offset;

		case DRAWLINE_ARROW_STYLE:
			end = (int)*avlist;
			if((end != 0) && (end != 1))
				break;
			return (Xv_opaque)dinfo->arrow[end].style;

		case DRAWLINE_ARROW_ANGLE:
			end = (int)*avlist;
			if((end != 0) && (end != 1))
				break;
			return (Xv_opaque) dinfo->arrow[end].angle_degrees;

		case DRAWLINE_ARROW_LENGTH:
			end = (int)*avlist;
			if((end != 0) && (end != 1))
				break;
			return (Xv_opaque) dinfo->arrow[end].length;

		case DRAWLINE_ARROW_INSET_LENGTH:
			end = (int)*avlist;
			if((end != 0) && (end != 1))
				break;
			return (Xv_opaque) dinfo->arrow[end].inset_length;

		default:
			*status = XV_ERROR;
			return (Xv_opaque) 0;
	}
}


/*ARGSUSED*/
Pkg_private int
drawline_destroy(drawline, status)
	Drawline		drawline;
	Destroy_status	status;
{
	Drawline_info	*dinfo = DRAWLINE_PRIVATE(drawline);

	if ((status == DESTROY_CHECKING) || (status == DESTROY_SAVE_YOURSELF))
		return XV_OK;

	free(dinfo);
	return XV_OK;
}

void
drawline_paint_arrow(ahead, dpy, win, gc, x, y, fg, bg)
	Arrow_head	*ahead;
	Display		*dpy;
	Window		win;
	GC		gc;
	short		x, y;
{
	XPoint	point[5];

	point[0].x = ahead->tip_ax;
	point[0].y = ahead->tip_ay;

	point[1].x = x;
	point[1].y = y;

	point[2].x = ahead->tip_bx;
	point[2].y = ahead->tip_by;

	point[3].x = ahead->interior_x;
	point[3].y = ahead->interior_y;

	point[4] = point[0];

	if(ahead->style == ARROW_SIMPLE) {
		XDrawLines(dpy, win, gc, point, 3, CoordModeOrigin);
		return;
	}

	if(ahead->style == ARROW_HOLLOW) {
		XSetForeground(dpy, gc, bg);
		XFillPolygon(dpy, win, gc, point, 4,
				Nonconvex, CoordModeOrigin);
		XSetForeground(dpy, gc, fg);
		XDrawLines(dpy, win, gc, point, 5, CoordModeOrigin);
		return;
	}
	XDrawLines(dpy, win, gc, point, 5, CoordModeOrigin);
	XFillPolygon(dpy, win, gc, point, 5, Nonconvex, CoordModeOrigin);
}


/*ARGSUSED*/
Pkg_private void
drawline_paint_proc(drawline, dpy, win, xrects)
        Drawline drawline;
        Display *dpy;
        Window  win;
        Xv_xrectlist *xrects;
{
	Drawline_info *dinfo = DRAWLINE_PRIVATE(drawline);
	Rectobj_info *rinfo = RECTOBJ_PRIVATE(drawline);
	GC gc;

	gc = XCreateGC(dpy, win, 0, 0);

        if(xrects && xrects->count)
                XSetClipRectangles(dpy, gc,
                                0, 0,
                                xrects->rect_array,
                                xrects->count,
                                Unsorted);

	XSetForeground(dpy, gc, rinfo->shared_info->pixels[rinfo->fg_color]);

	XDrawLine(dpy, win, gc, dinfo->x[0], dinfo->y[0],
				dinfo->x[1], dinfo->y[1]);

	if(dinfo->arrow[0].style != ARROW_NONE)
		drawline_paint_arrow(&dinfo->arrow[0], dpy, win, gc, 
				dinfo->x[0], dinfo->y[0], 
				rinfo->shared_info->pixels[rinfo->fg_color],
				rinfo->shared_info->pixels[rinfo->bg_color]);

	if(dinfo->arrow[1].style != ARROW_NONE)
		drawline_paint_arrow(&dinfo->arrow[1], dpy, win, gc,
				dinfo->x[1], dinfo->y[1],
				rinfo->shared_info->pixels[rinfo->fg_color],
				rinfo->shared_info->pixels[rinfo->bg_color]);

	XFreeGC(dpy, gc);

        rectobj_paint_children(drawline, dpy, win, xrects);
}


