/*
 * FIG : Facility for Interactive Generation of figures
 * Copyright (c) 1985-1988 by Supoj Sutanthavibul
 * Parts Copyright (c) 1989-2015 by Brian V. Smith
 * Parts Copyright (c) 1991 by Paul King
 * Parts Copyright (c) 2016-2020 by Thomas Loimer
 *
 * Any party obtaining a copy of these files is granted, free of charge, a
 * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
 * nonexclusive right and license to deal in this software and documentation
 * files (the "Software"), including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
 * the Software, and to permit persons who receive copies from any such
 * party to do so, with the only requirement being that the above copyright
 * and this permission notice remain intact.
 *
 */

/********************** DECLARATIONS ********************/

/* IMPORTS */

#include "d_arc.h"

#include <stddef.h>
#include <math.h>
#include <X11/Xlib.h>

#include "resources.h"
#include "object.h"
#include "mode.h"
#include "paintop.h"
#include "f_util.h"
#include "u_create.h"
#include "u_draw.h"
#include "u_elastic.h"
#include "u_free.h"
#include "u_list.h"
#include "u_markers.h"
#include "u_redraw.h"
#include "w_canvas.h"
#include "w_cursor.h"
#include "w_drawprim.h"
#include "w_msgpanel.h"
#include "w_mousefun.h"
#include "u_geom.h"
#include "xfig_math.h"


/* EXPORT */

F_pos		point[3];
F_pos		center_point;
Boolean		center_marked;

/* LOCAL */

static void	create_arcobject(int lx, int ly);
static void	get_arcpoint(int x, int y);
static void	init_arc_drawing(int x, int y);
static void	cancel_arc(void);
static void	init_arc_c_drawing(int x, int y);
static void	resizing_arc(int x, int y);

static F_arc	*tmparc = 0;
static F_pos	tpoint[3];

static Boolean save_shownums;



void
arc_drawing_selected(void)
{
    center_marked = FALSE;
    set_mousefun("first point", "center point", "", "", "", "");
    canvas_kbd_proc = null_proc;
    canvas_locmove_proc = null_proc;
    canvas_leftbut_proc = init_arc_drawing;
    canvas_middlebut_proc = init_arc_c_drawing;
    canvas_rightbut_proc = null_proc;
    set_cursor(crosshair_cursor);
    reset_action_on();
}

static void
init_arc_drawing(int x, int y)
{
    int		i;
    if (center_marked) {
	/* erase the circle */
	elastic_cbr();
	/* save and turn off showing vertex numbers */
	save_shownums   = appres.shownums;
	appres.shownums = False;
	set_mousefun("direction", "", "cancel", "", "", "");
	tmparc = create_arc();
	tmparc->thickness = 1;
	tmparc->center.x = center_point.x;
	tmparc->center.y = center_point.y;
	/* initialize points in elastic arc */
	for (i=0; i<3; i++) {
	    tpoint[i].x = x;
	    tpoint[i].y = y;
	}
	/* start the arc */
	elastic_arc();
	canvas_locmove_proc = resizing_arc;
    } else {
	set_mousefun("mid point", "", "cancel", "", "", "");
    }
    draw_mousefun_canvas();
    canvas_rightbut_proc = cancel_arc;
    point[0].x = fix_x = cur_x = x;
    point[0].y = fix_y = cur_y = y;
    num_point = 1;
    if (!center_marked) {
	canvas_locmove_proc = unconstrained_line;
	elastic_line();
    }
    canvas_leftbut_proc = get_arcpoint;
    canvas_middlebut_proc = null_proc;
    set_cursor(null_cursor);
    set_action_on();
}

static void
init_arc_c_drawing(int x, int y)
{
    set_mousefun("first point", "", "cancel", "", "", "");
    draw_mousefun_canvas();
    canvas_locmove_proc = resizing_cbr;
    canvas_middlebut_proc = null_proc;
    canvas_rightbut_proc = cancel_arc;
    center_point.x = fix_x = cur_x = x;
    center_point.y = fix_y = cur_y = y;
    center_marked = TRUE;
    center_marker(center_point.x, center_point.y);
    set_action_on();
    num_point = 0;
}

static void
cancel_arc(void)
{
    if (center_marked) {
	/* erase circle */
	if (num_point == 0)
	    elastic_cbr();
	else
	    elastic_arc();
	/* free the temporary arc */
	if (tmparc) {
	    free_arc(&tmparc);
	    tmparc = (F_arc *) 0;
	}
	center_marked = FALSE;
	center_marker(center_point.x, center_point.y);  /* clear center marker */
	/* restore shownums */
	appres.shownums = save_shownums;
    } else {
	/* erase any length info if appres.showlengths is true */
	erase_lengths();
	elastic_line();
	if (num_point == 2) {
	    /* erase initial part of line */
	    cur_x = point[0].x;
	    cur_y = point[0].y;
	    elastic_line();
	}
    }
    arc_drawing_selected();
    draw_mousefun_canvas();
}

static void
get_arcpoint(int x, int y)
{
    if (x == fix_x && y == fix_y)
	return;

    if (num_point == 1) {
	if (center_marked) {
	    set_mousefun("final angle", "", "cancel", "", "", "");
	} else {
	    set_mousefun("final point", "", "cancel", "", "", "");
	}
	draw_mousefun_canvas();
	canvas_leftbut_proc = create_arcobject;
    }
    if (!center_marked)
	elastic_line();
    cur_x = x;
    cur_y = y;
    if (!center_marked)
	elastic_line();
    point[num_point].x = fix_x = x;
    point[num_point++].y = fix_y = y;
    if (!center_marked)
	elastic_line();
}

static void
create_arcobject(int lx, int ly)
{
    F_arc	   *arc;
    int		    x, y;
    float	    xx, yy;

    /* erase last line segment */
    if (!center_marked)
	elastic_line();
    else /* restore shownums */
	appres.shownums = save_shownums;
    /* erase any length info if appres.showlengths is true */
    erase_lengths();
    point[num_point].x = lx;
    point[num_point++].y = ly;
    x = point[0].x;
    y = point[0].y;
    if (center_marked) {
	double theta, r;

	/* erase the center marker */
	center_marker(center_point.x, center_point.y);

	/* free the temporary arc */
	free_arc(&tmparc);

	/* recompute the second and last points to put them on the arc */
	r = sqrt((double)(point[0].x - center_point.x) * (point[0].x - center_point.x)
	       + (double)(point[0].y - center_point.y) * (point[0].y - center_point.y));

	theta = compute_angle((double)(point[1].x - center_point.x),
			    (double)(point[1].y - center_point.y));
	point[1].x = center_point.x + r * cos(theta);
	point[1].y = center_point.y + r * sin(theta);

	theta = compute_angle((double)(point[2].x - center_point.x),
			    (double)(point[2].y - center_point.y));
	point[2].x = center_point.x + r * cos(theta);
	point[2].y = center_point.y + r * sin(theta);
    } else {
	/* erase previous line segment */
	pw_vector(canvas_win, x, y, point[1].x, point[1].y, INV_PAINT,
		  1, RUBBER_LINE, 0.0, DEFAULT);
    }

    if (!compute_arccenter(point[0], point[1], point[2], &xx, &yy)) {
	put_msg("Invalid ARC geometry");
	beep();
	arc_drawing_selected();
	draw_mousefun_canvas();
	return;
    }
    if ((arc = create_arc()) == NULL) {
	arc_drawing_selected();
	draw_mousefun_canvas();
	return;
    }
    arc->type = cur_arctype;
    arc->style = cur_linestyle;
    arc->thickness = cur_linewidth;
    /* scale dash length according to linethickness */
    arc->style_val = cur_styleval * (cur_linewidth + 1) / 2;
    arc->pen_style = -1;
    arc->fill_style = cur_fillstyle;
    arc->pen_color = cur_pencolor;
    arc->fill_color = cur_fillcolor;
    arc->cap_style = cur_capstyle;
    arc->depth = cur_depth;
    arc->direction = compute_direction(point[0], point[1], point[2]);
    /* only allow arrowheads for open arc */
    if (arc->type == T_PIE_WEDGE_ARC) {
	arc->for_arrow = NULL;
	arc->back_arrow = NULL;
    } else {
	if (autoforwardarrow_mode)
	    arc->for_arrow = forward_arrow();
	else
	    arc->for_arrow = NULL;
	if (autobackwardarrow_mode)
	    arc->back_arrow = backward_arrow();
	else
	    arc->back_arrow = NULL;
    }
    arc->center.x = xx;
    arc->center.y = yy;
    arc->point[0].x = point[0].x;
    arc->point[0].y = point[0].y;
    arc->point[1].x = point[1].x;
    arc->point[1].y = point[1].y;
    arc->point[2].x = point[2].x;
    arc->point[2].y = point[2].y;
    arc->next = NULL;
    add_arc(arc);
    reset_action_on(); /* this signals redisplay_curobj() not to refresh */
    /* draw it and anything on top of it */
    redisplay_arc(arc);
    arc_drawing_selected();
    draw_mousefun_canvas();
}

static void
resizing_arc(int x, int y)
{
    F_point	p3;
    double	alpha;

    elastic_arc();
    cur_x = x;
    cur_y = y;
    p3.x = x;
    p3.y = y;
    if (num_point == 0)
	length_msg(MSG_RADIUS);
    else {
	compute_3p_angle((F_point *)&tpoint[0], (F_point *)&center_point, &p3, &alpha);
	put_msg("1st angle = %.2f degrees", (float) alpha*180.0/M_PI);
    }
    elastic_arc();
}

void
elastic_arc(void)
{
	register int i;
	register double	r, theta1, theta2;

	tpoint[2].x = cur_x;
	tpoint[2].y = cur_y;
	/* if user hasn't entered second point yet, make it average of endpoints */
	if (num_point <= 1) {
	    tpoint[1].x = (tpoint[0].x + cur_x)/2;
	    tpoint[1].y = (tpoint[0].y + cur_y)/2;
	}

	if (tpoint[0].x == tpoint[2].x && tpoint[0].y == tpoint[2].y)
		return;

	r = sqrt((double)(tpoint[0].x - center_point.x) * (tpoint[0].x - center_point.x)
	       + (double)(tpoint[0].y - center_point.y) * (tpoint[0].y - center_point.y));

	theta1 = compute_angle((double)(tpoint[1].x - center_point.x),
			    (double)(tpoint[1].y - center_point.y));
	tpoint[1].x = center_point.x + r * cos(theta1);
	tpoint[1].y = center_point.y + r * sin(theta1);

	theta2 = compute_angle((double)(tpoint[2].x - center_point.x),
			    (double)(tpoint[2].y - center_point.y));
	tpoint[2].x = center_point.x + r * cos(theta2);
	tpoint[2].y = center_point.y + r * sin(theta2);

	for (i=0; i<3; i++)
	    tmparc->point[i] = tpoint[i];
	tmparc->direction = compute_direction(tpoint[0], tpoint[1], tpoint[2]);

	draw_arc(tmparc, INV_PAINT);
}
