/* -*- Mode: C; c-file-style: "gnu" -*-
   japharJVMPluginInstance.cpp -- japhar's mozilla java runtime support (this class is instanciated for APPLET tags.)
   Created: Chris Toshok <toshok@hungry.com>, 8-Aug-1998
 */
/*
  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
*/
#include "config.h"

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

#include <Xm/XmP.h>
#include <Xm/DrawingA.h>

extern "C" {
#include "../libnative/sun.awt.motif/common.h" /* XXXX */
};

#include "arch.h"
#include "runtimeint.h"
#include "nsIJVMPluginTagInfo.h"
#include "nsIEventHandler.h"
#include "nsIPluginTagInfo2.h"
#include "japharJVMPluginInstance.h"

extern PRLogModuleInfo *japharOJILm;

japharJVMPluginInstance::japharJVMPluginInstance(JavaVM *vm,
						 nsISupports *pluginManager)
{
  _vm = vm;
  _pluginman = pluginManager;
  _started = JNI_FALSE;
}

/////////////////////////////////////////////////
// nsISupports
/////////////////////////////////////////////////

NS_IMPL_ADDREF(japharJVMPluginInstance)
  NS_IMPL_RELEASE(japharJVMPluginInstance)

  nsresult
japharJVMPluginInstance::QueryInterface(nsID const& aIID,
					void** aInstancePtr)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::QueryInterface()\n"));
  if (NULL == aInstancePtr) {
    return NS_ERROR_NULL_POINTER;
  }

  *aInstancePtr = NULL;

  if (aIID.Equals(nsCOMTypeInfo<nsIJVMPluginInstance>::GetIID())) {
    *aInstancePtr = NS_STATIC_CAST(nsIJVMPluginInstance*, this);
    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning nsIJVMPluginInstance\n"));
  }
  if (aIID.Equals(nsCOMTypeInfo<nsIPluginInstance>::GetIID())) {
    *aInstancePtr = NS_STATIC_CAST(nsIPluginInstance*, this);
    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning nsIPluginInstance\n"));
  }
#if 0 /* XXXX nsIEventHandler doesn't have GetIID.  ugh. */
  if (aIID.Equals(nsCOMTypeInfo<nsIEventHandler>::GetIID())) {
    *aInstancePtr = NS_STATIC_CAST(nsIEventHandler*, this);
    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning nsIEventHandler\n"));
  }
#endif
  if (aIID.Equals(nsCOMTypeInfo<nsISupports>::GetIID())) {
    *aInstancePtr = NS_STATIC_CAST(nsISupports*, this);
    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning nsISupports\n"));
  }

  nsresult status;

  if (!*aInstancePtr)
    {
      PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning NS_NOINTERFACE\n"));
      status = NS_NOINTERFACE;
    }
  else
    {
      NS_ADDREF( NS_REINTERPRET_CAST(nsISupports*, *aInstancePtr) );    
      status = NS_OK;
    }

  return status;
}

/////////////////////////////////////////////////
// nsIEventHandler
/////////////////////////////////////////////////

/**
 * Handles an event. An nsIEventHandler can also get registered with with
 * nsIPluginManager2::RegisterWindow and will be called whenever an event
 * comes in for that window.
 *
 * Note that for Unix and Mac the nsPluginEvent structure is different
 * from the old NPEvent structure -- it's no longer the native event
 * record, but is instead a struct. This was done for future extensibility,
 * and so that the Mac could receive the window argument too. For Windows
 * and OS2, it's always been a struct, so there's no change for them.
 *
 * (Corresponds to NPP_HandleEvent.)
 *
 * @param event - the event to be handled
 * @param handled - set to PR_TRUE if event was handled
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::HandleEvent(nsPluginEvent* event,
				     PRBool* handled)
{
  /* UNIX Plugins do not use HandleEvent */
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::HandleEvent\n"));
  return 0;
}


/////////////////////////////////////////////////
// nsIPluginInstance
/////////////////////////////////////////////////

/**
 * Initializes a newly created plugin instance, passing to it the plugin
 * instance peer which it should use for all communication back to the browser.
 * 
 * @param peer - the corresponding plugin instance peer
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::Initialize(nsIPluginInstancePeer* peer)
{
  static NS_DEFINE_IID(kIJVMPluginTagInfoIID, NS_IJVMPLUGINTAGINFO_IID);
  static NS_DEFINE_IID(kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID);

  PR_LOG (japharOJILm, PR_LOG_DEBUG,
	  ("japharJVMPluginInstance::Initialize(vm = %p, peer = %p)\n", _vm, peer));

  nsresult res;

  _peer = peer;
  _peer->AddRef(); /* XX */

  _peer->ShowStatus("Initializing JVM Plugin Instance...");

  /* make sure this thread has a JNIEnv associated with it */
  /* if the thread already has an env, get ahold of it */
  if (_vm->GetEnv((void**)&_env, JNI_VERSION_1_2) < 0)
    {
      JavaVMAttachArgs attach_args;
      
      attach_args.version = JNI_VERSION_1_2;
      attach_args.name = "java-thread";
      attach_args.group = NULL;
      
      /* if that failed, try to attach to the current thread */
      if (_vm->AttachCurrentThread(&_env, &attach_args) < 0)
	{
	  assert(0);
	  return NS_ERROR_FAILURE;
	}
    }

  nsIJVMPluginTagInfo *info;
  res = _peer->QueryInterface(kIJVMPluginTagInfoIID,
			      (void**)&info);

  if (NS_SUCCEEDED(res))
    {
      /* get information about the applet (code, codebase, archive, name, mayscript) */

      /* get the CODE applet attribute (the applet class) */
      if (NS_FAILED( info->GetCode(&_code) ))
	PR_LOG (japharOJILm, PR_LOG_DEBUG, ("error getting code for JVM Plugin Instance\n"));
      else
	PR_LOG (japharOJILm, PR_LOG_DEBUG, ("code == %s\n", _code));

      /* get the CODEBASE applet attribute */
      if (NS_FAILED( info->GetCodeBase(&_codebase) ))
	PR_LOG (japharOJILm, PR_LOG_DEBUG, ("error getting codebase for JVM Plugin Instance\n"));
      else if (_codebase == NULL)
	PR_LOG (japharOJILm, PR_LOG_DEBUG, ("codebase not set in applet tag\n"));
      else
	PR_LOG (japharOJILm, PR_LOG_DEBUG, ("codebase == %s\n", _codebase));

      /* get the ARCHIVE applet attribute */
      if (NS_FAILED( info->GetArchive(&_archive) ))
	PR_LOG (japharOJILm, PR_LOG_DEBUG, ("error getting archive for JVM Plugin Instance\n"));
      else
	PR_LOG (japharOJILm, PR_LOG_DEBUG, ("archive == %s\n", _archive));

      /* get the NAME applet attribute */
      if (NS_FAILED( info->GetName(&_name) ))
	PR_LOG (japharOJILm, PR_LOG_DEBUG, ("error getting name for JVM Plugin Instance\n"));
      else
	PR_LOG (japharOJILm, PR_LOG_DEBUG, ("name == %s\n", _name));

      /* get the MAYSCRIPT applet attribute */
      if (NS_FAILED( info->GetMayScript(&_mayscript) ))
	PR_LOG (japharOJILm, PR_LOG_DEBUG,
		("error getting mayscript for JVM Plugin Instance\n"));
      else
	PR_LOG (japharOJILm, PR_LOG_DEBUG, ("mayscript == %d\n", _mayscript));

      info->Release();
    }

  nsIPluginTagInfo2 *info2;
  res = _peer->QueryInterface(kIPluginTagInfo2IID,
			      (void**)&info2);

  if (NS_SUCCEEDED(res))
    {
      /* get the parameters to the applet */
      nsresult r;
      PRUint16 n;
      const char* const* names;
      const char* const* values;

      r = info2->GetParameters(n, names, values);
      if (NS_FAILED(r))
	PR_LOG (japharOJILm, PR_LOG_DEBUG,
		("error getting parameters for JVM Plugin Instance\n"));
      else
	{
	  int i;
	  for (i = 0; i < n; i ++)
	    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("param(%s) = %s\n", names[i], values[i]));
	}

      if (_codebase == NULL)
	{
	  r = info2->GetDocumentBase(&_codebase);
	  if (NS_FAILED(r))
	    PR_LOG (japharOJILm, PR_LOG_DEBUG,
		    ("error getting document base for JVM Plugin Instance\n"));
	  else
	    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("_codebase == %s\n", _codebase));

	  if (_codebase != NULL)
	    {
	      char *slash = strrchr(_codebase, '/');
	      
	      if (slash)
		slash[1] = 0; /* terminate the string after the slash. */
	    }
	}
    }

  /* if we couldn't get a valid codebase, error out. */
  if (_codebase == NULL)
    {
      info2->Release();
      return NS_ERROR_FAILURE;
    }

  /* if it's got a ':', assume it's got a protocol in it someplace and
     use it unmolested */
  if (!strchr(_codebase, ':'))
    {
      const char *doc_base;
      nsresult r;

      r = info2->GetDocumentBase(&doc_base);
      if (NS_FAILED(r))
	return NS_ERROR_FAILURE;
	
      if (_codebase[0] == '/')
	{
	  /* it's an absolute URL, just stick the hostname and protocol on
	     the front of it. */
	  char *end_hostname;

	  end_hostname = strstr(doc_base, "//");
	  if (end_hostname == NULL)
	    return NS_ERROR_FAILURE;

	  end_hostname += 2; /* strlen("//") */
	  end_hostname = strchr(end_hostname, '/');
	  if (end_hostname)
	    {
	      *end_hostname = 0;
	      char *real_codebase = (char*)malloc(strlen(doc_base)
						  + strlen(_codebase)
						  + 2);
	      strcpy(real_codebase, doc_base);
	      strcat(real_codebase, _codebase);
	      strcat(real_codebase, "/"); /* XXXX */

	      *end_hostname = '/';

	      free((char*)_codebase);
	      _codebase = real_codebase;
	    }
	  else
	    {
	      assert(0);
	    }
	}
      else
	{
	}
    }

  info2->Release();

  if (_codebase[strlen(_codebase) - 1] != '/')
    {
      char *new_codebase = (char*)malloc(strlen(_codebase) + 2);
      strcpy(new_codebase, _codebase);
      strcat(new_codebase, "/");
      free((char*)_codebase);
      _codebase = new_codebase;
    }

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("final codebase = %s\n", _codebase));

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("setting up applet context\n"));
  res = InitializeAppletContext();
  if (NS_FAILED(res))
    {
      PR_LOG (japharOJILm, PR_LOG_DEBUG,
	      ("japharJVMPluginInstance::InitializeAppletContext failed\n"));
      return res;
    }

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("setting up class loader\n"));
  res = InitializeClassLoader();
  if (NS_FAILED(res))
    {
      PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::InitializeClassLoader failed\n"));
      return res;
    }

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("loading applet\n"));
  res = InitializeApplet();
  if (NS_FAILED(res))
    {
      PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::InitializeApplet failed\n"));
      return res;
    }

  return res;
}

/**
 * Returns a reference back to the plugin instance peer. This method is
 * used whenever the browser needs to obtain the peer back from a plugin
 * instance. The implementation of this method should be sure to increment
 * the reference count on the peer by calling AddRef.
 *
 * @param resultingPeer - the resulting plugin instance peer
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::GetPeer(nsIPluginInstancePeer* *resultingPeer)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::GetPeer\n"));

  *resultingPeer = _peer;
  return NS_OK;
}

/**
 * Called to instruct the plugin instance to start. This will be called after
 * the plugin is first created and initialized, and may be called after the
 * plugin is stopped (via the Stop method) if the plugin instance is returned
 * to in the browser window's history.
 *
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::Start(void)
{
  if (_started == JNI_FALSE)
    {
      _started = JNI_TRUE;

      PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::Start\n"));
      _peer->ShowStatus("Starting JVM Plugin Instance...");
      
      if (_start_method)
	_env->CallVoidMethod(_obj, _start_method);
      
    }
  return NS_OK;
}
  
/**
 * Called to instruct the plugin instance to stop, thereby suspending its state.
 * This method will be called whenever the browser window goes on to display
 * another page and the page containing the plugin goes into the window's history
 * list.
 *
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::Stop(void)
{
  if (_started == JNI_TRUE)
    {
      _started = JNI_FALSE;

      PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::Stop\n"));
      _peer->ShowStatus("Stopping JVM Plugin Instance...");
      
      if (_stop_method)
	_env->CallVoidMethod(_obj, _stop_method);
    }

  return NS_OK;
}
  
/**
 * Called to instruct the plugin instance to destroy itself. This is called when
 * it become no longer possible to return to the plugin instance, either because 
 * the browser window's history list of pages is being trimmed, or because the
 * window containing this page in the history is being closed.
 *
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::Destroy(void)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::Destroy\n"));
  _peer->ShowStatus("Destroying JVM Plugin Instance...");

  if (_destroy_method)
    _env->CallVoidMethod(_obj, _destroy_method);

  return NS_OK;
}

extern "C" void initialize_awt_with_stuff(Widget, XtAppContext);

static Widget
find_toplevel(Widget w)
{
  while (XtParent(w))
    {
      w = XtParent(w);
    }

  return w;
}
  
/**
 * Called when the window containing the plugin instance changes.
 *
 * (Corresponds to NPP_SetWindow.)
 *
 * @param window - the plugin window structure
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::SetWindow(nsPluginWindow* window)
{
  Widget netscape_widget;

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::SetWindow(%p)\n", window));

#if 0
  if (window != NULL)
    {
      if ((Window)window->window != _window)
	{
	  _window = (Window) window->window;
	  _x = window->x;
	  _y = window->y;
	  _width = window->width;
	  _height = window->height;
	  _display = ((nsPluginSetWindowCallbackStruct *)window->ws_info)->display;
	  
	  netscape_widget = XtWindowToWidget(_display, _window);

	  initialize_awt();

	  jfieldID peer_field = _env->GetFieldID(_env->FindClass("java/awt/Component"),
						 "peer",
						 "Ljava/awt/peer/ComponentPeer;");
	  jobject peer = _env->GetObjectField(_obj,
					      peer_field);
	  Widget w = get_component_widget(_env, peer);

	  assert(w != NULL);

	  PR_LOG (japharOJILm, PR_LOG_DEBUG,
		  ("XtWindow(w) = %p\n_window = %p\n", XtWindow(w), _window));

#if 0
	  {
	    CompositeWidgetClass c = (CompositeWidgetClass) XtClass(_awt_toplevelWidget);
	    (c->composite_class.delete_child)(w);
	  }

	  XtParent(w) = netscape_widget;

	  {
	    CompositeWidgetClass c = (CompositeWidgetClass) XtClass(netscape_widget);
	    (c->composite_class.insert_child)(w);
	  }

	  if (XtWindow(w))
	    XReparentWindow(_display, XtWindow(w), _window, 0, 0);
	  else
	    XtRealizeWidget(w);

#if 0
	  XtAddEventHandler(netscape_widget, ExposureMask, FALSE, (XtEventHandler)Redraw, this);
	  Redraw(netscape_widget, (XtPointer)this, NULL);
#endif
#endif
	}
    }
  else
    {
      // window == NULL when we're being destroyed.
    }

#endif

  return NS_OK;
}

/**
 * Called to tell the plugin that the initial src/data stream is
 * ready.  Expects the plugin to return a nsIPluginStreamListener.
 *
 * (Corresponds to NPP_NewStream.)
 *
 * @param listener - listener the browser will use to give the plugin the data
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::NewStream(nsIPluginStreamListener** listener)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::NewStream\n"));
  return NS_OK;
}
  
/**
 * Called to instruct the plugin instance to print itself to a printer.
 *
 * (Corresponds to NPP_Print.)
 *
 * @param platformPrint - platform-specific printing information
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::Print(nsPluginPrint* platformPrint)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::Print\n"));
  return NS_OK;
}

/**
 * Called to notify the plugin instance that a URL request has been
 * completed. (See nsIPluginManager::GetURL and nsIPluginManager::PostURL).
 *
 * (Corresponds to NPP_URLNotify.)
 *
 * @param url - the requested URL
 * @param target - the target window name
 * @param reason - the reason for completion
 * @param notifyData - the notify data supplied to GetURL or PostURL
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::URLNotify(const char* url, const char* target,
				   nsPluginReason reason, void* notifyData)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::URLNotify\n"));
  return NS_OK;
}

/**
 * Returns the value of a variable associated with the plugin instance.
 *
 * @param variable - the plugin instance variable to get
 * @param value - the address of where to store the resulting value
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPluginInstance::GetValue(nsPluginInstanceVariable variable,
				  void *value)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG,
	  ("japharJVMPluginInstance::GetValue(variable == %d)\n", variable));
  if (variable == nsPluginInstanceVariable_WindowlessBool)
    {
      /* these should be PRBool's, but the plugin guys have their heads up their
	 collective asses. */
      *(unsigned char*)value = 0;
      return NS_OK;
    }
  else if (variable == nsPluginInstanceVariable_TransparentBool)
    {
      *(unsigned char*)value = PR_FALSE;
      return NS_OK;
    }
  else
    {
      return NS_ERROR_ILLEGAL_VALUE;
    }
}


/////////////////////////////////////////////////
// nsIJVMPluginInstance
/////////////////////////////////////////////////
  
// This method is called when LiveConnect wants to find the Java object
// associated with this plugin instance, e.g. the Applet or JavaBean object.
nsresult
japharJVMPluginInstance::GetJavaObject(jobject *result)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::GetJavaObject\n"));

  *result = _obj;
  return NS_OK;
}

nsresult
japharJVMPluginInstance::GetText(const char* *result)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::GetText\n"));
  return NS_OK;
}

nsISupports*
japharJVMPluginInstance::GetPluginManager()
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPluginInstance::GetPluginManager\n"));
  return _pluginman;
}

void
japharJVMPluginInstance::Redraw(Widget w,
				XtPointer closure,
				XEvent *event)
{
  GC gc;
  XGCValues gcv;
  const char* text = "I am a Java Applet";
  japharJVMPluginInstance* inst = (japharJVMPluginInstance*)closure;

  XtVaGetValues(w, XtNbackground, &gcv.background,
                XtNforeground, &gcv.foreground, 0);
  gc = XCreateGC(inst->_display, inst->_window, 
                 GCForeground|GCBackground, &gcv);
#if 0
  XDrawRectangle(inst->_display, inst->_window, gc, 
                 0, 0, inst->_width-1, inst->_height-1);
#endif
  XDrawString(inst->_display, inst->_window, gc, 
              inst->_width/2 - 100, inst->_height/2,
              text, strlen(text));
}

nsresult
japharJVMPluginInstance::InitializeClassLoader()
{
  jmethodID class_loader_ctor;
  jclass url_class;
  jmethodID url_ctor;
  jobject url;
  jstring codebase = _env->NewStringUTF(_codebase);

  url_class = _env->FindClass("java/net/URL");
  url_ctor = _env->GetMethodID(url_class, "<init>",
			       "(Ljava/lang/String;)V");
  url = _env->NewObject(url_class, url_ctor, codebase);

  if (_env->ExceptionOccurred())
    {
      _env->ExceptionDescribe();
      _env->ExceptionClear();
      return NS_ERROR_FAILURE;
    }

  _class_loader_class = _env->FindClass("com/hungry/java/japhar/JapharClassLoader");

  class_loader_ctor = _env->GetMethodID(_class_loader_class,
					"<init>", "(Ljava/net/URL;)V");

  _class_loader = _env->NewObject(_class_loader_class,
				  class_loader_ctor, url);

  return NS_OK;
}

nsresult
japharJVMPluginInstance::InitializeAppletContext()
{
  jclass _applContext_class;
  jmethodID applContext_ctor;

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("+  getting class\n"));
  _applContext_class = _env->FindClass("com/hungry/java/japhar/JapharAppletContext");
  assert(_applContext_class);
  if (!_applContext_class)
    return NS_ERROR_FAILURE;

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("+  getting constructor\n"));
  applContext_ctor= _env->GetMethodID(_applContext_class, "<init>", "(I)V");
  assert(applContext_ctor);
  if (!applContext_ctor)
    return NS_ERROR_FAILURE;

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("+  making object\n"));
  _applContext = _env->NewObject(_applContext_class, applContext_ctor, (jint)this);
  assert(_applContext);
  if (!_applContext)
    return NS_ERROR_FAILURE;

  return NS_OK;
}

nsresult
japharJVMPluginInstance::InitializeApplet()
{
#if 0
  jmethodID loadCode = _env->GetMethodID(_class_loader_class,
					 "loadCode", "(Ljava/lang/String;)Ljava/lang/Class;");
  assert(loadCode);
  jstring code_string = _env->NewStringUTF(_code);

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("+  calling loadCode on classloader(%s)\n", _code));
  _applet_class = _env->CallObjectMethod(_class_loader,
					 loadCode, code_string);

  if (_env->ExceptionOccurred())
    {
      _env->ExceptionDescribe();
      _env->ExceptionClear();
      return NS_ERROR_FAILURE;
    }
#else
  _applet_class = _env->FindClass(_code);
#endif

  assert(_applet_class);
  if (!_applet_class)
    {
      PR_LOG (japharOJILm, PR_LOG_DEBUG, ("   failed to load applet\n"));
      return NS_ERROR_FAILURE;
    }

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("+  creating applet instance\n"));
  jmethodID ctor = _env->GetMethodID(_applet_class, "<init>", "()V");
  assert(ctor);
  if (!ctor)
    return NS_ERROR_FAILURE;
  _obj = _env->NewObject(_applet_class, ctor);
  assert(_obj);
  if (!_obj)
    return NS_ERROR_FAILURE;

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("+  calling addNotify\n"));
  jmethodID addNotify = _env->GetMethodID(_applet_class, "addNotify", "()V");
  assert(addNotify);
  if (!addNotify)
    return NS_ERROR_FAILURE;
  _env->CallVoidMethod(_obj, addNotify);

  _init_method = _env->GetMethodID(_applet_class, "init", "()V");
  _start_method = _env->GetMethodID(_applet_class, "start", "()V");
  _stop_method = _env->GetMethodID(_applet_class, "stop", "()V");
  _destroy_method = _env->GetMethodID(_applet_class, "destroy", "()V");

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("+  calling setStub\n"));
  jmethodID setStub = _env->GetMethodID(_applet_class, "setStub", "(Ljava/applet/AppletStub;)V");
  _env->CallVoidMethod(_obj, setStub, _applContext);

  if (_init_method)
    {
      PR_LOG (japharOJILm, PR_LOG_DEBUG, ("+  calling init method\n"));
      _env->CallVoidMethod(_obj, _init_method);
    }
      
  return NS_OK;
}
