/*
 * 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) 1995 by C. Blanc and C. Schlick
 * 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.
 *
 */

#include "e_convert.h"

#include <stdlib.h>

#include "resources.h"
#include "mode.h"
#include "object.h"
#include "paintop.h"
#include "d_spline.h"
#include "f_util.h"
#include "u_create.h"
#include "u_draw.h"
#include "u_free.h"
#include "u_list.h"
#include "u_markers.h"
#include "u_redraw.h"
#include "u_search.h"
#include "u_undo.h"
#include "w_canvas.h"
#include "w_cursor.h"
#include "w_mousefun.h"
#include "w_msgpanel.h"


static void	init_convert_line_spline(F_line *p, int type, int x, int y,
					int px, int py);
static void	init_convert_open_closed(F_line *obj, int type, int x, int y,
					F_point *p, F_point *q);



void
convert_selected(void)
{
    set_mousefun("spline<->line", "", "open<->closed", LOC_OBJ, LOC_OBJ, LOC_OBJ);
    canvas_kbd_proc = null_proc;
    canvas_locmove_proc = null_proc;
    canvas_ref_proc = null_proc;
    init_searchproc_left(init_convert_line_spline);
    init_searchproc_right(init_convert_open_closed);
    canvas_leftbut_proc = object_search_left;
    canvas_middlebut_proc = null_proc;
    canvas_rightbut_proc = point_search_right;
    set_cursor(pick15_cursor);
    reset_action_on();
}

static void
init_convert_open_closed(F_line *obj, int type, int x, int y, F_point *p,
			F_point *q)
{
	(void)x;
	(void)y;

    switch (type) {
    case O_POLYLINE:
      cur_l = (F_line *) obj;
      toggle_polyline_polygon(cur_l, p, q);
	break;
    case O_SPLINE:
      cur_s = (F_spline *) obj;
      toggle_open_closed_spline(cur_s, p, q);
	break;
    default:
	return;
    }
}

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

    static int flag = 0;

    switch (type) {
    case O_POLYLINE:
	cur_l = (F_line *) p;
	/* the search routine will ensure that we don't have a box */
	if (cur_l->type == T_POLYLINE || cur_l->type == T_POLYGON) {
	    line_spline(cur_l, cur_l->type == T_POLYGON ?
		    flag ? T_CLOSED_APPROX : T_CLOSED_INTERP :
		    flag ? T_OPEN_APPROX : T_OPEN_INTERP);
	} else if (cur_l->type == T_ARCBOX || cur_l->type == T_BOX) {
		box_2_box(cur_l);
	}
	break;
    case O_SPLINE:
	cur_s = (F_spline *) p;
	flag = (cur_s->type==T_OPEN_INTERP) || (cur_s->type==T_CLOSED_INTERP);
	spline_line(cur_s);
	break;
    default:
	return;
    }
}

/* handle conversion of box to arc_box and arc_box to box */

void
box_2_box(F_line *old_l)
{
    F_line	   *new_l;

    new_l = copy_line(old_l);
    switch (old_l->type) {
      case T_BOX:
	new_l->type = T_ARCBOX;
	if (new_l->radius == DEFAULT || new_l->radius == 0)
	    new_l->radius = cur_boxradius;
	break;
      case T_ARCBOX:
	new_l->type = T_BOX;
	break;
    }
    list_delete_line(&objects.lines, old_l);
    list_add_line(&objects.lines, new_l);
    clean_up();
    old_l->next = new_l;
    set_latestline(old_l);
    set_action_object(F_CONVERT, O_POLYLINE);
    set_modifiedflag();
    /* save pointer to this line for undo */
    latest_line = new_l;
    redisplay_line(new_l);
    return;
}

void
line_spline(F_line *l, int type_value)
{
    F_spline	   *s;

    if (num_points(l->points) < CLOSED_SPLINE_MIN_NUM_POINTS) {
	put_msg("Not enough points for a spline");
	beep();
	return;
    }

    if ((s = create_spline()) == NULL)
        return;
    s->type = type_value;

    if (l->type == T_POLYGON)
	s->points = copy_points(l->points->next);
    else
	s->points = copy_points(l->points);

    s->style = l->style;
    s->thickness = l->thickness;
    s->pen_color = l->pen_color;
    s->fill_color = l->fill_color;
    s->depth = l->depth;
    s->style_val = l->style_val;
    s->cap_style = l->cap_style;
    s->pen_style = l->pen_style;
    s->fill_style = l->fill_style;
    s->sfactors = NULL;
    s->next = NULL;

    if (l->for_arrow) {
	s->for_arrow = create_arrow();
	s->for_arrow->type = l->for_arrow->type;
	s->for_arrow->style = l->for_arrow->style;
	s->for_arrow->thickness = l->for_arrow->thickness;
	s->for_arrow->wd = l->for_arrow->wd;
	s->for_arrow->ht = l->for_arrow->ht;
    } else {
	s->for_arrow = NULL;
    }
    if (l->back_arrow) {
	s->back_arrow = create_arrow();
	s->back_arrow->type = l->back_arrow->type;
	s->back_arrow->style = l->back_arrow->style;
	s->back_arrow->thickness = l->back_arrow->thickness;
	s->back_arrow->wd = l->back_arrow->wd;
	s->back_arrow->ht = l->back_arrow->ht;
    } else {
	s->back_arrow = NULL;
    }
    /* A spline must have an s parameter for each point */
    if (!make_sfactors(s)) {
	free_spline(&s);
	return;
    }

    /* Get rid of the line and draw the new spline */
    delete_line(l);
    /* now put back the new spline */
    mask_toggle_splinemarker(s);
    list_add_spline(&objects.splines, s);
    redisplay_spline(s);
    set_action_object(F_CONVERT, O_POLYLINE);
    set_latestspline(s);
    set_modifiedflag();
}

void
spline_line(F_spline *s)
{
    F_line	   *l;
    F_point        *tmppoint;

    /* Now we turn s into a line */
    if ((l = create_line()) == NULL)
	return;

    if (open_spline(s)) {
	l->type = T_POLYLINE;
	l->points = s->points;
    } else {
	l->type = T_POLYGON;
	if ((l->points = create_point())==NULL)
	    return;
	tmppoint = last_point(s->points);
	l->points->x = tmppoint->x;
	l->points->y = tmppoint->y;
	l->points->next = copy_points(s->points);
    }
    l->style = s->style;
    l->thickness = s->thickness;
    l->pen_color = s->pen_color;
    l->fill_color = s->fill_color;
    l->depth = s->depth;
    l->style_val = s->style_val;
    l->cap_style = s->cap_style;
    l->join_style = cur_joinstyle;
    l->pen_style = s->pen_style;
    l->radius = DEFAULT;
    l->fill_style = s->fill_style;
    if (s->for_arrow) {
	l->for_arrow = create_arrow();
	l->for_arrow->type = s->for_arrow->type;
	l->for_arrow->style = s->for_arrow->style;
	l->for_arrow->thickness = s->for_arrow->thickness;
	l->for_arrow->wd = s->for_arrow->wd;
	l->for_arrow->ht = s->for_arrow->ht;
    } else {
	l->for_arrow = NULL;
    }
    if (s->back_arrow) {
	l->back_arrow = create_arrow();
	l->back_arrow->type = s->back_arrow->type;
	l->back_arrow->style = s->back_arrow->style;
	l->back_arrow->thickness = s->back_arrow->thickness;
	l->back_arrow->wd = s->back_arrow->wd;
	l->back_arrow->ht = s->back_arrow->ht;
    } else {
	l->back_arrow = NULL;
    }

    /* now we have finished creating the line, we can get rid of the spline */
    delete_spline(s);

    /* and put in the new line */
    mask_toggle_linemarker(l);
    list_add_line(&objects.lines, l);
    redisplay_line(l);
    set_action_object(F_CONVERT, O_SPLINE);
    set_latestline(l);
    set_modifiedflag();
    return;
}

void
toggle_polyline_polygon(F_line *line, F_point *previous_point, F_point *selected_point)
{
  F_point *point, *last_pt;

  last_pt = last_point(line->points);

  if (line->type == T_POLYLINE)
    {

      if (line->points->next == NULL || line->points->next->next == NULL) {
	  put_msg("Not enough points for a polygon");
	  beep();
	  return;  /* less than 3 points - don't close the polyline */
      }
      if ((point = create_point()) == NULL)
	  return;

      point->x = last_pt->x;
      point->y = last_pt->y;
      point->next = line->points;
      line->points = point;

      line->type = T_POLYGON;
      clean_up();
      set_last_arrows(line->for_arrow, line->back_arrow);
      line->back_arrow = line->for_arrow = NULL;
    }
  else if (line->type == T_POLYGON)
    {
      point = line->points;
      line->points = point->next;           /* unchain the first point */
      free((char *) point);

      if ((line->points != selected_point) && (previous_point != NULL))
	{
	  last_pt->next = line->points;     /* let selected point become */
	  previous_point->next = NULL;      /* first point */
	  line->points = selected_point;
	}
      line->type = T_POLYLINE;
      clean_up();
    }
  redisplay_line(line);
  set_action_object(F_OPEN_CLOSE, O_POLYLINE);
  set_last_selectedpoint(line->points);
  set_last_prevpoint(NULL);
  set_latestline(line);
  set_modifiedflag();
}


void
toggle_open_closed_spline(F_spline *spline, F_point *previous_point, F_point *selected_point)
{
  F_point *last_pt;
  F_sfactor *last_sfactor, *previous_sfactor, *selected_sfactor;

  if (spline->points->next == NULL || spline->points->next->next == NULL) {
      put_msg("Not enough points for a spline");
      beep();
      return;  /* less than 3 points - don't close the spline */
  }

  last_pt = last_point(spline->points);
  last_sfactor = search_sfactor(spline, last_pt);

  if (previous_point == NULL)
    {
      previous_sfactor = NULL;
      selected_sfactor = spline->sfactors;
    }
  else
    {
      previous_sfactor = search_sfactor(spline, previous_point);
      selected_sfactor = previous_sfactor->next;
      set_last_tension(selected_sfactor->s, previous_sfactor->s);
    }

  draw_spline(spline, ERASE);

  if (closed_spline(spline))
    {
      if (spline->points != selected_point)
	{
	  last_pt->next = spline->points;
	  last_sfactor->next = spline->sfactors;
	  previous_point->next = NULL;
	  previous_sfactor->next = NULL;
	  previous_sfactor->s = S_SPLINE_ANGULAR;
	  spline->points = selected_point;
	  spline->sfactors = selected_sfactor;
	}
      else
	{
	  last_sfactor->s = S_SPLINE_ANGULAR;
	}
      spline->sfactors->s = S_SPLINE_ANGULAR;
      spline->type = (x_spline(spline)) ? T_OPEN_XSPLINE :
	(int_spline(spline)) ? T_OPEN_INTERP : T_OPEN_APPROX;
      clean_up();
    }
  else
    {
      int type_tmp;
      double s_tmp;

      if(int_spline(spline))
	{
	  s_tmp = S_SPLINE_INTERP;
	  type_tmp = T_CLOSED_INTERP;
	}
      else
	if (x_spline(spline))
	  {
	      s_tmp = S_SPLINE_INTERP;
	      type_tmp = T_CLOSED_XSPLINE;
	    }
	else
	  {
	    s_tmp = S_SPLINE_APPROX;
	    type_tmp = T_CLOSED_APPROX;
	  }
      spline->sfactors->s = last_sfactor->s = s_tmp;
      spline->type = type_tmp;
      clean_up();
      set_last_arrows(spline->for_arrow, spline->back_arrow);
      spline->back_arrow = spline->for_arrow = NULL;
    }
  draw_spline(spline, PAINT);
  set_action_object(F_OPEN_CLOSE, O_SPLINE);
  set_last_selectedpoint(spline->points);
  set_last_prevpoint(NULL);
  set_latestspline(spline);
  set_modifiedflag();
}

