/*
 * Copyright (c) 2000 Daniel Wickstrom
 *
 * See also http://www.aolserver.com for details on the AOLserver
 * web server software
 *
 * 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., 675 Mass Ave, Cambridge,
 * MA 02139, USA.
 * 
 */


#include <nsjava.h>

/*
 *----------------------------------------------------------------------
 *
 * throwByName --
 *
 *	Throw a named java exception.  Null terminated varargs are passed as
 *      the exception message.
 *
 * Results:
 *	Exception is returned to java.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void 
throwByName(JNIEnv *env, const char *name, ...)
{
    Ns_DString  dsPtr;
    jclass      cls;
    va_list     ap;
    char       *s;

    va_start(ap, name);
    Ns_DStringInit(&dsPtr);
    Ns_DStringVarAppend(&dsPtr, name, ": ", NULL);

    while ((s = va_arg(ap, char *)) != NULL) {
	Ns_DStringAppend(&dsPtr, s);
    }
    va_end(ap);

    cls = (*env)->FindClass(env, name);
    if (cls != NULL) {
        (*env)->ThrowNew(env, cls, dsPtr.string);
        (*env)->DeleteLocalRef(env, cls);
    }
    else {
      cls = (*env)->FindClass(env, "java/lang/ClassNotFoundException");
      (*env)->ThrowNew(env, cls, dsPtr.string);
      (*env)->DeleteLocalRef(env, cls);
    }
    Ns_DStringFree(&dsPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * enterMonitor --
 *
 *      enter the java global monitor when entering 'c' and leaving java, 
 *      or at startup after the jvm is initialized.
 *
 * Results:
 *	grabs the monitor
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

void
enterMonitor(JNIEnv *env) 
{
  char *func = "enterMonitor";
  jclass      monitor;
  jclass      gmonitor;

  Ns_ModLog(Debug, javaModLogHandle, "%s: called", func);
  monitor = (*env)->FindClass(env, "nsjava/JavaMonitorLock");
  gmonitor = (*env)->NewGlobalRef(env, (jobject)monitor);
  (*env)->DeleteLocalRef(env, monitor);
  (*env)->MonitorEnter(env, gmonitor);
}

/*
 *----------------------------------------------------------------------
 *
 * exitMonitor --
 *
 *      exit the java global monitor when leaving 'c' and entering java.
 *
 * Results:
 *	releases the monitor
 *
 * Side effects:
 *
 *----------------------------------------------------------------------
 */

void
exitMonitor(JNIEnv *env) 
{
  char *func = "exitMonitor";
  jclass      monitor;
  jclass      gmonitor;

  Ns_ModLog(Debug, javaModLogHandle, "%s: called", func);
  monitor = (*env)->FindClass(env, "nsjava/JavaMonitorLock");
  gmonitor = (*env)->NewGlobalRef(env, (jobject)monitor);
  (*env)->DeleteLocalRef(env, monitor);
  (*env)->MonitorExit(env, gmonitor);
}

/*
 *----------------------------------------------------------------------
 *
 * checkForException --
 *
 *      Check for an exception in java and clear it.  The exception message
 *      will appear in the aolserver log.
 *
 * Results:
 *	Returns true if exception occured.
 *
 * Side effects:
 *	Exception message will appear in the aolserver log file.
 *
 *----------------------------------------------------------------------
 */


int
checkForException(JNIEnv *env) 
{
  jobject exception = (*env)->ExceptionOccurred(env);

  if (exception != NULL) {
    (*env)->ExceptionDescribe(env);
    (*env)->ExceptionClear(env);
    (*env)->DeleteLocalRef(env, exception);
    return JNI_TRUE;
  }

  return JNI_FALSE;
}



/*
 *----------------------------------------------------------------------
 *
 * copyStr --
 *
 *      Used to copy the string generated by java.  This is probably overkill
 *      but the java documentation wasen't clear about what happens to
 *      a copied string when you release it.  I would assume that it would
 *      be untouched, but I need to verify that.
 *
 * Results:
 *	Mallocs a new string buffer and copies the original
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static char *copyStr(char *str) 
{
  int   len;
  int   i;
  char *nstr;

  len  = strlen(str);
  nstr = Ns_Malloc((len+1)*sizeof(char));
  for(i = 0; i < len; i++)
    nstr[i] = str[i];

  nstr[len] = '\0';
  return nstr;
}


/*
 *----------------------------------------------------------------------
 *
 * toString --
 *
 *      call the toString method for any java object.
 *
 * Results:
 *	Returns the java/lang/String output of any java objects toString 
 *      method.
 *
 * Side effects:
 *	Exception message will appear in the aolserver log file.
 *
 *----------------------------------------------------------------------
 */


char *
toString(JNIEnv *env, jobject obj)		
{
  jstring    str;
  jmethodID  mid;
  char      *nstr;
  char      *cstr;
  jclass     cls;

  cls = (*env)->GetObjectClass(env, obj);
  if(checkForException(env) == JNI_TRUE || cls == NULL) {
    return NULL;
  }
  mid = (*env)->GetMethodID(env, cls, "toString", "()Ljava/lang/String;");
  if(checkForException(env) == JNI_TRUE || mid == NULL) {
    return NULL;
  }
  str = (*env)->CallObjectMethod(env, obj, mid);
  if(checkForException(env) == JNI_TRUE || str == NULL) {
    return NULL;
  }
  nstr = (char*)((*env)->GetStringUTFChars(env, str, NULL));
  if(checkForException(env) == JNI_TRUE || nstr == NULL) {
    return NULL;
  }
  cstr = copyStr(nstr);
  (*env)->ReleaseStringUTFChars(env, str, nstr);

  return cstr;
}


int errCheck(int err, Ns_DString *str, char *msg) 
{
  if(err == 1) {
    Ns_DStringAppend(str, msg);
    return 1;
  }

  return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * NsJava_GetTclInterp --
 *
 *	Get the connection interpreter if the connection is not null
 *      otherwise allocate a new interpreter.
 *
 * Results:
 *	Returns an interpreter.
 *
 * Side effects:
 *	A new interpreter is allocated if there is no connection.  For 
 *      scheduled procs, this function is probably leaking memory due to 
 *      the allocation of interpreters.  Need to check this.
 *
 *----------------------------------------------------------------------
 */


Tcl_Interp *NsJava_GetTclInterp(Ns_Conn *conn)
{

  if(conn == NULL) {
    return Ns_TclAllocateInterp(NULL);
  }
  else {
    return Ns_GetConnInterp(conn);
  }
}

