/**********************************************************************
* Copyright (c) 2008, 2009 IBM Corporation and others.
* All rights reserved.   This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* $Id: java.c,v 1.7 2009/07/30 16:52:53 jwest Exp $
* 
* Contributors: 
* IBM - Initial API and implementation
**********************************************************************/

#include "java.h"
#include "ClientCTL.h"
#include "tptp/TPTPJava.h"

#ifdef _WIN32
#include <windows.h>
#endif

#include <stdlib.h>

/* Bug 62342 begins */
#ifdef _WIN32
#define NUM_JVM_OPTIONS 2
#elif defined(__OS400__) /* Bug 95493 */
#define NUM_JVM_OPTIONS 2
#else
#define NUM_JVM_OPTIONS 1
#endif
/* Bug 62342 ends */

/**
* GLOBALS
*/

/* The global JVM handle*/
JavaVM *_java_VM;


/**
* FILE SCOPE VARIABLES
*/
static int _currentJavaJobId = 0;


/**
* PROTOTYPES
*/
#ifdef _WIN32
DWORD WINAPI win32JavaJobProxy(LPVOID args);
#endif

void *CCTLJavaJob(void *args);

tptp_uint32 tptpCreateJavaVitualMachine(tl_state_data_t* stateData, tptp_string *classpath) {
	_java_VM = tptpCreateJVM(classpath);
	return (_java_VM != NULL) ? 0 : -1;
}

tptp_uint32 tptpSubmitJavaJob(tl_state_data_t* stateData, tptpJavaJobImplementation *jobImpl, BOOL asynchronous, TID *tid) {
    tptpJavaJobWrapper* ptr; /* the pointer to the wrapper for the function pointer */
	HANDLE handle;
	
					  /* On AS/400, function and variable pointers are not interchangable.  This wrapper is therefore needed
					  to create threads properly. This pointer is dynamically allocated to avoid a race condition.  It is
	to be freed after the function pointer is passed properly.  Currently, this is done in CCTLJavaJob(). */
    ptr = tptp_malloc(sizeof(tptpJavaJobWrapper));
    ptr -> fcn_ptr = jobImpl;
	ptr->stateData = stateData;

    if (asynchronous) {
        /* Create a thread to run the job on */
#if defined(_WIN32)
        handle = CreateThread(NULL,              /* default security attributes */
			0,                 /* same stack size as current thread */
			win32JavaJobProxy, /* Thread entry point */
			(LPVOID) ptr,      /* params */
			0,                 /* start executing immediately */
			tid);              /* the thread ID */
		CLOSE_THREAD_HANDLE(handle);
#elif defined(_AIX)
		pthread_attr_t thread_attr;

		pthread_attr_init(&thread_attr);
		pthread_attr_setstacksize( &thread_attr, 4194304 );
		int result = pthread_create(tid, &thread_attr, CCTLJavaJob, ptr);
        if (result != 0) {
            return -1;
        }
#else
		int result = pthread_create(tid, NULL, CCTLJavaJob, ptr);
        if (result != 0) {
            return -1;
        }
#endif
    } else {
        tptpJavaJob *job;
		
        /* Allocate memory for the job. */
        job = (tptpJavaJob*) tptp_malloc(sizeof(tptpJavaJob));
        BZERO(job, sizeof(tptpJavaJob));
		job->stateData = stateData;
		
        if (!ATTACH_THREAD(job -> env)) {
            /* Run the job */
            jobImpl(job);
            /* Detach the thread from the JVM */
			DETACH_THREAD();
            /* cleanup */
			tptp_free(job);
        } else {
			/* couldn't attach thread to jvm */
			/* cleanup */
			tptp_free(job);
			return -1;
        }
    }
    return 0;
}


tptp_uint32 tptpDestroyJavaVirtualMachine(tl_state_data_t* stateData) {
	
/* TODO: Currently, JVMs on most platforms do not handle the JVM destruction gracefully, e.g., process crash upon the DestroyJavaVM().
As a result, this call cannot be made until better support is available.
    */
    return 0;
}


void javaJobCleanup(void *arg) {
    tptpJavaJob *job=(tptpJavaJob*)arg;
    /* Notify the listener we have exited */
	
    tptp_free(job);
}



void *CCTLJavaJob(void *args) {
    tptpJavaJobImplementation *jobImpl; /* the entry point to the startup method */
    tptpJavaJob *job; /* the job object */
	
					  /* The pointer args is allocated in tptpSubmitJavaJob() as a wrapper to pass the function pointer
	on AS/400 properly.  Freeing it here the correct behaviour.  */
    jobImpl = ((tptpJavaJobWrapper*) args) -> fcn_ptr;
	
    /* Allocate the job. */
    job = (tptpJavaJob *) tptp_malloc(sizeof(tptpJavaJob));
    BZERO(job, sizeof(tptpJavaJob));
	job->stateData = ((tptpJavaJobWrapper*)args)->stateData;
    tptp_free(args);
	
#ifndef _WIN32
    /* Prepare for cleanup. */
    pthread_cleanup_push(javaJobCleanup, job);
#endif
	
    /* Start the job. */
    if (!ATTACH_THREAD(job -> env)) {
        jobImpl(job);
        DETACH_THREAD();
    } else {
		// Couldn't run job because couldn't attach thread to JVM
    }
	
#ifndef _WIN32
    pthread_cleanup_pop(1);
#else
    javaJobCleanup(job);
#endif
    return (void*) NULL;
}


#ifdef _WIN32
DWORD WINAPI win32JavaJobProxy(LPVOID args) {
	DWORD returnVal = 0;
	CCTLJavaJob(args);
	return returnVal;
}
#endif

/** getnewstrutf **********************************************************************************
  * This function creates a new UTF8 string using the string given, taking care of platform-
  * dependent encoding on AS/400 and OS/390.
  *
  * @param env  the environment in which the search is to be performed
  * @param str  the content of the string to be created
  * @returns the Java string corresponding to the string given.
  */
jstring getnewstrutf(JNIEnv *env, char *str) {
#if defined __OS400__ || defined MVS
    char *asciistr; /* the temporary ASCII string */
    jstring javastr; /* the new Java string */

	if (!str) { 
		return 0;
	}
 #ifdef __OS400__
    asciistr = as400_etoa(str);
 #else
    asciistr = (char *) ra_malloc(strlen(str) + 1);
    strcpy(asciistr, str);
    __etoa(asciistr);
 #endif
    javastr = ENV(env) -> NewStringUTF(ENVPARM(env) asciistr);
    ra_free(asciistr);
    return javastr;
#else
	if (!str) {
		return 0; 
	}
    return ENV(env) -> NewStringUTF(ENVPARM(env) str);
#endif
}

/**************************************************************************************************
 * This function retrieves the class ID for a fully qualified Java class name.  All '.'s are
 * automatically converted to '/' as appropriate.
 **************************************************************************************************/
jclass getcnvcls(JNIEnv *env, char *clsname, int clslen) {
    jclass clsid; /* the class ID for the class name */
    char *slshclsname; /* the class name with all dots replaced by slashes */
    int i; /* loop control variable */

    /* Convert all dots in the class name to slashes as necessary. */
    slshclsname = (char *) tptp_malloc(clslen + 1);
    for (i = 0; i <= clslen; i++) {
    	slshclsname[i] = clsname[i] == '.' ? '/' : clsname[i];
    }

    clsid = fndclsid(env, slshclsname);
    tptp_free(slshclsname);
    return clsid;
}


/** fndmthdid *************************************************************************************
  * This function finds the method ID corresponding to the method given, taking care of platform-
  * dependent encoding on AS/400 and OS/390.
  *
  * @param env        the environment in which the search is to be performed
  * @param clsid      the class ID to which the method belongs
  * @param mthd       the name of the method in native encoding
  * @param sig        the signature of the method in native encoding
  * @returns the Java method ID corresponding to the requested method if any.
  */
jmethodID getmthdid(JNIEnv *env, jclass clsid, char *mthd, char *sig) {
    jmethodID mthdid; /* the resulting method ID */
    char *asciimthd; /* the method name in ASCII format */
    char *asciisig; /* the method signature in ASCII format */

    /* On each platform, obtain the ASCII forms of the method name and signature. */
#ifdef __OS400__
    asciimthd = as400_etoa(mthd);
    asciisig = as400_etoa(sig);
#elif defined MVS
    asciimthd = (char *) ra_malloc(strlen(mthd) + 1);
    asciisig = (char *) ra_malloc(strlen(sig) + 1);
    strcpy(asciimthd, mthd);
    strcpy(asciisig, sig);
    __etoa(asciimthd);
    __etoa(asciisig);
#else
    asciimthd = mthd;
    asciisig = sig;
#endif

    /* Look up the method ID. */
    mthdid = ENV(env) -> GetMethodID(ENVPARM(env) clsid, asciimthd, asciisig);

    /* ra_free memory before the methid ID is returned. */
#if defined __OS400__ || defined MVS
    ra_free(asciimthd);
    ra_free(asciisig);
#endif
    return mthdid;
}


/** fndclsid **************************************************************************************
  * This function finds the class ID corresponding to the class name given, taking care of
  * platform-dependent encoding on AS/400 and OS/390.
  *
  * @param env          the environment in which the search is to be performed
  * @param clsname      the name of the class in native encoding
  * @returns the Java class ID corresponding to the requested class if any.
  */
jclass fndclsid(JNIEnv *env, char *clsname) {
    jclass clsid; /* the class ID for the class name */
    char *asciicls; /* the class name in ASCII format */

/* On AS/400 and OS/390, convert the class name at run-time into the ASCII format for the lookup. */
#ifdef __OS400__
    asciicls = as400_etoa(clsname);
#elif defined MVS
    asciicls = (char *) ra_malloc(strlen(clsname) + 1);
    strcpy(asciicls, clsname);
    __etoa(asciicls);
#else
    asciicls = clsname;
#endif

    /* Look up the class ID, after appropriate transformation. */
    clsid = ENV(env) -> FindClass(ENVPARM(env) asciicls);

/* On AS/400 and OS/390, ra_free the ASCII class name before the class ID is returned. */
#if defined __OS400__ || defined MVS
    ra_free(asciicls);
#endif
    return clsid;
}

