#include <stddef.h>
#include <assert.h>
#include "toba.h"
#include "runtime.h"
#include "java_lang_Class.h"
#include "java_lang_Long.h"
#include "java_util_Hashtable.h"
#ifdef OPTION_JIT
#include "toba_runtime_SystemClassLoader.h"

/* The class loader that the system uses to find classes */
extern struct in_toba_runtime_SystemClassLoader *system_loader;

#else /* OPTION_JIT */

/* hash table with (classname -> instance of class Class) */
static struct in_java_util_Hashtable *all_classes = 0;

/* install a class in the hashtable (called during startup) */
void
register_class(Class cl)
{
    if (all_classes == NULL) {
	struct in_java_util_Hashtable * nac;

	/* make a hashtable w/ 250 buckets to start with */
	nac = new(&cl_java_util_Hashtable.C);
	/* java_util_Hashtable Hashtable(int) */
	init_i_yOEvX(nac, 250);

	/* Poor-mans atomic update: things go very badly if the init call
	 * above recurses into this function, where all_classes is non-null,
	 * but does not have valid stuff in it. */
	if (NULL == all_classes) {
	    all_classes = nac;
	}
    }
    /* java_util_Hashtable put(Object, Object) */
    put_OO_4Xy3U(all_classes, cl->name, find_classclass(cl));
}

#endif /* OPTION_JIT */

/* return the Class associated with a java.lang.Class instance */
Class
find_class(struct in_java_lang_Class *clcl)
{
    Class ret;

    if(!clcl)
        throwNullPointerException("find class");

    /* 
     * the classclass instance is embedded in a Class - just subtract
     * the offset to get the corresponding Class
     */
    ret = (Class) ((char *)clcl - offsetof(struct class, classclass));
    return ret;
}

/* return the java.lang.Class instance associated with a Class */
struct in_java_lang_Class *
find_classclass(Class cl)
{
    /*
     * The in_java_lang_Class structure is embedded in a Class
     * it is made up of the pointer to its type descriptor.
     * the address of the pointer is the Object instance
     */
    return (struct in_java_lang_Class *)&cl->classclass;
}

/* java/lang/Class forName (Ljava/lang/String;)Ljava/lang/Class; */
Object forName_S_UuOsX(Object Harg1) 
{
    struct in_java_lang_String *arg1 = (struct in_java_lang_String *)Harg1;
    struct in_java_lang_Class *val;

    /* STATIC Method */
    init_java_lang_Class();
    
#ifdef OPTION_JIT
    /* This is the moral equivalent of systemclassloader.findSystemClass(c),
     * which loads the class, followed by systemclassloader.resolveClass(c),
     * which links it.  Don't call loadClass; we may not want to generate
     * code.  findSystemClass knows whether defineClass is required or not,
     * depending on where it found things. */
    /* From empirical testing, this routine is not obliged to find classes
     * that were loaded with something other than the system class loader. */
    val = system_loader->class->M.findSystemClass_S_FLv3x.f(system_loader, arg1);
    /* If the find failed, we threw an exception, right? */
    assert (NULL != val);
    system_loader->class->M.resolveClass_C_vKDXp.f(system_loader, val);
#else /* OPTION_JIT */
    /* look it up in our hash table of all known classes */
    /* java_util_Hashtable get(Object, Object) */
    val = (struct in_java_lang_Class *)get_O_doAa7(all_classes, arg1);

    /* Try to find the class in the shared libraries */
    if(!val)
        val = load_native_system_class(arg1);

    if(!val) {
        throwClassNotFoundException(cstring(arg1));
    }
#endif /* OPTION_JIT */
    return val;
}

/* java/lang/Class newInstance ()Ljava/lang/Object; */
Object newInstance__GLAhk(Object Harg1) 
{
    struct in_java_lang_Class *this = (struct in_java_lang_Class *)Harg1;
    Class cl;
    Object res;

    cl = find_class(this);
    /* we're never allowed to newInstance java.lang.Class
     * since all instances *must* be embedded within struct Class
     */
    if(cl == &cl_java_lang_Class.C)
        throwIllegalAccessException("can't instantiate java.lang.Class");

    res = construct(cl);
    return res;
}

/* java/lang/Class getName ()Ljava/lang/String; */
Object getName__KWUaL(Object Harg1) 
{
    struct in_java_lang_Class *this = (struct in_java_lang_Class *)Harg1;
    Class cl;

    cl = find_class(this);

    /* Strings are immutable, we can return this one without making a copy */
    return cl->name;
}

/* java/lang/Class getSuperclass ()Ljava/lang/Class; */
Object getSuperclass__bNIlH(Object Harg1) 
{
    struct in_java_lang_Class *this = (struct in_java_lang_Class *)Harg1;
    Class cl, super;

    /* do we have a superclass? */
    cl = find_class(this);
    if(cl->nsupers < 2)
        return 0;

    super = cl->supers[1];
    return find_classclass(super);
}

/* java/lang/Class getInterfaces ()[Ljava/lang/Class; */
Object getInterfaces__156xb(Object Harg1) 
{
    struct in_java_lang_Class *this = (struct in_java_lang_Class *)Harg1;
    struct aarray *inters;
    Class c;
    int i;

    c = find_class(this);
    inters = anewarray(&cl_java_lang_Class.C, c->ndinters);

    for(i = 0; i < c->ndinters; i++) {
        inters->data[i] = find_classclass(c->inters[i]);
    }
    return inters;
}

/* java/lang/Class getClassLoader ()Ljava/lang/ClassLoader; */
Object getClassLoader__Gl1Ig(Object Harg1) 
/*ARGSUSED*/
{
    return find_class((struct in_java_lang_Class *)Harg1)->classloader;
}

/* java/lang/Class isInterface ()Z */
Boolean isInterface__so6R0(Object Harg1) 
{
    struct in_java_lang_Class *this = (struct in_java_lang_Class *)Harg1;
    Class cl;

    cl = find_class(this);
    if(cl->flags & IS_INTERFACE)
        return JAVA_TRUE;
    else
        return JAVA_FALSE;
}

