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

/* Copyright 2003, W. Borgert <debacle@debian.org>
   copied a lot from UML/actor.c */

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

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

#include "intl.h"
#include "object.h"
#include "element.h"
#include "diarenderer.h"
#include "attributes.h"
#include "text.h"
#include "properties.h"

#include "pixmaps/basestation.xpm"

#define NUM_CONNECTIONS 9

typedef struct _Basestation Basestation;
struct _Basestation {
  Element element;

  ConnectionPoint connections[NUM_CONNECTIONS];

  Color line_colour;
  Color fill_colour;

  Text *text;
  TextAttributes attrs;

  int sectors;			/* oftenly 3 or 4, always >= 1, but
				   check is missing */
};

#define BASESTATION_WIDTH 0.8
#define BASESTATION_HEIGHT 4.0
#define BASESTATION_LINEWIDTH 0.1

static real basestation_distance_from(Basestation *basestation,
                                      Point *point);
static void basestation_select(Basestation *basestation,
                               Point *clicked_point,
                               DiaRenderer *interactive_renderer);
static ObjectChange
    *basestation_move_handle(Basestation *basestation, Handle *handle,
                             Point *to, ConnectionPoint *cp,
                             HandleMoveReason reason,
                             ModifierKeys modifiers);
static ObjectChange *basestation_move(Basestation *basestation, Point *to);
static void basestation_draw(Basestation *basestation,
                             DiaRenderer *renderer);
static DiaObject *basestation_create(Point *startpoint,
                                  void *user_data,
                                  Handle **handle1,
                                  Handle **handle2);
static void basestation_destroy(Basestation *basestation);
static DiaObject *basestation_load(ObjectNode obj_node,
                                int version,
                                const char *filename);
static PropDescription
    *basestation_describe_props(Basestation *basestation);
static void basestation_get_props(Basestation *basestation,
                                  GPtrArray *props);
static void basestation_set_props(Basestation *basestation,
                                  GPtrArray *props);
static void basestation_update_data(Basestation *basestation);

static ObjectTypeOps basestation_type_ops =
  {
    (CreateFunc) basestation_create,
    (LoadFunc)   basestation_load,
    (SaveFunc)   object_save_using_properties,
    (GetDefaultsFunc)   NULL,
    (ApplyDefaultsFunc) NULL
  };

DiaObjectType basestation_type =
  {
    "Network - Base Station",   /* name */
    0,                          /* version */
    (char **) basestation_xpm,  /* pixmap */

    &basestation_type_ops       /* ops */
  };

static ObjectOps basestation_ops = {
  (DestroyFunc)         basestation_destroy,
  (DrawFunc)            basestation_draw,
  (DistanceFunc)        basestation_distance_from,
  (SelectFunc)          basestation_select,
  (CopyFunc)            object_copy_using_properties,
  (MoveFunc)            basestation_move,
  (MoveHandleFunc)      basestation_move_handle,
  (GetPropertiesFunc)   object_create_props_dialog,
  (ApplyPropertiesFunc) object_apply_props_from_dialog,
  (ObjectMenuFunc)      NULL,
  (DescribePropsFunc)   basestation_describe_props,
  (GetPropsFunc)        basestation_get_props,
  (SetPropsFunc)        basestation_set_props,
};

static PropDescription basestation_props[] = {
  ELEMENT_COMMON_PROPERTIES,
  PROP_STD_LINE_COLOUR,
  PROP_STD_FILL_COLOUR,
  PROP_STD_TEXT_FONT,
  PROP_STD_TEXT_HEIGHT,
  PROP_STD_TEXT_COLOUR,
  PROP_STD_TEXT_ALIGNMENT,
  { "text", PROP_TYPE_TEXT, 0, N_("Text"), NULL, NULL },
  { "sectors", PROP_TYPE_INT, PROP_FLAG_VISIBLE,
    N_("Sectors"), NULL, NULL },
  PROP_DESC_END
};

static PropDescription *
basestation_describe_props(Basestation *basestation)
{
  if (basestation_props[0].quark == 0) {
    prop_desc_list_calculate_quarks(basestation_props);
  }
  return basestation_props;
}

static PropOffset basestation_offsets[] = {
  ELEMENT_COMMON_PROPERTIES_OFFSETS,
  {"line_colour", PROP_TYPE_COLOUR, offsetof(Basestation, line_colour)},
  {"fill_colour", PROP_TYPE_COLOUR, offsetof(Basestation, fill_colour)},
  {"text", PROP_TYPE_TEXT, offsetof(Basestation, text)},
  {"text_font", PROP_TYPE_FONT, offsetof(Basestation, attrs.font)},
  {"text_height", PROP_TYPE_REAL, offsetof(Basestation, attrs.height)},
  {"text_colour", PROP_TYPE_COLOUR, offsetof(Basestation, attrs.color)},
  {"text_alignment", PROP_TYPE_ENUM,
   offsetof(Basestation, attrs.alignment)},
  {"sectors", PROP_TYPE_INT, offsetof(Basestation, sectors)},
  { NULL, 0, 0 },
};

static void
basestation_get_props(Basestation * basestation, GPtrArray *props)
{
  text_get_attributes(basestation->text,&basestation->attrs);
  object_get_props_from_offsets(&basestation->element.object,
                                basestation_offsets,props);
}

static void
basestation_set_props(Basestation *basestation, GPtrArray *props)
{
  object_set_props_from_offsets(&basestation->element.object,
                                basestation_offsets, props);
  apply_textattr_properties(props,basestation->text,
                            "text", &basestation->attrs);
  basestation_update_data(basestation);
}

static real
basestation_distance_from(Basestation *basestation, Point *point)
{
  DiaObject *obj = &basestation->element.object;
  return distance_rectangle_point(&obj->bounding_box, point);
}

static void
basestation_select(Basestation *basestation, Point *clicked_point,
                   DiaRenderer *interactive_renderer)
{
  text_set_cursor(basestation->text, clicked_point, interactive_renderer);
  text_grab_focus(basestation->text, &basestation->element.object);
  element_update_handles(&basestation->element);
}

static ObjectChange*
basestation_move_handle(Basestation *basestation, Handle *handle,
                        Point *to, ConnectionPoint *cp,
                        HandleMoveReason reason, ModifierKeys modifiers)
{
  ObjectChange* oc;

  assert(basestation!=NULL);
  assert(handle!=NULL);
  assert(to!=NULL);
  assert(handle->id < 8);

  oc = element_move_handle (&(basestation->element), handle->id, to, cp, reason, modifiers);

  return oc;
}

static ObjectChange*
basestation_move(Basestation *basestation, Point *to)
{
  Element *elem = &basestation->element;

  elem->corner = *to;
  elem->corner.x -= elem->width/2.0;
  elem->corner.y -= elem->height / 2.0;

  basestation_update_data(basestation);

  return NULL;
}

static void
basestation_draw(Basestation *basestation, DiaRenderer *renderer)
{
  DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
  Element *elem;
  real x, y, w, h;
  real r = BASESTATION_WIDTH/2.0;
  Point ct, cb, p1, p2;
  Point points[6];

  assert(basestation != NULL);
  assert(renderer != NULL);

  elem = &basestation->element;

  x = elem->corner.x;
  y = elem->corner.y + r;
  w = elem->width;
  h = elem->height - r;

  renderer_ops->set_fillstyle(renderer, FILLSTYLE_SOLID);
  renderer_ops->set_linejoin(renderer, LINEJOIN_ROUND);
  renderer_ops->set_linestyle(renderer, LINESTYLE_SOLID);
  renderer_ops->set_linewidth(renderer, BASESTATION_LINEWIDTH);

  ct.x = x + w/2.0;
  ct.y = y + r/2.0;
  cb.x = ct.x;
  cb.y = ct.y + h - r;

  /* antenna 1 */
  points[0] = ct; points[0].x -= r/4.0; points[0].y -= 3.0*r/4.0;
  points[1] = ct; points[1].x += r/4.0; points[1].y -= 3.0*r/4.0;
  points[2] = ct; points[2].x += r/4.0; points[2].y += 1.0;
  points[3] = ct; points[3].x -= r/4.0; points[3].y += 1.0;
  renderer_ops->fill_polygon(renderer, points, 4,
                             &basestation->fill_colour);
  renderer_ops->draw_polygon(renderer, points, 4,
                             &basestation->line_colour);
  /* bottom */
  renderer_ops->fill_ellipse(renderer, &cb, r, r/2.0,
                             &basestation->fill_colour);
  renderer_ops->draw_arc(renderer, &cb, r, r/2.0, 180, 0,
                         &basestation->line_colour);
  /* bar */
  p1 = ct;
  p1.x -= r/2.0;
  p2 = cb;
  p2.x += r/2.0;
  renderer_ops->fill_rect(renderer, &p1, &p2,
                          &basestation->fill_colour);
  p2.x -= r;
  renderer_ops->draw_line(renderer, &p1, &p2,
                          &basestation->line_colour);
  p1.x += r;
  p2.x += r;
  renderer_ops->draw_line(renderer, &p1, &p2,
                          &basestation->line_colour);
  /* top */
  renderer_ops->fill_ellipse(renderer, &ct, r, r/2.0,
                             &basestation->fill_colour);
  renderer_ops->draw_ellipse(renderer, &ct, r, r/2.0,
                             &basestation->line_colour);
  /* antenna 2 */
  points[0] = ct; points[0].x += r/4.0;   points[0].y -= 0;
  points[1] = ct; points[1].x += 3.0*r/4.0; points[1].y -= r/2.0;
  points[2] = ct; points[2].x += 3.0*r/4.0; points[2].y += 1.0 - r/2.0;
  points[3] = ct; points[3].x += r/4.0;   points[3].y += 1.0;
  renderer_ops->fill_polygon(renderer, points, 4,
                             &basestation->fill_colour);
  renderer_ops->draw_polygon(renderer, points, 4,
                             &basestation->line_colour);
  /* antenna 3 */
  points[0] = ct; points[0].x -= r/4.0;   points[0].y -= 0;
  points[1] = ct; points[1].x -= 3.0*r/4.0; points[1].y -= r/2.0;
  points[2] = ct; points[2].x -= 3.0*r/4.0; points[2].y += 1.0 - r/2.0;
  points[3] = ct; points[3].x -= r/4.0;   points[3].y += 1.0;
  renderer_ops->fill_polygon(renderer, points, 4,
                             &basestation->fill_colour);
  renderer_ops->draw_polygon(renderer, points, 4,
                             &basestation->line_colour);
  text_draw(basestation->text, renderer);
}

static void
basestation_update_data(Basestation *basestation)
{
  Element *elem = &basestation->element;
  DiaObject *obj = &elem->object;
  Rectangle text_box;
  Point p;

  elem->width = BASESTATION_WIDTH;
  elem->height = BASESTATION_HEIGHT+basestation->text->height;

  p = elem->corner;
  p.x += elem->width/2;
  p.y += elem->height + basestation->text->ascent;
  text_set_position(basestation->text, &p);

  text_calc_boundingbox(basestation->text, &text_box);

  /* Update connections: */
  basestation->connections[0].pos.x = elem->corner.x;
  basestation->connections[0].pos.y = elem->corner.y;
  basestation->connections[0].directions = DIR_NORTH|DIR_WEST;
  basestation->connections[1].pos.x = elem->corner.x + elem->width / 2.0;
  basestation->connections[1].pos.y = elem->corner.y;
  basestation->connections[1].directions = DIR_NORTH;
  basestation->connections[2].pos.x = elem->corner.x + elem->width;
  basestation->connections[2].pos.y = elem->corner.y;
  basestation->connections[2].directions = DIR_NORTH|DIR_EAST;
  basestation->connections[3].pos.x = elem->corner.x;
  basestation->connections[3].pos.y = elem->corner.y + elem->height / 2.0;
  basestation->connections[3].directions = DIR_WEST;
  basestation->connections[4].pos.x = elem->corner.x + elem->width;
  basestation->connections[4].pos.y = elem->corner.y + elem->height / 2.0;
  basestation->connections[4].directions = DIR_EAST;
  basestation->connections[5].pos.x = elem->corner.x;
  basestation->connections[5].pos.y = elem->corner.y + elem->height;
  basestation->connections[5].directions = DIR_SOUTH|DIR_WEST;
  basestation->connections[6].pos.x = elem->corner.x + elem->width / 2.0;
  basestation->connections[6].pos.y = elem->corner.y + elem->height;
  basestation->connections[6].directions = DIR_SOUTH;
  basestation->connections[7].pos.x = elem->corner.x + elem->width;
  basestation->connections[7].pos.y = elem->corner.y + elem->height;
  basestation->connections[7].directions = DIR_SOUTH|DIR_EAST;
  basestation->connections[8].pos.x = elem->corner.x + elem->width / 2.0;
  basestation->connections[8].pos.y = elem->corner.y + elem->height / 2.0;
  basestation->connections[8].directions = DIR_ALL;

  element_update_boundingbox(elem);

  /* Add bounding box for text: */
  rectangle_union(&obj->bounding_box, &text_box);

  obj->position = elem->corner;
  obj->position.x += elem->width/2.0;
  obj->position.y += elem->height/2.0;

  element_update_handles(elem);
}

static DiaObject *
basestation_create(Point *startpoint,
                   void *user_data,
                   Handle **handle1,
                   Handle **handle2)
{
  Basestation *basestation;
  Element *elem;
  DiaObject *obj;
  Point p;
  DiaFont *font;
  int i;

  basestation = g_new0(Basestation, 1);
  elem = &basestation->element;
  obj = &elem->object;

  obj->type = &basestation_type;
  obj->ops = &basestation_ops;
  elem->corner = *startpoint;
  elem->width = BASESTATION_WIDTH;
  elem->height = BASESTATION_HEIGHT;

  font = dia_font_new_from_style (DIA_FONT_MONOSPACE, 0.8);
  p = *startpoint;
  p.y += BASESTATION_HEIGHT -
    dia_font_descent(_("Base Station"), font, 0.8);

  basestation->text = new_text(_("Base Station"),
                               font, 0.8, &p, &color_black, ALIGN_CENTER);
  dia_font_unref(font);
  text_get_attributes(basestation->text,&basestation->attrs);
  basestation->line_colour = color_black;
  basestation->fill_colour = color_white;
  basestation->sectors = 3;

  element_init(elem, 8, NUM_CONNECTIONS);

  for (i=0; i<NUM_CONNECTIONS; i++) {
    obj->connections[i] = &basestation->connections[i];
    basestation->connections[i].object = obj;
    basestation->connections[i].connected = NULL;
    basestation->connections[i].flags = 0;
  }
  basestation->connections[8].flags = CP_FLAGS_MAIN;

  elem->extra_spacing.border_trans = BASESTATION_LINEWIDTH/2.0;
  basestation_update_data(basestation);

  for (i=0; i<8; i++) {
    obj->handles[i]->type = HANDLE_NON_MOVABLE;
  }

  *handle1 = NULL;
  *handle2 = NULL;
  return &basestation->element.object;
}

static void
basestation_destroy(Basestation *basestation)
{
  text_destroy(basestation->text);

  element_destroy(&basestation->element);
}

static DiaObject *
basestation_load(ObjectNode obj_node, int version, const char *filename)
{
  return object_load_using_properties(&basestation_type,
                                      obj_node, version, filename);
}
