/*
 * 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-2024 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.
 *
 */

#include "e_flip.h"

#include <math.h>
#include <stddef.h>

#include "resources.h"
#include "mode.h"
#include "object.h"
#include "paintop.h"
#include "e_rotate.h"
#include "u_draw.h"
#include "u_fonts.h"
#include "u_search.h"
#include "u_redraw.h"
#include "u_create.h"
#include "u_list.h"
#include "u_markers.h"
#include "w_canvas.h"
#include "w_cursor.h"
#include "w_mousefun.h"
#include "xfig_math.h"

/* EXPORTS */

int		setanchor;
int		setanchor_x;
int		setanchor_y;

/* LOCAL */

static int	flip_axis;
static int	copy;

static void	init_flip(F_line *p, int type, int x, int y, int px, int py);
static void	init_copynflip(F_line *p, int type, int x,int y, int px,int py);
static void	set_unset_anchor(int x, int y);
static void	init_fliparc(F_arc *old_a, int px, int py);
static void	init_flipcompound(F_compound *old_c, int px, int py);
static void	init_flipellipse(F_ellipse *old_e, int px, int py);
static void	init_flipline(F_line *old_l, int px, int py);
static void	init_flipspline(F_spline *old_s, int px, int py);
static void	flip_selected(void);
static void	flip_search(F_line *p, int type, int x, int y, int px, int py);
static void	flip_arc (F_arc *a, int x, int y, int flip_axis);
static void	flip_ellipse (F_ellipse *e, int x, int y, int flip_axis);
static void	flip_line (F_line *l, int x, int y, int flip_axis);
static void	flip_spline (F_spline *s, int x, int y, int flip_axis);


void
flip_ud_selected(void)
{
    flip_axis = UD_FLIP;
    /* erase any existing anchor */
    if (setanchor)
	center_marker(setanchor_x, setanchor_y);
    /* and any center */
    if (setcenter)
	center_marker(setcenter_x, setcenter_y);
    setcenter = 0;
    setanchor = 0;
    flip_selected();
    reset_action_on();
}

void
flip_lr_selected(void)
{
    flip_axis = LR_FLIP;
    /* erase any existing anchor */
    if (setanchor)
	center_marker(setanchor_x, setanchor_y);
    /* and any center */
    if (setcenter)
	center_marker(setcenter_x, setcenter_y);
    setcenter = 0;
    setanchor = 0;
    flip_selected();
    reset_action_on();
}

static void
flip_selected(void)
{
    set_mousefun("flip", "copy & flip", "set anchor",
			LOC_OBJ, LOC_OBJ, "set anchor");
    canvas_kbd_proc = null_proc;
    canvas_locmove_proc = null_proc;
    canvas_ref_proc = null_proc;
    init_searchproc_left(init_flip);
    init_searchproc_middle(init_copynflip);
    canvas_leftbut_proc = object_search_left;
    canvas_middlebut_proc = object_search_middle;
    canvas_rightbut_proc = set_unset_anchor;
    set_cursor(pick15_cursor);
}

static void
set_unset_anchor(int x, int y)
{
    if (setanchor) {
      set_mousefun("flip", "copy & flip", "set anchor",
			LOC_OBJ, LOC_OBJ, "set anchor");
      draw_mousefun_canvas();
      setanchor = 0;
      center_marker(setanchor_x,setanchor_y);
      /* second call to center_mark on same position deletes */
    }
    else {
      set_mousefun("flip", "copy & flip", "unset anchor",
			LOC_OBJ, LOC_OBJ, "unset anchor");
      draw_mousefun_canvas();
      setanchor = 1;
      setanchor_x = x;
      setanchor_y = y;
      center_marker(setanchor_x,setanchor_y);
    }

}

static void
init_flip(F_line *p, int type, int x, int y, int px, int py)
{
    copy = 0;
    if (setanchor)
	flip_search(p, type, x, y,setanchor_x,setanchor_y );
	/* remember rotation center, e.g for multiple rotation*/
    else
	flip_search(p, type, x, y, px, py);
}

static void
init_copynflip(F_line *p, int type, int x, int y, int px, int py)
{
    copy = 1;
    if (setanchor)
	flip_search(p, type, x, y,setanchor_x,setanchor_y );
	/* remember rotation center, e.g for multiple rotation*/
    else
	flip_search(p, type, x, y, px, py);
}

static void
flip_search(F_line *p, int type, int x, int y, int px, int py)
{
	(void)x;
	(void)y;

    switch (type) {
    case O_POLYLINE:
	cur_l = (F_line *) p;
	init_flipline(cur_l, px, py);
	break;
    case O_ARC:
	cur_a = (F_arc *) p;
	init_fliparc(cur_a, px, py);
	break;
    case O_ELLIPSE:
	cur_e = (F_ellipse *) p;
	init_flipellipse(cur_e, px, py);
	break;
    case O_SPLINE:
	cur_s = (F_spline *) p;
	init_flipspline(cur_s, px, py);
	break;
    case O_COMPOUND:
	cur_c = (F_compound *) p;
	init_flipcompound(cur_c, px, py);
	break;
    default:
	return;
    }
}

static void
init_fliparc(F_arc *old_a, int px, int py)
{
    F_arc	   *new_a;

    set_temp_cursor(wait_cursor);
    new_a = copy_arc(old_a);
    flip_arc(new_a, px, py, flip_axis);
    if (copy) {
	add_arc(new_a);
    } else {
	toggle_arcmarker(old_a);
	draw_arc(old_a, ERASE);
	change_arc(old_a, new_a);
    }
    /* redisplay objects under this object before it was rotated */
    redisplay_arc(old_a);
    /* and this arc and any other objects on top */
    redisplay_arc(new_a);
    reset_cursor();
}

static void
init_flipcompound(F_compound *old_c, int px, int py)
{
    F_compound	   *new_c;

    set_temp_cursor(wait_cursor);
    new_c = copy_compound(old_c);
    flip_compound(new_c, px, py, flip_axis);
    if (copy) {
	add_compound(new_c);
    } else {
	toggle_compoundmarker(old_c);
	draw_compoundelements(old_c, ERASE);
	change_compound(old_c, new_c);
    }
    /* redisplay objects under this object before it was rotated */
    redisplay_compound(old_c);
    /* and this object and any other objects on top */
    redisplay_compound(new_c);
    reset_cursor();
}

static void
init_flipellipse(F_ellipse *old_e, int px, int py)
{
    F_ellipse	   *new_e;

    new_e = copy_ellipse(old_e);
    flip_ellipse(new_e, px, py, flip_axis);
    if (copy) {
	add_ellipse(new_e);
    } else {
	toggle_ellipsemarker(old_e);
	draw_ellipse(old_e, ERASE);
	change_ellipse(old_e, new_e);
    }
    /* redisplay objects under this object before it was rotated */
    redisplay_ellipse(old_e);
    /* and this object and any other objects on top */
    redisplay_ellipse(new_e);
}

static void
init_flipline(F_line *old_l, int px, int py)
{
    F_line	   *new_l;

    new_l = copy_line(old_l);
    flip_line(new_l, px, py, flip_axis);
    if (copy) {
	add_line(new_l);
    } else {
	toggle_linemarker(old_l);
	draw_line(old_l, ERASE);
	change_line(old_l, new_l);
    }
    /* redisplay objects under this object before it was rotated */
    redisplay_line(old_l);
    /* and this object and any other objects on top */
    redisplay_line(new_l);
}

static void
init_flipspline(F_spline *old_s, int px, int py)
{
    F_spline	   *new_s;

    new_s = copy_spline(old_s);
    flip_spline(new_s, px, py, flip_axis);
    if (copy) {
	add_spline(new_s);
    } else {
	toggle_splinemarker(old_s);
	draw_spline(old_s, ERASE);
	change_spline(old_s, new_s);
    }
    /* redisplay objects under this object before it was rotated */
    redisplay_spline(old_s);
    /* and this object and any other objects on top */
    redisplay_spline(new_s);
}

static void
flip_line(F_line *l, int x, int y, int flip_axis)
{
    F_point	   *p;

    switch (flip_axis) {
    case UD_FLIP:		/* x axis  */
	for (p = l->points; p != NULL; p = p->next)
	    p->y = y + (y - p->y);
	break;
    case LR_FLIP:		/* y axis  */
	for (p = l->points; p != NULL; p = p->next)
	    p->x = x + (x - p->x);
	break;
    }
    if (l->type == T_PICTURE)
	l->pic->flipped = 1 - l->pic->flipped;
}

void flip_spline(F_spline *s, int x, int y, int flip_axis)
{
    F_point	   *p;

    switch (flip_axis) {
    case UD_FLIP:		/* x axis  */
	for (p = s->points; p != NULL; p = p->next)
	    p->y = y + (y - p->y);
	break;
    case LR_FLIP:		/* y axis  */
	for (p = s->points; p != NULL; p = p->next)
	    p->x = x + (x - p->x);
	break;
    }
}

static void
flip_text(F_text *t, int x, int y, int flip_axis)
{
	int		dx, dy;

	/* flip the angle around 2PI */
	t->angle = M_2PI - t->angle;
	switch (flip_axis) {
	case LR_FLIP:		/* left/right around the y axis  */
		t->base_x = x + (x - t->base_x);
		/* switch justification */
		if (t->type == T_LEFT_JUSTIFIED)
			t->type = T_RIGHT_JUSTIFIED;
		else if (t->type == T_RIGHT_JUSTIFIED)
			t->type = T_LEFT_JUSTIFIED;
		font_reload(t);
		font_setbbox_offset(t);
		break;
	case UD_FLIP:		/* up/down around the x axis  */
		/*
		 * Remember the top left corner of the un-flipped text,
		 * compute the bounding box of the text with the new angle,
		 * translate the new text to the flipped position.
		 *   dx = bb[0].x - nbb[0].x
		 *   dy = bb[1].y - nbb[0].y + 2 * (Y - bb[1].y)
		 */
		dx = t->bb[0].x;
		dy = 2 * y - t->bb[1].y;
		font_reload(t);
		font_setbbox_offset(t);
		dx -= t->bb[0].x;
		dy -= t->bb[0].y;
		t->base_x += dx;
		t->base_y += dy;
		t->bb[0].x += dx;
		t->bb[0].y += dy;
		t->bb[1].x += dx;
		t->bb[1].y += dy;
		break;
	}
}

static void
flip_ellipse(F_ellipse *e, int x, int y, int flip_axis)
{
    switch (flip_axis) {
    case UD_FLIP:		/* x axis  */
	e->direction ^= 1;
	e->center.y = y + (y - e->center.y);
	e->start.y = y + (y - e->start.y);
	e->end.y = y + (y - e->end.y);
	break;
    case LR_FLIP:		/* y axis  */
	e->direction ^= 1;
	e->center.x = x + (x - e->center.x);
	e->start.x = x + (x - e->start.x);
	e->end.x = x + (x - e->end.x);
	break;
    }
    e->angle = - e->angle;
}

static void
flip_arc(F_arc *a, int x, int y, int flip_axis)
{
    switch (flip_axis) {
    case UD_FLIP:		/* x axis  */
	a->direction ^= 1;
	a->center.y = y + (y - a->center.y);
	a->point[0].y = y + (y - a->point[0].y);
	a->point[1].y = y + (y - a->point[1].y);
	a->point[2].y = y + (y - a->point[2].y);
	break;
    case LR_FLIP:		/* y axis  */
	a->direction ^= 1;
	a->center.x = x + (x - a->center.x);
	a->point[0].x = x + (x - a->point[0].x);
	a->point[1].x = x + (x - a->point[1].x);
	a->point[2].x = x + (x - a->point[2].x);
	break;
    }
}

void
flip_compound(F_compound *c, int x, int y, int flip_axis)
{
    F_line	   *l;
    F_arc	   *a;
    F_ellipse	   *e;
    F_spline	   *s;
    F_text	   *t;
    F_compound	   *c1;
    int		    p, q;

    switch (flip_axis) {
    case UD_FLIP:		/* x axis  */
	p = y + (y - c->nwcorner.y);
	q = y + (y - c->secorner.y);
	c->nwcorner.y = min2(p, q);
	c->secorner.y = max2(p, q);
	break;
    case LR_FLIP:		/* y axis  */
	p = x + (x - c->nwcorner.x);
	q = x + (x - c->secorner.x);
	c->nwcorner.x = min2(p, q);
	c->secorner.x = max2(p, q);
	break;
    }
    for (l = c->lines; l != NULL; l = l->next)
	flip_line(l, x, y, flip_axis);
    for (a = c->arcs; a != NULL; a = a->next)
	flip_arc(a, x, y, flip_axis);
    for (e = c->ellipses; e != NULL; e = e->next)
	flip_ellipse(e, x, y, flip_axis);
    for (s = c->splines; s != NULL; s = s->next)
	flip_spline(s, x, y, flip_axis);
    for (t = c->texts; t != NULL; t = t->next)
	flip_text(t, x, y, flip_axis);
    for (c1 = c->compounds; c1 != NULL; c1 = c1->next)
	flip_compound(c1, x, y, flip_axis);
}
