/*
 * Copyright (c) 2003 Alex Leigh, All Rights Reserved
 * 
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 * 
 * Use, disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */

#include <continuity.h>
#include <http.h>
#include <jni.h>

#define PATH_SEPARATOR ';'      /* define it to be ':' on Solaris */
#define USER_CLASSPATH "."      /* where Prog.class is */

JNIEnv *javaGenv;
JavaVM *javaGjvm;

JNIEnv *javaFcreate_vm(char *classpath);
int javaFrun_method(JNIEnv * env, const char *class, const char *method);
int javaFinit(void *p, lstTset * opts);

int javaFinit(void *p, lstTset * opts)
{
    pthread_t tid;
    char *classpath = lstFset_get(opts, "classpath");
    char *initclass = lstFset_get(opts, "initclass");
    char *initmethod = lstFset_get(opts, "initmethod");

    logFmsg(0, "mod/java: Java Runtime Engine v1.0.0");
    logFmsg(0, "mod/java: Copyright (c) 2003, Alex Leigh");

    if (classpath == NULL || initclass == NULL || initmethod == NULL) {
        logFmsg(2,
                "mod/java: javaFinit: classpath, initclass, and initmethod must be specified.");
        exit(0);
    }
    javaGenv = javaFcreate_vm(classpath);
    javaFrun_method(javaGenv, initclass, initmethod);

    return STATUS_OK;
}

int javaFrun_method(JNIEnv * env, const char *class, const char *method)
{
    jclass cls;
    jmethodID mid;
    jstring jstr;
    jclass stringClass;
    jobjectArray args;

    cls = (*env)->FindClass(env, class);
    if (cls == NULL) {
        logFmsg(2, "mod/java: init class %s could not be loaded.", class);
        exit(0);
    }
    mid = (*env)->GetStaticMethodID(env, cls, method,
                                    "([Ljava/lang/String;)V");
    if (mid == NULL) {
        logFmsg(2, "mod/java: init method %s.%s could not be loaded.",
                class, method);
        exit(0);

    }
    jstr = (*env)->NewStringUTF(env, " from C!");
    if (jstr == NULL) {

    }
    stringClass = (*env)->FindClass(env, "java/lang/String");
    args = (*env)->NewObjectArray(env, 1, stringClass, jstr);
    if (args == NULL) {

    }

    (*env)->CallStaticVoidMethod(env, cls, mid, args);

    return STATUS_OK;
}

JNIEnv *javaFcreate_vm(char *classpath)
{
    JNIEnv *env;
    jint res;
    jclass cls;
    jmethodID mid;
    jstring jstr;
    jclass stringClass;
    jobjectArray args;
    char argbuf[512];

#ifdef JNI_VERSION_1_2
    JavaVMInitArgs vm_args;
    JavaVMOption options[3];

    sprintf(argbuf, "-Djava.class.path=%s", classpath);

    options[0].optionString = &argbuf;
    options[1].optionString = "-Djava.security.policy=lib/java.policy";
    options[2].optionString = "-verbose:jni";
    vm_args.version = 0x00010002;
    vm_args.options = options;
    vm_args.nOptions = 3;
    vm_args.ignoreUnrecognized = JNI_TRUE;
    /* Create the Java VM */
    res = JNI_CreateJavaVM(&javaGjvm, (void **) &env, &vm_args);
#else
    JDK1_1InitArgs vm_args;
    char classpath[1024];
    vm_args.version = 0x00010001;
    JNI_GetDefaultJavaVMInitArgs(&vm_args);
    /* Append USER_CLASSPATH to the default system class path */
    sprintf(classpath, "%s%c%s",
            vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
    vm_args.classpath = classpath;
    /* Create the Java VM */
    res = JNI_CreateJavaVM(&javaGjvm, &env, &vm_args);
#endif                          /* JNI_VERSION_1_2 */

    if (res < 0) {
        fprintf(stderr, "Can't create Java VM\n");
        exit(1);
    }
    return env;

  destroy:
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
    }
    (*javaGjvm)->DestroyJavaVM(javaGjvm);

    return NULL;
}

int javaFservice(httpTtrans * t, lstTset * opts)
{
    jint res;
    JNIEnv *env;
    const char *class_arg = lstFset_get(opts, "class");
    const char *method_arg = lstFset_get(opts, "method");

    logFmsg(3, "mod/java: service: Called.");

    if (class_arg == NULL || method_arg == NULL) {
        logFmsg(2,
                "mod/java: javaFservice: method or class not specified.");
        return STATUS_ERROR;
    }
#ifdef JNI_VERSION_1_2
    res = (*javaGjvm)->AttachCurrentThread(javaGjvm, (void **) &env, NULL);
#else
    res = (*javaGjvm)->AttachCurrentThread(javaGjvm, &env, NULL);
#endif

    if (res < 0) {
        logFmsg(2, "mod/java: thread attach to VM failed.");
        return STATUS_ERROR;
    }

    javaFrun_method(env, class_arg, method_arg);
    return STATUS_EXIT;
}
