/* Dia -- an diagram creation/manipulation program
 * Copyright (C) 1998 Alexander Larsson
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <assert.h>
#include <math.h>

#include "intl.h"
#include "object.h"
#include "connection.h"
#include "connectionpoint.h"
#include "diarenderer.h"
#include "attributes.h"
#include "widgets.h"
#include "arrows.h"
#include "properties.h"

#include "tool-icons.h"

#define DEFAULT_WIDTH 0.25

#define HANDLE_MIDDLE HANDLE_CUSTOM1

/* If you wan debug spew */
#define TRACE(fun) /* fun */

typedef struct _Arc Arc;

struct _Arc {
  Connection connection;

  Handle middle_handle;

  Color arc_color;
  real curve_distance;
  real line_width;
  LineStyle line_style;
  real dashlength;
  Arrow start_arrow, end_arrow;

  /* Calculated parameters: */
  real radius;
  Point center;
  real angle1, angle2;

};

/* updates both endpoints and arc->curve_distance */
static ObjectChange* arc_move_handle(Arc *arc, Handle *handle,
				     Point *to, ConnectionPoint *cp,
				     HandleMoveReason reason, ModifierKeys modifiers);
static ObjectChange* arc_move(Arc *arc, Point *to);
static void arc_select(Arc *arc, Point *clicked_point,
		       DiaRenderer *interactive_renderer);
static void arc_draw(Arc *arc, DiaRenderer *renderer);
static DiaObject *arc_create(Point *startpoint,
			  void *user_data,
			  Handle **handle1,
			  Handle **handle2);
static real arc_distance_from(Arc *arc, Point *point);
static void arc_update_data(Arc *arc);
static void arc_update_handles(Arc *arc);
static void arc_destroy(Arc *arc);
static DiaObject *arc_copy(Arc *arc);

static PropDescription *arc_describe_props(Arc *arc);
static void arc_get_props(Arc *arc, GPtrArray *props);
static void arc_set_props(Arc *arc, GPtrArray *props);

static void arc_save(Arc *arc, ObjectNode obj_node, const char *filename);
static DiaObject *arc_load(ObjectNode obj_node, int version, const char *filename);
static int arc_compute_midpoint(Arc *arc, const Point * ep0, const Point * ep1 , Point * midpoint);
static void calculate_arc_object_edge(Arc *arc, real ang_start, real ang_end, DiaObject *obj, Point *target, gboolean clockwiseness);
static void arc_get_point_at_angle(Arc *arc, Point* point, real angle);
real round_angle(real angle);
real get_middle_arc_angle(real angle1, real angle2, gboolean clock);

static ObjectTypeOps arc_type_ops =
{
  (CreateFunc) arc_create,
  (LoadFunc)   arc_load,
  (SaveFunc)   arc_save,
  (GetDefaultsFunc)   NULL,
  (ApplyDefaultsFunc) NULL
};

DiaObjectType arc_type =
{
  "Standard - Arc",  /* name */
  0,                 /* version */
  (char **) arc_icon, /* pixmap */
  
  &arc_type_ops      /* ops */
};

DiaObjectType *_arc_type = (DiaObjectType *) &arc_type;

static ObjectOps arc_ops = {
  (DestroyFunc)         arc_destroy,
  (DrawFunc)            arc_draw,
  (DistanceFunc)        arc_distance_from,
  (SelectFunc)          arc_select,
  (CopyFunc)            arc_copy,
  (MoveFunc)            arc_move,
  (MoveHandleFunc)      arc_move_handle,
  (GetPropertiesFunc)   object_create_props_dialog,
  (ApplyPropertiesFunc) object_apply_props_from_dialog,
  (ObjectMenuFunc)      NULL,
  (DescribePropsFunc)   arc_describe_props,
  (GetPropsFunc)        arc_get_props,
  (SetPropsFunc)        arc_set_props,
};

static PropDescription arc_props[] = {
  OBJECT_COMMON_PROPERTIES,
  PROP_STD_LINE_WIDTH,
  PROP_STD_LINE_COLOUR,
  PROP_STD_LINE_STYLE,
  PROP_STD_START_ARROW,
  PROP_STD_END_ARROW,
  { "curve_distance", PROP_TYPE_REAL, 0,
    N_("Curve distance"), NULL },
  PROP_DESC_END
};

static PropDescription *
arc_describe_props(Arc *arc)
{
  if (arc_props[0].quark == 0)
    prop_desc_list_calculate_quarks(arc_props);
  return arc_props;
}

static PropOffset arc_offsets[] = {
  OBJECT_COMMON_PROPERTIES_OFFSETS,
  { "line_width", PROP_TYPE_REAL, offsetof(Arc, line_width) },
  { "line_colour", PROP_TYPE_COLOUR, offsetof(Arc, arc_color) },
  { "line_style", PROP_TYPE_LINESTYLE,
    offsetof(Arc, line_style), offsetof(Arc, dashlength) },
  { "start_arrow", PROP_TYPE_ARROW, offsetof(Arc, start_arrow) },
  { "end_arrow", PROP_TYPE_ARROW, offsetof(Arc, end_arrow) },
  { "curve_distance", PROP_TYPE_REAL, offsetof(Arc, curve_distance) },
  { "start_point", PROP_TYPE_POINT, offsetof(Connection, endpoints[0]) },
  { "end_point", PROP_TYPE_POINT, offsetof(Connection, endpoints[1]) },
  { NULL, 0, 0 }
};

static void
arc_get_props(Arc *arc, GPtrArray *props)
{
  object_get_props_from_offsets(&arc->connection.object, 
                                arc_offsets, props);
}

static void
arc_set_props(Arc *arc, GPtrArray *props)
{
  object_set_props_from_offsets(&arc->connection.object, 
                                arc_offsets, props);
  arc_update_data(arc);
}

static int
in_angle(real angle, real startangle, real endangle)
{
  if (startangle > endangle) {  /* passes 360 degrees */
    endangle += 360.0;
    if (angle<startangle)
      angle += 360;
  }
  return (angle>=startangle) && (angle<=endangle);
}

static real
arc_distance_from(Arc *arc, Point *point)
{
  Point *endpoints;
  Point from_center;
  real angle;
  real d, d2;
  
  endpoints = &arc->connection.endpoints[0];

  from_center = *point;
  point_sub(&from_center, &arc->center);

  angle = -atan2(from_center.y, from_center.x)*180.0/M_PI;
  if (angle<0)
    angle+=360.0;

  if (in_angle(angle, arc->angle1, arc->angle2)) {
    d = fabs(sqrt(point_dot(&from_center, &from_center)) - arc->radius);
    d -= arc->line_width/2.0;
    if (d<0)
      d = 0.0;
    return d;
  } else {
    d = distance_point_point(&endpoints[0], point);
    d2 = distance_point_point(&endpoints[1], point);

    return MIN(d,d2);
  }
}

static void
arc_select(Arc *arc, Point *clicked_point,
	   DiaRenderer *interactive_renderer)
{
  arc_update_handles(arc);
}

static void
arc_update_handles(Arc *arc)
{
  Point *middle_pos;
  real dist,dx,dy;

  Connection *conn = &arc->connection;

  connection_update_handles(conn);
  
  middle_pos = &arc->middle_handle.pos;

  dx = conn->endpoints[1].x - conn->endpoints[0].x;
  dy = conn->endpoints[1].y - conn->endpoints[0].y;
  
  dist = sqrt(dx*dx + dy*dy);
  if (dist > 0.000001) {
    middle_pos->x =
      (conn->endpoints[0].x + conn->endpoints[1].x) / 2.0 -
      arc->curve_distance*dy/dist;
    middle_pos->y =
      (conn->endpoints[0].y + conn->endpoints[1].y) / 2.0 +
      arc->curve_distance*dx/dist;
  }
}
/** returns the number of intersection the circle has with the horizontal line at y=horiz
 * if 1 point intersects then *int1 is that point
 * if 2 points intersect then *int1 and *int2 are these points
 */
static int
arc_circle_intersects_horiz(const Arc *arc, real horiz, Point *int1, Point *int2){
  /* inject y=horiz into r^2 = (x-x_c)^2 + (y-y_c)^2 
   * this is r^2 = (x-x_c)^2 + (horiz-y_c)^2 
   * translate to x^2 + b*x + c = 0 
   * b = -2 x_c 
   * c = x_c^2 - r^2 + (horiz-y_c)^2 
   * and solve classically */
  real b, c, delta;
  b = -2.0 * arc->center.x;
  c =  arc->center.x * arc->center.x + (horiz - arc->center.y) * (horiz - arc->center.y) - arc->radius * arc->radius;
  delta = b*b - 4 * c;
  if (delta < 0)
          return 0;
  else if (delta == 0){
         int1->x = -b/2; 
         int1->y = horiz;
         return 1; 
  }
  else {
         int1->x = (sqrt(delta)-b)/2; 
         int1->y = horiz;
         int2->x = (-sqrt(delta)-b)/2; 
         int2->y = horiz;
          return 2;
  }
}
/** returns the number of intersection the circle has with the vertical line at x=vert
 * if 1 point intersects then *int1 is that point
 * if 2 points intersect then *int1 and *int2 are these points
 */
static int
arc_circle_intersects_vert(const Arc *arc, real vert, Point *int1, Point *int2){
  /* inject x=vert into r^2 = (x-x_c)^2 + (y-y_c)^2 
   * this is r^2 = (vert-x_c)^2 + (y-y_c)^2 
   * translate to y^2 + b*y + c = 0 and solve classically */
  real b, c, delta;
  b = -2*arc->center.y;
  c =  arc->center.y * arc->center.y + (vert - arc->center.x) * (vert - arc->center.x) - arc->radius * arc->radius;
  delta = b*b - 4 * c;
  if (delta < 0)
          return 0;
  else if (delta == 0){
         int1->y = -b/2; 
         int1->x = vert;
         return 1; 
  }
  else {
         int1->y = (sqrt(delta)-b)/2; 
         int1->x = vert;
         int2->y = (-sqrt(delta)-b)/2; 
         int2->x = vert;
         return 2;
  }
}
        
        
            

static real
arc_compute_curve_distance(const Arc *arc, const Point *start, const Point *end, const Point *mid)
{
    Point a,b;
    real tmp,cd;

    b = *mid;
    point_sub(&b, start);
   
    a = *end;
    point_sub(&a, start);

    tmp = point_dot(&a,&b);
    cd =
      sqrt(fabs(point_dot(&b,&b) - tmp*tmp/point_dot(&a,&a)));
    
    if (a.x*b.y - a.y*b.x < 0) 
      cd = - cd;
    return cd;
}

/** rotates p around the center by an angle given in radians 
 * a positive angle is ccw on the screen*/
static void
rotate_point_around_point(Point *p, const Point *center, real angle)
{
        real radius;
        real a;
        point_sub(p,center);
        radius = point_len(p);
        a = -atan2(p->y,p->x); /* y axis points down*/
        a += angle;
        p->x = cos(a); p->y = -sin(a);/* y axis points down*/
        point_scale(p,radius);
        point_add(p,center);
}


/* finds the point intersecting the full circle 
 * on the vector defined by the center and Point *to
 * that point is returned in Point *best if 1 is returned */
static int
arc_find_radial(const Arc *arc, const Point *to, Point *best)
{
        Point tmp;
        tmp = *to;
        point_sub(&tmp, &arc->center);
        point_normalize(&tmp);
        point_scale(&tmp,arc->radius);
        point_add(&tmp, &arc->center);
        *best = tmp;
        return 1;
        
}

/* finds the closest point intersecting the full circle 
 * at any position on the vertical and horizontal lines going through Point to
 * that point is returned in Point *best if 1 is returned */
static int
arc_find_closest_vert_horiz(const Arc *arc, const Point *to, Point *best){
     Point i1,i2;
     int nh,nv;
        nh = arc_circle_intersects_horiz(arc, to->y, &i1, &i2);
        if (nh==2){
           *best = *closest_to(to,&i1,&i2);
        }
        else if (nh==1) {
           *best = i1;
        }
        nv = arc_circle_intersects_vert(arc, to->x, &i1, &i2);
        if (nv==2){
           Point tmp;
           tmp = *closest_to(to,&i1,&i2);
           *best = *closest_to(to,&tmp,best);
        }
        else if (nv==1) {
           *best = *closest_to(to,&i1,best);
        }
        if (nv|nh)
                return 1;
        return 0;
}
static ObjectChange*
arc_move_handle(Arc *arc, Handle *handle,
		Point *to, ConnectionPoint *cp,
		HandleMoveReason reason, ModifierKeys modifiers)
{
  assert(arc!=NULL);
  assert(handle!=NULL);
  assert(to!=NULL);
  if (handle->id == HANDLE_MIDDLE) {
          TRACE(printf("curve_dist: %.2f \n",arc->curve_distance));
          arc->curve_distance = arc_compute_curve_distance(arc, &arc->connection.endpoints[0], &arc->connection.endpoints[1], to);
          TRACE(printf("curve_dist: %.2f \n",arc->curve_distance));

  } else {
        Point best;
        TRACE(printf("Modifiers: %d \n",modifiers));
        if (modifiers & MODIFIER_SHIFT)
        /* if(arc->end_arrow.type == ARROW_NONE)*/
        {
          TRACE(printf("SHIFT USED, to at %.2f %.2f  ",to->x,to->y));
          if (arc_find_radial(arc, to, &best)){
            /* needs to move two handles at the same time 
             * compute pos of middle handle */
            Point midpoint;
            int ok;
            if (handle == (&arc->connection.endpoint_handles[0]))
              ok = arc_compute_midpoint(arc, &best , &arc->connection.endpoints[1], &midpoint);
            else
              ok = arc_compute_midpoint(arc,  &arc->connection.endpoints[0], &best , &midpoint);
            if (!ok)
              return NULL;
            connection_move_handle(&arc->connection, handle->id, &best, cp, reason, modifiers);
            /* recompute curve distance equiv. move middle handle */
            arc->curve_distance = arc_compute_curve_distance(arc, &arc->connection.endpoints[0], &arc->connection.endpoints[1], &midpoint);
            TRACE(printf("curve_dist: %.2f \n",arc->curve_distance));
          }
          else {
            TRACE(printf("NO best\n"));
          }
       } else {
          connection_move_handle(&arc->connection, handle->id, to, cp, reason, modifiers);
       }
  }

  arc_update_data(arc);

  return NULL;
}

static ObjectChange*
arc_move(Arc *arc, Point *to)
{
  Point start_to_end;
  Point *endpoints = &arc->connection.endpoints[0]; 

  start_to_end = endpoints[1];
  point_sub(&start_to_end, &endpoints[0]);

  endpoints[1] = endpoints[0] = *to;
  point_add(&endpoints[1], &start_to_end);

  arc_update_data(arc);

  return NULL;
}

static int 
arc_compute_midpoint(Arc *arc, const Point * ep0, const Point * ep1 , Point * midpoint)
{
            real angle;
            Point midpos;
            Point *oep0, *oep1;
            
            oep0 = &arc->connection.endpoints[0];
            oep1 = &arc->connection.endpoints[1];

            /* angle is total delta of angle of both endpoints */
            angle = -atan2(ep0->y - arc->center.y, ep0->x - arc->center.x); /* angle of new */
            angle -= -atan2(oep0->y - arc->center.y, oep0->x - arc->center.x); /* minus angle of old */
            angle += -atan2(ep1->y - arc->center.y, ep1->x - arc->center.x); /* plus angle of new */
            angle -= -atan2(oep1->y - arc->center.y, oep1->x - arc->center.x); /* minus angle of old */
            if (!finite(angle)){
                    return 0;
            }
            if (angle < -1 * M_PI){
                    TRACE(printf("angle: %.2f ",angle));
                    angle += 2*M_PI;
                    TRACE(printf("angle: %.2f ",angle));
            }
            if (angle > 1 * M_PI){
                    TRACE(printf("angle: %.2f ",angle));
                    angle -= 2*M_PI;
                    TRACE(printf("angle: %.2f ",angle));
            }

            midpos = arc->middle_handle.pos;
            /*rotate middle handle by half the angle */
            TRACE(printf("\nmidpos before: %.2f %.2f \n",midpos.x, midpos.y));
            rotate_point_around_point(&midpos, &arc->center, angle/2); 
            TRACE(printf("\nmidpos after : %.2f %.2f \n",midpos.x, midpos.y));
            *midpoint = midpos;
            return 1;
}
/** updates point to the point on the arc at angle angle degrees */
void arc_get_point_at_angle(Arc *arc, Point* point, real angle)
{
        Point vec;
        vec.x = cos(angle/180.0*M_PI);
        vec.y = -sin(angle/180.0*M_PI);
        point_copy(point,&arc->center);
        point_add_scaled(point,&vec,arc->radius);
}
/** returns the angle in [0,360[ corresponding to this angle*/
real round_angle(real angle){
        real a = angle;
        while (a<0) a+=360;
        while (a>=360) a-=360;
        return a;
}
/** returns the angle in the middle from angle1 to angle2*/
real get_middle_arc_angle(real angle1, real angle2, gboolean clock)
{
        real delta;
        angle1 = round_angle(angle1);
        angle2 = round_angle(angle2);
        delta = (angle2-angle1);
        if (delta<0) delta+=360;
        if (clock) 
                return round_angle(angle1-(360-delta)/2);
        else  
                return round_angle(angle1+delta/2);
}

#undef TRACE_DIST
/* PRE: ang_start should be outside object.
 *      ang_end should be inside
 *      if both are inside or if ang_start is very close , then the point at ang_start is returned
 */
static void
calculate_arc_object_edge(Arc *arc, real ang_start, real ang_end, DiaObject *obj, Point *target, gboolean clockwiseness) 
{
#define MAXITER 25
#ifdef TRACE_DIST
  real trace[MAXITER];
  real disttrace[MAXITER];
#endif
  real mid1, mid2, mid3;
  real dist;
  int i = 0;
  int j = 0;

  mid1 = ang_start;
  mid2 = get_middle_arc_angle(ang_start, ang_end, clockwiseness);
  mid3 = ang_end;

  TRACE(printf("Find middle angle between %f° and  %f°\n",ang_start,ang_end));
  /* If the other end is inside the object */
  arc_get_point_at_angle(arc,target,mid1);
  dist = obj->ops->distance_from(obj, target );
  if (dist < 0.001){
          TRACE(printf("Point at %f°: %f,%f is very close to object: %f, returning it\n",mid1, target->x, target->y, dist)); 
          return ;
  }
  do {
    arc_get_point_at_angle(arc, target, mid2);
    dist = obj->ops->distance_from(obj, target);
#ifdef TRACE_DIST
    trace[i] = mid2;
    disttrace[i] = dist;
#endif
    i++;
    
    if (dist < 0.0000001) {
      mid3 = mid2;
    } else {
      mid1 = mid2;
    }
    mid2 = get_middle_arc_angle(mid1,mid3,clockwiseness);
    
  } while (i < MAXITER && (dist < 0.0000001 || dist > 0.001));
  
#ifdef TRACE_DIST
    for (j = 0; j < i; j++) {
      arc_get_point_at_angle(arc,target,trace[j]);
      printf("%d: %f ° : %f,%f :%f\n", j, trace[j],target->x,target->y, disttrace[j]);
    }
#endif
  arc_get_point_at_angle(arc,target,mid2);
  return ;
}
static void
arc_draw(Arc *arc, DiaRenderer *renderer)
{
  DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
  Point *endpoints;
  Point gaptmp[3];
  ConnectionPoint *start_cp, *end_cp;  
    
  assert(arc != NULL);
  assert(renderer != NULL);

  endpoints = &arc->connection.endpoints[0];

  gaptmp[0] = endpoints[0];
  gaptmp[1] = endpoints[1];
  start_cp = arc->connection.endpoint_handles[0].connected_to;
  end_cp = arc->connection.endpoint_handles[1].connected_to;

  arc_update_data(arc);
  TRACE(printf("drawing arc:\n start:%f °:%f,%f \tend:%f °:%f,%f\n",arc->angle1,endpoints[0].x,endpoints[0].y, arc->angle2,endpoints[1].x,endpoints[1].y));

  if (connpoint_is_autogap(start_cp)) {
     TRACE(printf("computing start intersection\ncurve_distance: %f\n",arc->curve_distance));
     if (arc->curve_distance < 0)
             calculate_arc_object_edge(arc, arc->angle1, arc->angle2, start_cp->object, &gaptmp[0], FALSE);
     else
             calculate_arc_object_edge(arc, arc->angle2, arc->angle1, start_cp->object, &gaptmp[0], TRUE);
  }
  if (connpoint_is_autogap(end_cp)) {
     TRACE(printf("computing end intersection\ncurve_distance: %f\n",arc->curve_distance));
     if (arc->curve_distance < 0)
             calculate_arc_object_edge(arc, arc->angle2, arc->angle1, end_cp->object, &gaptmp[1], TRUE);
     else
             calculate_arc_object_edge(arc, arc->angle1, arc->angle2, end_cp->object, &gaptmp[1], FALSE);
  }

  /* compute new middle_point */
  arc_compute_midpoint(arc, &gaptmp[0], &gaptmp[1], &gaptmp[2]); 

  renderer_ops->set_linewidth(renderer, arc->line_width);
  renderer_ops->set_linestyle(renderer, arc->line_style);
  renderer_ops->set_dashlength(renderer, arc->dashlength);
  renderer_ops->set_linecaps(renderer, LINECAPS_BUTT);
  
  /* Special case when almost line: */
  if (fabs(arc->curve_distance) <= 0.01) {
          TRACE(printf("drawing like a line\n")); 
    renderer_ops->draw_line_with_arrows(renderer,
					 &gaptmp[0], &gaptmp[1],
					 arc->line_width,
					 &arc->arc_color,
					 &arc->start_arrow,
					 &arc->end_arrow);
    return;
  }

  renderer_ops->draw_arc_with_arrows(renderer,
				      &gaptmp[0],
				      &gaptmp[1],
				      &gaptmp[2],
				      arc->line_width,
				      &arc->arc_color,
				      &arc->start_arrow,
				      &arc->end_arrow);
}

static DiaObject *
arc_create(Point *startpoint,
	   void *user_data,
	   Handle **handle1,
	   Handle **handle2)
{
  Arc *arc;
  Connection *conn;
  DiaObject *obj;
  Point defaultlen = { 1.0, 1.0 };

  arc = g_malloc0(sizeof(Arc));

  arc->line_width =  attributes_get_default_linewidth();
  arc->curve_distance = 1.0;
  arc->arc_color = attributes_get_foreground(); 
  attributes_get_default_line_style(&arc->line_style, &arc->dashlength);
  arc->start_arrow = attributes_get_default_start_arrow();
  arc->end_arrow = attributes_get_default_end_arrow();

  conn = &arc->connection;
  conn->endpoints[0] = *startpoint;
  conn->endpoints[1] = *startpoint;
  point_add(&conn->endpoints[1], &defaultlen);
 
  obj = &conn->object;
  
  obj->type = &arc_type;;
  obj->ops = &arc_ops;
  
  connection_init(conn, 3, 0);

  obj->handles[2] = &arc->middle_handle;
  arc->middle_handle.id = HANDLE_MIDDLE;
  arc->middle_handle.type = HANDLE_MINOR_CONTROL;
  arc->middle_handle.connect_type = HANDLE_NONCONNECTABLE;
  arc->middle_handle.connected_to = NULL;

  arc_update_data(arc);

  *handle1 = obj->handles[0];
  *handle2 = obj->handles[1];
  return &arc->connection.object;
}

static void
arc_destroy(Arc *arc)
{
  connection_destroy(&arc->connection);
}

static DiaObject *
arc_copy(Arc *arc)
{
  Arc *newarc;
  Connection *conn, *newconn;
  DiaObject *newobj;
  
  conn = &arc->connection;
  
  newarc = g_malloc0(sizeof(Arc));
  newconn = &newarc->connection;
  newobj = &newconn->object;

  connection_copy(conn, newconn);

  newarc->arc_color = arc->arc_color;
  newarc->curve_distance = arc->curve_distance;
  newarc->line_width = arc->line_width;
  newarc->line_style = arc->line_style;
  newarc->dashlength = arc->dashlength;
  newarc->start_arrow = arc->start_arrow;
  newarc->end_arrow = arc->end_arrow;
  newarc->radius = arc->radius;
  newarc->center = arc->center;
  newarc->angle1 = arc->angle1;
  newarc->angle2 = arc->angle2;

  newobj->handles[2] = &newarc->middle_handle;
  
  newarc->middle_handle = arc->middle_handle;

  return &newarc->connection.object;
}

static void
arc_update_data(Arc *arc)
{
  Connection *conn = &arc->connection;
  LineBBExtras *extra =&conn->extra_spacing;
  DiaObject *obj = &conn->object;
  Point *endpoints;
  real x1,y1,x2,y2,xc,yc;
  real lensq, alpha, radius;
  real angle1, angle2;
  
  endpoints = &arc->connection.endpoints[0];
  x1 = endpoints[0].x;
  y1 = endpoints[0].y;
  x2 = endpoints[1].x;
  y2 = endpoints[1].y;
  
  lensq = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
  radius = lensq/(8*arc->curve_distance) + arc->curve_distance/2.0;

  if (lensq == 0.0)
	alpha = 1.0; /* arbitrary, but /not/ 1/0  */
  else
    alpha = (radius - arc->curve_distance) / sqrt(lensq);

  xc = (x1 + x2) / 2.0 + (y2 - y1)*alpha;
  yc = (y1 + y2) / 2.0 + (x1 - x2)*alpha;

  angle1 = -atan2(y1-yc, x1-xc)*180.0/M_PI;
  if (angle1<0)
    angle1+=360.0;
  angle2 = -atan2(y2-yc, x2-xc)*180.0/M_PI;
  if (angle2<0)
    angle2+=360.0;

  if (radius<0.0) {
    real tmp;
    tmp = angle1;
    angle1 = angle2;
    angle2 = tmp;
    radius = -radius;
  }
  
  arc->radius = radius;
  arc->center.x = xc; arc->center.y = yc;
  arc->angle1 = angle1;
  arc->angle2 = angle2;

  extra->start_trans =  (arc->line_width / 2.0);
  extra->end_trans =     (arc->line_width / 2.0);
  if (arc->start_arrow.type != ARROW_NONE) 
    extra->start_trans = MAX(extra->start_trans,arc->start_arrow.width);
  if (arc->end_arrow.type != ARROW_NONE) 
    extra->end_trans = MAX(extra->end_trans,arc->end_arrow.width);
  extra->start_long  = (arc->line_width / 2.0);
  extra->end_long    = (arc->line_width / 2.0);

  connection_update_boundingbox(conn);
  /* fix boundingbox for arc's special shape XXX find a more elegant way: */
  if (in_angle(0, arc->angle1, arc->angle2)) {
    obj->bounding_box.right = arc->center.x + arc->radius 
      + (arc->line_width / 2.0);
  }
  if (in_angle(90, arc->angle1, arc->angle2)) {
    obj->bounding_box.top = arc->center.y - arc->radius
      - (arc->line_width / 2.0);
  }
  if (in_angle(180, arc->angle1, arc->angle2)) {
    obj->bounding_box.left = arc->center.x - arc->radius
      - (arc->line_width / 2.0);
  }
  if (in_angle(270, arc->angle1, arc->angle2)) {
    obj->bounding_box.bottom = arc->center.y + arc->radius
      + (arc->line_width / 2.0);
  }

  obj->position = conn->endpoints[0];
  
  arc_update_handles(arc);
}

static void
arc_save(Arc *arc, ObjectNode obj_node, const char *filename)
{
  connection_save(&arc->connection, obj_node);

  if (!color_equals(&arc->arc_color, &color_black))
    data_add_color(new_attribute(obj_node, "arc_color"),
		   &arc->arc_color);
  
  if (arc->curve_distance != 0.1)
    data_add_real(new_attribute(obj_node, "curve_distance"),
		  arc->curve_distance);
  
  if (arc->line_width != 0.1)
    data_add_real(new_attribute(obj_node, "line_width"),
		  arc->line_width);
  
  if (arc->line_style != LINESTYLE_SOLID)
    data_add_enum(new_attribute(obj_node, "line_style"),
		  arc->line_style);

  if (arc->line_style != LINESTYLE_SOLID &&
      arc->dashlength != DEFAULT_LINESTYLE_DASHLEN)
    data_add_real(new_attribute(obj_node, "dashlength"),
		  arc->dashlength);
  
  if (arc->start_arrow.type != ARROW_NONE) {
    save_arrow(obj_node, &arc->start_arrow, "start_arrow",
	     "start_arrow_length", "start_arrow_width");
  }

  if (arc->end_arrow.type != ARROW_NONE) {
    save_arrow(obj_node, &arc->end_arrow, "end_arrow",
	     "end_arrow_length", "end_arrow_width");
  }
}

static DiaObject *
arc_load(ObjectNode obj_node, int version, const char *filename)
{
  Arc *arc;
  Connection *conn;
  DiaObject *obj;
  AttributeNode attr;

  arc = g_malloc0(sizeof(Arc));

  conn = &arc->connection;
  obj = &conn->object;

  obj->type = &arc_type;
  obj->ops = &arc_ops;

  connection_load(conn, obj_node);

  arc->arc_color = color_black;
  attr = object_find_attribute(obj_node, "arc_color");
  if (attr != NULL)
    data_color(attribute_first_data(attr), &arc->arc_color);

  arc->curve_distance = 0.1;
  attr = object_find_attribute(obj_node, "curve_distance");
  if (attr != NULL)
    arc->curve_distance = data_real(attribute_first_data(attr));

  arc->line_width = 0.1;
  attr = object_find_attribute(obj_node, "line_width");
  if (attr != NULL)
    arc->line_width = data_real(attribute_first_data(attr));

  arc->line_style = LINESTYLE_SOLID;
  attr = object_find_attribute(obj_node, "line_style");
  if (attr != NULL)
    arc->line_style = data_enum(attribute_first_data(attr));

  arc->dashlength = DEFAULT_LINESTYLE_DASHLEN;
  attr = object_find_attribute(obj_node, "dashlength");
  if (attr != NULL)
    arc->dashlength = data_real(attribute_first_data(attr));

  load_arrow(obj_node, &arc->start_arrow, "start_arrow",
	     "start_arrow_length", "start_arrow_width");

  load_arrow(obj_node, &arc->end_arrow, "end_arrow",
	     "end_arrow_length", "end_arrow_width");

  connection_init(conn, 3, 0);

  obj->handles[2] = &arc->middle_handle;
  arc->middle_handle.id = HANDLE_MIDDLE;
  arc->middle_handle.type = HANDLE_MINOR_CONTROL;
  arc->middle_handle.connect_type = HANDLE_NONCONNECTABLE;
  arc->middle_handle.connected_to = NULL;

  arc_update_data(arc);

  return &arc->connection.object;
}


