/*
 * hash table and lookup code for classes
 */
/*
  This file is part of Japhar, the GNU Virtual Machine for Java Bytecodes.
  Japhar is a project of The Hungry Programmers, GNU, and OryxSoft.

  Copyright (C) 1997, 1998, 1999 The Hungry Programmers

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "jniint.h"
#include "ClazzFile.h"
#include "class-repository.h"
#include "log.h"

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#define MYLOG "ClassRepository"

/* please make this a prime number :) */
/* more are : 137, 139, 149, 151, 163, 167, 181, 191, 211, 213, ... */
#define NUM_BUCKETS 167

struct class_list {
  ClazzFile *cf;

  class_list *next;
};

/* hack... used for japharh, since it doesn't really create a JavaVM */
static class_list *_class_repository[NUM_BUCKETS];

void
initialize_class_repository(JavaVM *vm)
{
  HungryJavaVM* hvm = (HungryJavaVM*)vm;

  if (hvm && NULL == hvm->_class_repository)
    hvm->_class_repository = (class_list**)calloc(NUM_BUCKETS, sizeof(class_list*));
  assert(NULL != hvm->_class_repository);
}

void
destroy_class_repository(JavaVM *vm)
{
  HungryJavaVM *hvm = (HungryJavaVM*)vm;

  if (hvm)
    free(hvm->_class_repository);
}

static unsigned long
hash_func(char *class_name)
{
  unsigned long hash;
  char *str;

  for (str = class_name, hash = 0; *str != 0; str++)
    hash = hash * 33 + *str;

  return hash;
}

/*
 * add_class_to_repository() - give a name a classfile, and, well, you know
 */
void
add_class_to_repository (JNIEnv *env, ClazzFile *classfile, char *name)
{
  unsigned long hash;
  class_list *new_class_list;
  class_list **repository;

  if (env)
    repository = ((HungryJNIEnv*)env)->_vm->_class_repository;
  else
    repository = _class_repository;
  assert(NULL != repository);

  JAVARLOG1(MYLOG, 1, "in add_class_to_repository(%s)\n", name);

  hash = hash_func(name) % NUM_BUCKETS;

  new_class_list = (class_list*)malloc(sizeof(class_list));
  new_class_list->cf = classfile;

  new_class_list->next = repository[ hash ];
  repository[ hash ] = new_class_list;

  JAVARLOG1(MYLOG, 2, "     added new class = %p\n", new_class_list);
}

/* 
 * delete class from repository
 */
void
delete_class_from_repository (JNIEnv *env, char *name)
{
  unsigned long hash;
  class_list *cl, *prev;
  class_list **repository;

  if (env)
    repository = ((HungryJNIEnv*)env)->_vm->_class_repository;
  else
    repository = _class_repository;
  assert(NULL != repository);
  
  JAVARLOG1(MYLOG, 1, "in delete_class_to_repository(%s)\n", name);

  hash = hash_func(name) % NUM_BUCKETS;

  cl = repository[ hash ];

  /* is it the first one? */
  if (!strcmp(getClassName(env, cl->cf), name))
    {
      repository[ hash ]->next = cl->next;
    }
  else
    {
      while (cl != NULL)
	{
	  prev = cl;
	  cl = cl->next;

	  if (!strcmp(getClassName(env, cl->cf), name))
	    {
	      prev->next = cl->next;
	      break;
	    }
	}
    }

  assert(cl != NULL);

  JAVARLOG1(MYLOG, 2, "    deleting %p\n", cl);

  /* at this point, cl points to the entry to get rid of. */
  free(cl);
}

static class_list *
find_class_list_in_repository(JNIEnv *env, char *name)
{
  unsigned long hash;
  class_list *cl;
  class_list **repository;

  if (env)
    repository = ((HungryJNIEnv*)env)->_vm->_class_repository;
  else
    repository = _class_repository;

  /* 
   * This method might get called before initialize_class_repository,
   * if something goes wrong during native library loading.
   */
  if (NULL == repository)
    return NULL;

  hash = hash_func(name) % NUM_BUCKETS;
  cl = repository[ hash ];

  while (cl != NULL && cl->cf != NULL)
    {
      if (!strcmp(name, getClassName(env, cl->cf)))
	return cl;
      
      cl = cl->next;
    }

  return NULL;
}

/*
 * find class in repository */
ClazzFile *
find_class_in_repository (JNIEnv *env, char *name)
{
  class_list *cl;

  JAVARLOG1(MYLOG, 1, "in find_class_to_repository(%s)\n", name);

  cl = find_class_list_in_repository(env, name);

  if (cl == NULL)
    {
      JAVARLOG0(MYLOG, 2, "   returning NULL\n");      
      return NULL;
    }
  else
    {
      JAVARLOG1(MYLOG, 2, "   returning %p\n", cl->cf);
      return cl->cf;
    }
}

jint
get_num_loaded_classes(JNIEnv *env)
{
  int i;
  int num_classes = 0;
  class_list **repository;

  if (env)
    repository = ((HungryJNIEnv*)env)->_vm->_class_repository;
  else
    repository = _class_repository;
  assert(NULL != repository);
  
  for (i = 0; i < NUM_BUCKETS; i++) {
    class_list *cur;
	  
    for (cur = repository[i]; cur != NULL; cur = cur->next) {
      ++num_classes;
    }
	  
  }
  return num_classes;
}

void
get_loaded_classes(JNIEnv *env, jclass *classes)
{
  int i;
  int num_classes = 0;
  class_list **repository;

  if (env)
    repository = ((HungryJNIEnv*)env)->_vm->_class_repository;
  else
    repository = _class_repository;
  assert(NULL != repository);
  
  for (i = 0; i < NUM_BUCKETS; i++) {
    class_list *cur;
    
    for (cur = repository[i]; cur != NULL; cur = cur->next) {
      classes[num_classes++] = clazzfile_to_jclass(env, cur->cf);
    }
	  
  }
}

void
map_over_repository(JavaVM *vm,
		    repository_entry_func func)
{
  int i;
  class_list **repository;

  repository = ((HungryJavaVM*)vm)->_class_repository;
  assert(NULL != repository);
  
  for (i = 0; i < NUM_BUCKETS; i++) {
    class_list *cur;
    
    for (cur = repository[i]; cur != NULL; cur = cur->next) {
      (*func)(cur->cf);
    }
  }
}

void
dump_class_repository(JNIEnv *env)
{
  int i;
  class_list **repository;

  if (env)
    repository = ((HungryJNIEnv*)env)->_vm->_class_repository;
  else
    repository = _class_repository;
  assert(NULL != repository);
  
  for (i = 0; i < NUM_BUCKETS; i ++)
    {
      class_list *cur;
	  
      printf ("%d : ", i);
	  
      for (cur = repository[i];
	   cur != NULL;
	   cur = cur->next)
	{
	  printf ("%s : ", getClassName(env, cur->cf));
	}
	  
      printf ("\n");
    }
}
