/**@name Synchronization and Communication for Object-Oriented Programming (SCOOP)
 *
 * Implementation of common parts to all types of subsystems
 *
 * @author Philippe Ribet, Cyril Adrian
 */
//@{

/// The VFTs
static se_subsystem_vft_t** vft = NULL;
static int vft_count = 0;

/**
 * Register a new type of subsystem. Must be done before the runtime starts
 * (no locking)
 */
void se_register_vft(int type, se_subsystem_vft_t* a_vft) {
  se_subsystem_vft_t** new_vft;
  int new_count;
  int i;
  if (vft_count <= type) {
    new_count = type+1;
    new_vft = (se_subsystem_vft_t**)malloc(new_count * sizeof(se_subsystem_vft_t*));
    if (new_vft == NULL) {
      fprintf(stderr, "SCOOP internals: could not alloc VFT table\n");
      exit(1);
    }
    for (i = new_count; i --> vft_count; ) new_vft[i] = NULL;
    if (vft_count != 0) {
      memcpy(new_vft, vft, vft_count * sizeof(se_subsystem_vft_t*));
    }
    vft = new_vft;
    vft_count = new_count;
  }
  vft[type] = a_vft;
}

/**
 * Create a new subsystem. The type must exist.
 */
se_subsystem_t* se_new_subsystem(int type, char* name) {
  se_subsystem_t* result = NULL;
  static int counter = 0;
#ifndef SE_BOOST
  if (type > vft_count) {
    fprintf(stderr, "SCOOP internals: invalid type %d.\n", type);
    exit(1);
  }
#endif
  result = vft[type]->new(name);
  result->vft = *vft[type];
  result->num = ++counter;
  return result;
}

#ifndef SE_BOOST
/**
 * Print the stack traces of all threads
 */
void se_print_all_stack_traces() {
  int i;
  for (i = vft_count; i --> 0; ) {
    vft[i]->print_all_stack_traces();
  }
}
#endif

/**
 * Create a new subsystem mapping the current thread. Used only for root
 * thread (init). The type must exist.
 */
se_subsystem_t* se_init_separate_root(int type, char* name) {
  se_subsystem_t* result = NULL;
#ifndef SE_BOOST
  if (type > vft_count) {
    fprintf(stderr, "SCOOP internals: invalid type %d.\n", type);
    exit(1);
  }
#endif
  result = vft[type]->from_root(name);
  result->vft = *vft[type];
  result->num = 0;
  return result;
}

static void se_join(void* data, int length, void* result) {
  //fprintf(SE_ERR, "JOINING\n");
  *((int*)result) = 1;
  return;
}

/**
 * Wait for the current thread to be ready (sends it a dummy query)
 */
void se_join_subsystem(se_subsystem_t* subsystem) {
  volatile int wait = 0;
  //fprintf(SE_ERR, "WAIT JOIN\n");
  subsystem->vft.query(subsystem, &se_join, NULL, 0, (void*)(&wait));
  //fprintf(SE_ERR, "JOINED %d\n", wait);
}

/**
 * Returns the local object
 */
T0* as_local(T0* object) {
    if (is_local(object)) return object;
    return ((sT0*)object)->ref;
}

/**
 * Returns true if the objects are identical, modulo the subsystem object
 */
int scoop_cmp(T0* o1, T0* o2) {
    int result = 0;
    T0* local1, local2;
    if (o1 == o2) {
	result = 1;
    }
    else if (o1 == NULL) {
    }
    else if (o2 == NULL) {
    }
    else {
	result = (as_local(o1) == as_local(o2));
    }
    return result;
}

//@}
