/* Dia -- an diagram creation/manipulation program
 * Copyright (C) 1998, 1999 Alexander Larsson
 *
 * Custom Lines -- line shapes defined in XML rather than C.
 * Based on the original Custom Objects plugin.
 * Copyright (C) 1999 James Henstridge.
 *
 * Adapted for Custom Lines plugin by Marcel Toele.
 * Modifications (C) 2007 Kern Automatiseringsdiensten BV.
 *
 * Rewrite to use only public API (C) 2008 Hans Breuer
 *
 * 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.
 */

#include <assert.h>
#include <gmodule.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#include "object.h"

#include "line_info.h"
#include "custom_linetypes.h"

#include "properties.h"
#include "propinternals.h"

#include "pixmaps/default.xpm"

static void customline_apply_properties( DiaObject* line, LineInfo* info );
static DiaObject* customline_create(Point *startpoint,
                                    void *user_data,
                                    Handle **handle1,
                                    Handle **handle2);
static DiaObject *custom_zigzagline_load (ObjectNode obj_node, int version, const char *filename);
static DiaObject *custom_polyline_load   (ObjectNode obj_node, int version, const char *filename);
static DiaObject *custom_bezierline_load (ObjectNode obj_node, int version, const char *filename);

static void customline_save (DiaObject *object, ObjectNode obj_node, const char *filename);

static ObjectTypeOps 
custom_zigzagline_type_ops = {
  (CreateFunc)customline_create,   /* create */
  (LoadFunc)  custom_zigzagline_load,     /* load */
  (SaveFunc)  customline_save,     /* save */
  (GetDefaultsFunc)   NULL /* get_defaults*/,
  (ApplyDefaultsFunc) NULL /* apply_defaults*/
};

static ObjectTypeOps 
custom_polyline_type_ops = {
  (CreateFunc)customline_create,   /* create */
  (LoadFunc)  custom_polyline_load,     /* load */
  (SaveFunc)  customline_save,     /* save */
  (GetDefaultsFunc)   NULL /* get_defaults*/,
  (ApplyDefaultsFunc) NULL /* apply_defaults*/
};

static ObjectTypeOps 
custom_bezierline_type_ops = {
  (CreateFunc)customline_create,   /* create */
  (LoadFunc)  custom_bezierline_load, /* load */
  (SaveFunc)  customline_save,     /* save */
  (GetDefaultsFunc)   NULL /* get_defaults*/,
  (ApplyDefaultsFunc) NULL /* apply_defaults*/
};

/* our delegates types, intialized on demand */
static DiaObjectType *polyline_ot = NULL;
static DiaObjectType *bezier_ot = NULL;
static DiaObjectType *zigzag_ot = NULL;


static gboolean
ensure_standard_types (void)
{
  if (!zigzag_ot)
    zigzag_ot = object_get_type ("Standard - ZigZagLine");
  if (!polyline_ot)
    polyline_ot = object_get_type ("Standard - PolyLine");
  if (!bezier_ot)
    bezier_ot = object_get_type ("Standard - BezierLine");

  return (polyline_ot && bezier_ot && zigzag_ot);
}

static DiaObject *
custom_zigzagline_load (ObjectNode obj_node, int version, const char *filename)
{
  ensure_standard_types ();
  
  if (!zigzag_ot) {
    g_warning ("Can't delegate to 'Standard - ZigZagLine'");
    return NULL;
  }
  return zigzag_ot->ops->load (obj_node, version, filename);
}
static DiaObject *
custom_polyline_load (ObjectNode obj_node, int version, const char *filename)
{
  ensure_standard_types ();

  if (!polyline_ot) {
    g_warning ("Can't delegate to 'Standard - PolyLine'");
    return NULL;
  }
  return polyline_ot->ops->load (obj_node, version, filename);
}
static DiaObject *
custom_bezierline_load (ObjectNode obj_node, int version, const char *filename)
{
  ensure_standard_types ();
  
  if (!bezier_ot) {
    g_warning ("Can't delegate to 'Standard - BezierLine'");
    return NULL;
  }
  return bezier_ot->ops->load (obj_node, version, filename);
}

static void 
customline_save (DiaObject *object, ObjectNode obj_node, const char *filename)
{
  g_assert (object->type &&  object->type->ops && object->type->ops->save);
   
  if (!ensure_standard_types()) {
    g_warning ("Can't create standard types");
    return;
  }

  if (object->type->ops == &custom_zigzagline_type_ops)
    zigzag_ot->ops->save (object, obj_node, filename);
  else if (object->type->ops == &custom_polyline_type_ops)
    polyline_ot->ops->save (object, obj_node, filename);
  else if (object->type->ops == &custom_bezierline_type_ops)
    bezier_ot->ops->save (object, obj_node, filename);
  else 
    g_warning ("customline_save() no delegate");
}

/* the order here must match the one in customline_apply_properties */
static PropDescription
_customline_prop_descs[] = {
  { "line_colour", PROP_TYPE_COLOUR },
  { "line_style", PROP_TYPE_LINESTYLE },
  { PROP_STDNAME_LINE_WIDTH, PROP_STDTYPE_LINE_WIDTH },
  { "corner_radius", PROP_TYPE_REAL },
  { "start_arrow", PROP_TYPE_ARROW },
  { "end_arrow", PROP_TYPE_ARROW },
   PROP_DESC_END
};

void 
customline_apply_properties( DiaObject* line, LineInfo* info )
{
  GPtrArray *props;
  LinestyleProperty *lsprop;
  ColorProperty     *cprop;
  RealProperty      *rprop;
  ArrowProperty     *aprop;
  
  props = prop_list_from_descs( _customline_prop_descs, pdtpp_true );
  g_assert( props->len == 6 );
  
  /* order/index/type must match _customline_prop_descs */
  cprop = g_ptr_array_index( props, 0 );
  cprop->color_data = info->line_color;
  
  lsprop = g_ptr_array_index( props, 1 );
  lsprop->style = info->line_style;
  lsprop->dash = info->dashlength;
  
  rprop = g_ptr_array_index( props, 2 );
  rprop->real_data = info->line_width;
  
  rprop = g_ptr_array_index( props, 3 );
  rprop->real_data = info->corner_radius;
  
  aprop = g_ptr_array_index( props, 4 );
  aprop->arrow_data = info->start_arrow;
  
  aprop = g_ptr_array_index( props, 5 );
  aprop->arrow_data = info->end_arrow;
  
  line->ops->set_props( line, props );

  prop_list_free(props);
}

DiaObject *
customline_create(Point *startpoint,
                  void *user_data,
                  Handle **handle1,
                  Handle **handle2)
{
  DiaObject* res = NULL;
  LineInfo* line_info = (LineInfo*)user_data;

  if (!ensure_standard_types()) {
    g_warning ("Can't create standar types.");
    return NULL;
  }

  if (line_info->type == CUSTOM_LINETYPE_ZIGZAGLINE)
    res = zigzag_ot->ops->create( startpoint, user_data, handle1, handle2 );
  else if (line_info->type == CUSTOM_LINETYPE_POLYLINE)
    res = polyline_ot->ops->create( startpoint, NULL, handle1, handle2 );
  else if (line_info->type == CUSTOM_LINETYPE_BEZIERLINE)
    res = bezier_ot->ops->create( startpoint, NULL, handle1, handle2 );
  else 
    g_warning(_("INTERNAL: CustomLines: Illegal line type in LineInfo object."));

  if (res) {
    customline_apply_properties (res, line_info);
    res->type = line_info->object_type;
  }

  return( res );
}

void 
custom_linetype_new(LineInfo *info, DiaObjectType **otype)
{
  DiaObjectType *obj = g_new0(DiaObjectType, 1);

  obj->version = 1;
  obj->pixmap = default_xpm;

  if (info->type == CUSTOM_LINETYPE_ZIGZAGLINE)
    obj->ops = &custom_zigzagline_type_ops;
  else if (info->type == CUSTOM_LINETYPE_POLYLINE)
    obj->ops = &custom_polyline_type_ops;
  else if (info->type == CUSTOM_LINETYPE_BEZIERLINE)
    obj->ops = &custom_bezierline_type_ops;
  else
    g_warning(_("INTERNAL: CustomLines: Illegal line type in LineInfo object %s."),
              obj->name);

  obj->name = info->name;
  obj->default_user_data = info;

  if (info->icon_filename) {
    struct stat buf;
    if (0==stat(info->icon_filename,&buf)) {
      obj->pixmap = NULL;
      obj->pixmap_file = info->icon_filename;
    } else {
      g_warning(_("Cannot open icon file %s for object type '%s'."),
                info->icon_filename, obj->name);
    }
  }

  info->object_type = obj; /* <-- Reciproce type linking */

  obj->default_user_data = (void*)info;

  *otype = obj;
}
