#include "im.h"

static PVector *get_object_list(void) {
  static PVector *object_list;

  if (!object_list)
    object_list = p_vector_new();

  return object_list;
}

static int object_register(void *o) {
  return p_vector_add_entry(get_object_list(), o);
}

static int object_unregister(void *o) {
  p_vector_remove_entry(get_object_list(), o);
}

/**
 * im_object_init - Constructor for base ImObject object.
 * @o, ImObject: Pointer to ImObject or object derived from ImObject.
 *
 * Constructor for base ImObject object.  Allocates space for the user
 * hash, registers the object with the internal object list.  This
 * method should always be called by constructors when objects inherit
 * from ImObject.
 *
 */
void im_object_init(ImObject *o) {
  static klass = -1;

  /* if this is the first object we've allocated, then register our
   * class with the class hierarchy */
  if (klass == -1)
    klass = im_class_add(-1, "ImObject");
  o->klass = klass;
  
  /* allocate user data hash */
  o->user_data = p_hash_new();

  /* add to internal object list */
  o->id = object_register(o);
}

/**
 * im_object_free - Finalize and free an object.
 * @o: void *, Pointer to specified object.
 *
 * Remove @o from the internal list of objects and call @o 's finalize
 * methods in the opposite order that they were registered (ie, by
 * walking up the class hierarchy and calling the finalize method
 * associated with each class).  Finally, add @o to a garbage collection
 * list.
 *
 */
void im_object_free(void *o) {
  ImClass *klass;
  int i;

  /* remove the object from our internal object list */
  object_unregister(o);

  /* walk up the class hierarchy and call finalize methods until we
   * reach the top level */
  for (i = o->klass; i != IM_CLASS_TOPLEVEL; i = im_class_get_parent(i)) {
    klass = im_class_get(i);

    if (klass->finalize)
      klass->finalize(o, NULL);
  }

  /* FIXME: still need GC here */
}

/**
 * im_object_get_id - Get an object's internal ID.
 * @o, ImObject *, Specified object.
 *
 * Get an object's internal ID.
 *
 */
__inline__ ImID im_object_get_id(ImObject *o) {
  return o->id;
}

/**
 * im_object_get_class - Get an object's class.
 * @o, ImObject *, Specified object.
 *
 * Get an object's class (ImClassType).
 *
 */
__inline__ ImClassType im_object_get_class(ImObject *o) {
  return o->klass;
}

/**
 * im_object_get_rect - Get an object's rectangle.
 * @o, ImObject *, Specified object.
 *
 * Get an object's rectangle (ImRect).
 *
 */
__inline__ ImRect im_object_get_rect(ImObject *o) {
  return o->rect;
}

/**
 * im_object_set_rect - Set an object's rectangle.
 * @o, ImObject *, Specified object.
 *
 * Set an object's rectangle (ImRect).
 *
 */
__inline__ void im_object_set_rect(ImObject *o, ImRect rect) {
  ImClass *klass = im_class_get(o->klass);
  if (klass->resize) {
    o->rect.point.x = rect.point.x;
    o->rect.point.y = rect.point.y;
    klass->resize(o, rect.size);
  } else {
    p_warn("The class \"%s\" has not implemented a resize() method.  "
           "Please tell the author to fix his or her code.",
           klass->name ? klass->name : "UNKNOWN");
    /* blindly set rect here... :/ */
    o->rect = rect;
  }
}

/**
 * im_object_get_location - Set an object's x, y location.
 * @o, ImObject *, Specified object.
 *
 * Get an object's location (ImPoint).  Note that this can be overridden
 * for many draw methods, so these values may not be set by application 
 * developers, or may be inaccurate.
 *
 */
__inline__ ImPoint im_object_get_location(ImObject *o) {
  return o->rect.point;
}

/**
 * im_object_set_location - Set an object's x, y location.
 * @o, ImObject *, Specified object.
 *
 * Set an object's location (ImPoint).  Note that this can be overridden
 * for many draw methods.
 *
 */
__inline__ void im_object_set_location(ImObject *o, ImPoint point) {
  o->rect.point = point;
}

/**
 * im_object_set_xy - Set an object's x, y location.
 * @o, ImObject *, Specified object.
 *
 * Set an object's location (ImPoint).  Note that this can be overridden
 * for many draw methods.
 *
 */
__inline__ void im_object_set_xy(ImObject *o, int x, int y) {
  o->rect.point.x = x;
  o->rect.point.y = y;
}

/**
 * im_object_get_size - Get an object's width and height.
 * @o, ImObject *, Specified object.
 *
 * Get an object's width and height (ImSize).  Note that many objects
 * are non-rectangular, so the return value may just be a bounding box.
 *
 */
__inline__ ImSize im_object_get_size(ImObject *o) {
  return o->rect.size;
}

/**
 * im_object_get_width - Get an object's width.
 * @o, ImObject *, Specified object.
 *
 * Get an object's width.  Note that many objects are non-rectangular,
 * so the return value may just be the width of a bounding box.
 *
 */
__inline__ void im_object_get_width(ImObject *o) {
  return o->rect.size.w;
}

/**
 * im_object_get_height - Get an object's width.
 * @o, ImObject *, Specified object.
 *
 * Get an object's height.  Note that many objects are non-rectangular,
 * so the return value may just be the height of a bounding box.
 *
 */
__inline__ void im_object_get_height(ImObject *o) {
  return o->rect.size.h;
}

/**
 * im_object_set_size - Set an object's size.
 * @o, ImObject *, Specified object.
 *
 * Set an object's size (ImSize).  For many objects this is the
 * size of the bounding rectangle (the object will be resized to fill
 * the new bounding rectangle if possible).
 *
 */
__inline__ void im_object_set_size(ImObject *o, ImSize size) {
  ImClass *klass = im_class_get(o->klass);
  if (klass->resize) {
    klass->resize(o, size);
  } else {
    p_warn("The class \"%s\" has not implemented a resize() method.  "
           "Please tell the author to fix his or her code.",
           klass->name ? klass->name : "UNKNOWN");
    /* blindly set rect here... :/ */
    o->rect.size = size;
  }
}

/**
 * im_object_set_wh - Set an object's size.
 * @o, ImObject *, Specified object.
 *
 * Set an object's size in width and height.  For many objects this is
 * the size of the bounding rectangle (the object will be resized to
 * fill the new bounding rectangle if possible).
 *
 */
__inline__ void im_object_set_wh(ImObject *o, int w, int h) {
  ImSize size;
  if (klass->resize) {
    size.w = w; size.h = h;
    klass->resize(o, size);
  } else {
    p_warn("The class \"%s\" has not implemented a resize() method.  "
           "Please tell the author to fix his or her code.",
           klass->name ? klass->name : "UNKNOWN");

    /* blindly set rect here... :/ */
    o->rect.size.w = w;
    o->rect.size.h = h;
  }
}

ImMask im_object_get_mask(ImObject *o) {
  static ImMask default_mask;
  ImClass *klass = im_class_get(o->klass);
  
  if (!klass->mask) {
    p_warn("The class \"%s\" has not implemented a mask() method.  "
           "Please tell the author to fix his or her code.",
           klass->name ? klass->name : "UNKNOWN");
    return default_mask;
  }

  return klass->mask(o);
}

/* object user data methods */
void *im_object_get_data(ImObject *o, char *key) {
  return p_hash_get(o->user_data, key);
}

void *im_object_set_data(ImObject *o, char *key, void *value) {
  p_hash_set(o->user_data, key, value);
}

void *im_object_remove_data(ImObject *o, char *key) {
  p_hash_remove(o->user_data, key, FALSE);
}

Pixmap im_object_get_x_pixmap(ImObject *o) {
}



