/* Copyright (c) 1993 Stephen F. White */

#include "cool.h"
#include "proto.h"

static void do_fmr (Object * o, const char *name);

/*
 * find_method() - find a method on an object
 *
 * returns a pointer to the method if found, 0 if not.
 */

Method *find_method (Object * o, const char *name)
{
  Method *m;
  int hval;

  if (!o || !o->methods) {
    return 0;
  }
  hval = hash (name) % o->methods->size;
  for (m = o->methods->table[hval]; m; m = m->next) {
    if (!strcasecmp (sym_get (o, m->name)->str, name)) {
      return m;
    }
  }
  return 0;
}

/*
 * find_method_recursive() - find a method on an object or its ancestors
 *
 * Uses a post-order depth first search reversing the order of the
 * parents, and marks searched objects using a generational index.
 * This actually results in a reverse topological search, with a left to
 * right ordering of branches.  Thus, an object is only searched after
 * all the objects that point to it are searched.  Thanks to greg hudson
 * for help with the algorithm.
 */

static Method *found_m;
static Object *found_o;
static int found_blocked_method;        /* flag */
static int	 search_gen = 0;

Method *find_method_recursive (Object * o, const char *name, Object ** where)
{
  if (!o || !name)
    return 0;
  search_gen += 1;
  found_m = 0;
  found_o = 0;
  found_blocked_method = 0;
#ifdef METHOD_DEBUG
  printf ("#%d.%s:  ", o->id.id, name);
#endif /* METHOD_DEBUG */
  do_fmr (o, name);
#ifdef METHOD_DEBUG
  if (found_m) {
    if (found_blocked_method) {
      printf (", found on #%d, blocked\n", found_o->id.id);
    } else {
      printf (", found on #%d\n", found_o->id.id);
    }                           /* if */
  } else {
    printf (", not found\n");
  }                             /* if */
#endif /* METHOD_DEBUG */
  *where = found_o;
  return found_m;
}                               /* find_method_recursive */

static void do_fmr (Object * o, const char *name)
{
  Method *m;
  int i;
  Object *p;

  /* bail if we've already searched this one */
  if (!o || o->last_search == search_gen)
    return;

  /* bail if we already found a blocked method */
  if (found_blocked_method)
    return;

  /* search parents, in reverse order */
  for (i = o->parents->len - 1; i >= 0; i--) {
    p = retrieve (o->parents->el[i].v.obj);
    do_fmr (p, name);
    if (found_blocked_method)
      return;
  }                             /* for */
  o->last_search = search_gen;  /* mark this object searched */
  m = find_method (o, name);
#ifdef METHOD_DEBUG
  printf ("#%d ", o->id.id);
#endif /* METHOD_DEBUG */
  if (m) {                      /* if we hit one, */
    found_m = m;                /* save it */
    found_o = o;
    if (m->blocked) {           /* if it was blocked, */
      found_blocked_method = 1; /* set the flag */
    }                           /* if */
  }                             /* if */
}                               /* find_method_recursive */

void add_method (Object * o, Method * new_method)
{
  Method *m;
  int hval;

  if (!o || !new_method)
    return;
  new_method->next = 0;
  if (!o->methods) {
    o->methods = hash_new (HASH_INIT_SIZE);
  }
  hval = hash (sym_get (o, new_method->name)->str) % o->methods->size;
  /* skip to end of chain */
  for (m = o->methods->table[hval]; m && m->next; m = m->next);
  if (m) {
    m->next = new_method;
  } else {
    o->methods->table[hval] = new_method;
  }
  o->methods->num++;
}

Error rm_method (Object * o, const char *name)
{
  Method *m, *prev;
  int hval;

  if (!o || !name)
    return E_INVIND;
  if (!o->methods)
    return E_METHODNF;
  hval = hash (name) % o->methods->size;
  for (m = o->methods->table[hval], prev = 0; m; prev = m, m = m->next) {
    if (!strcasecmp (sym_get (o, m->name)->str, name)) {
      if (prev) {
        prev->next = m->next;
      } else {
        o->methods->table[hval] = m->next;
      }
      free_method (o, m);
      o->methods->num--;
      return E_NONE;
    }
  }
  return E_METHODNF;
}