#include "im.h"

static PVector *get_class_list(void) {
  static PVector *classes = NULL;

  if (!classes)
    classes = p_vector_new();
  
  return classes;
}

/**
 * im_class_add - Create a class and add it to the class hierarchy.
 * @parent_class: ImClassType, Parent of new class.
 * @name: char *, Name of new class.
 *
 * Create a new class and add it to the class hierarchy.  If parent is
 * set to IM_CLASS_TOPLEVEL, then the class will not be a part of the
 * standard hierarchy (ie, it will be a new top-level class).
 *
 */
ImClassType im_class_add(ImClassType parent_class, char *name) {
  PVector *classes = get_class_list();
  ImClass *klass = p_malloc(sizeof(ImClass));
  memset(klass, 0, sizeof(ImClass));

  klass->type = p_vector_add_entry(classes, klass);
  klass->parent = parent_class;
  klass->name = p_string_dup(name);
  
  return type;
}

/**
 * im_class_get_name - Get the name of the specified class.
 * @klass: ImClassType, Specified class.
 *
 * Get the name of the specified class.  Returns NULL if the class could
 * not be found or if the name is empty.  Note: returns a copy of the
 * internal string, so be sure to free it.
 *
 */
ImClassType im_class_get_name(ImClassType klass) {
  ImClass *klass = IM_CLASS(p_vector_get_nth(get_class_list(), klass));

  if (!klass || !klass->name)
    return NULL;

  return strdup(klass->name);
}

/**
 * im_class_get_parent - Get the parent of the specified class.
 * @klass: ImClassType, Specified class.
 *
 * Return the parent of the specified class, or IM_CLASS_TOPLEVEL if the
 * specified class could not be found.
 *
 */
ImClassType im_class_get_parent(ImClassType klass) {
  ImClass *klass = IM_CLASS(p_vector_get_nth(get_class_list, klass));

  if (!klass)
    return IM_CLASS_TOPLEVEL;

  return klass->parent;
}

/**
 * im_class_is_a - Is this class a descendant of this parent?
 * @child_class: ImClassType, Specified class.
 * @parent_class: ImClassType, Parent class in question.
 *
 * Returns TRUE if @child_class is a descendant of @parent_class, and
 * FALSE otherwise.
 *
 */
ImBool im_class_is_a(ImClassType child_class, ImClassType parent_class) {
  ImClass *klass = IM_CLASS(p_vector_get_nth(get_class_list(), child_class));
  int i, type;

  if (!klass)
    return FALSE;

  for (i = klass->type; i > IM_CLASS_TOPLEVEL; i = im_class_get_parent(i))
    if (i == parent_class)
      return TRUE;

  return FALSE;
}

/**
 * im_class_get_type - Get the ImClassType from the name of a class.
 * @class_name: char *, Name of specified class (case-insensitive).
 * 
 * Return the ImClassType of a class named @class_name
 * (case-insensitive).  Returns -1 if the specified class could not be
 * found.
 *
 */
ImClassType im_class_get_type(char *class_name) {
  PVector *classes = get_class_list();
  ImClass *klass;
  int i = 0, len;

  if (!class_name || !strlen(class_name))
    return -1;
  
  len = p_vector_get_length(classes);

  for (i = 0; i < len; i++) {
    klass = IM_CLASS(p_vector_get_nth(classes, i));

    if (klass && strncasecmp(klass->name, class_name, strlen(class_name) + 1))
      return klass->type;
  }

  return -1;
}

/**
 * im_class_get - Get the structure for the specified class.
 * @klass: ImClassType, Specified class.
 *
 * Get the structure for the specified class.  Note that this data
 * should be considered READ ONLY by anyone other than class and object
 * authors.  Third-party software modifying these values is not
 * recommended.
 *
 */
ImClass *im_class_get(ImClassType klass) {
  return IM_CLASS(p_vector_get_nth(get_class_list(), klass));
}
