/* -*- Mode: C; c-file-style: "gnu" -*-
   japharJVMPlugin.cpp -- japhar's mozilla java runtime support
   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 "jniint.h"
#include "japharJVMPlugin.h"
#include "japharJVMPluginInstance.h"
#include "japharSecureEnv.h"

extern PRLogModuleInfo *japharOJILm = NULL;

static PRBool initialized = PR_FALSE;

japharJVMPlugin::japharJVMPlugin()
{
  _vm = NULL;
  _env = NULL;
  _env_refcnt = 0;
  _factory_monitor = NULL;

  if (japharOJILm == NULL)
    japharOJILm = PR_NewLogModule("JapharOJI");
}

JavaVM*
japharJVMPlugin::GetRunningVM()
{
  StartupJVM();

  return _vm;
}

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

NS_IMPL_ADDREF(japharJVMPlugin)
NS_IMPL_RELEASE(japharJVMPlugin)

nsresult
japharJVMPlugin::QueryInterface(REFNSIID aIID,
				void** aInstancePtr)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::QueryInterface(this=%p)\n", this));
  if (NULL == aInstancePtr) {
    return NS_ERROR_NULL_POINTER;
  }

  *aInstancePtr = NULL;

  static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
  static NS_DEFINE_IID(kIJVMPluginIID, NS_IJVMPLUGIN_IID);
  static NS_DEFINE_IID(kIJVMConsoleIID, NS_IJVMCONSOLE_IID);
  static NS_DEFINE_IID(kIPluginIID, NS_IPLUGIN_IID);
  static NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID);

  if (aIID.Equals(kIJVMPluginIID)) {
    *aInstancePtr = NS_STATIC_CAST(nsIJVMPlugin*, this);
    AddRef();
    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning nsIJVMPlugin\n"));
    return NS_OK;
  }
  if (aIID.Equals(kIPluginIID)) {
    *aInstancePtr = NS_STATIC_CAST(nsIPlugin*, this);
    AddRef();
    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning nsIPlugin\n"));
    return NS_OK;
  }
  if (aIID.Equals(kIFactoryIID)) {
    *aInstancePtr = NS_STATIC_CAST(nsIFactory*, this);
    AddRef();
    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning nsIFactory\n"));
    return NS_OK;
  }
  if (aIID.Equals(kISupportsIID)) {
    *aInstancePtr = NS_STATIC_CAST(nsISupports*, this);
    AddRef();
    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning nsISupports\n"));
    return NS_OK;
  }

#if 0
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("deferring to japharJVMConsole\n"));
  if (_console == NULL)
    {
      japharJVMConsole *con = new japharJVMConsole(this);

      if (con)
	{
	  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("Calling queryinterface on the console\n"));
	  nsresult ok = con->QueryInterface(kISupportsIID,
					    (void**)&_console);

	  if (NS_FAILED(ok))
	    _console = NULL;
	}
    }

  if (_console)
    {
      return _console->QueryInterface(aIID, aInstancePtr);
    }
  else
    {
#endif
      PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning NS_NOINTERFACE\n"));
      return NS_NOINTERFACE;
#if 0
    }
#endif
}

/////////////////////////////////////////////////
// nsIFactory
/////////////////////////////////////////////////

nsresult
japharJVMPlugin::CreateInstance(nsISupports *aOuter,
				REFNSIID aIID,
				void **aResult)
{
  StartupJVM();

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::CreateInstance(this=%p)\n", this));

  if (aResult == NULL) {
    PR_LOG (japharOJILm, PR_LOG_DEBUG, (" + return NS_ERROR_NULL_POINTER\n"));
    return NS_ERROR_NULL_POINTER;
  }

  *aResult = NULL;

  nsISupports* inst = new japharJVMPluginInstance(_vm, _pluginman);

  if (inst == NULL) {
    PR_LOG (japharOJILm, PR_LOG_DEBUG, ("+ returning NS_ERROR_OUT_OF_MEMORY\n"));
    return NS_ERROR_OUT_OF_MEMORY;
  }

  nsresult res = inst->QueryInterface(aIID, aResult);

  if (NS_FAILED(res)) {
    // We didn't get the right interface, so clean up
    PR_LOG (japharOJILm, PR_LOG_DEBUG, (" + returning %d, failed QueryInterface on instance\n", res));
    delete inst;
  }

  return res;
}

nsresult
japharJVMPlugin::LockFactory(PRBool aLock)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::LockFactory(this=%p)\n", this));
  if (aLock)
    PR_EnterMonitor(_factory_monitor);
  else
    PR_ExitMonitor(_factory_monitor);
}

/////////////////////////////////////////////////
// nsIPlugin
/////////////////////////////////////////////////

/**
 * Creates a new plugin instance, based on a MIME type. This
 * allows different impelementations to be created depending on
 * the specified MIME type.
 */
nsresult
japharJVMPlugin::CreatePluginInstance(nsISupports *aOuter, REFNSIID aIID, 
				      const char* aPluginMIMEType,
				      void **aResult)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::CreatePluginInstance(this = %p)\n", this));

  StartupJVM();

  return NS_ERROR_FAILURE;
}


/**
 * Initializes the plugin and will be called before any new instances are
 * created. It is passed browserInterfaces on which QueryInterface
 * may be used to obtain an nsIPluginManager, and other interfaces.
 *
 * @param browserInterfaces - an object that allows access to other browser
 * interfaces via QueryInterface
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPlugin::Initialize()
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::Initialize(this=%p)\n", this));

  if (!_factory_monitor)
    _factory_monitor = PR_NewMonitor();

  return NS_OK;
}
  
/**
 * Called when the browser is done with the plugin factory, or when
 * the plugin is disabled by the user.
 *
 * (Corresponds to NPP_Shutdown.)
 *
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPlugin::Shutdown(void)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::Shutdown(this=%p)\n", this));
  PR_DestroyMonitor(_factory_monitor);
  _factory_monitor = NULL;
  return NS_OK;
}

/**
 * Returns the MIME description for the plugin. The MIME description 
 * is a colon-separated string containg the plugin MIME type, plugin
 * data file extension, and plugin name, e.g.:
 *
 * "application/x-simple-plugin:smp:Simple LiveConnect Sample Plug-in"
 *
 * (Corresponds to NPP_GetMIMEDescription.)
 *
 * @param resultingDesc - the resulting MIME description 
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPlugin::GetMIMEDescription(const char* *resultingDesc)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::GetMIMEDescription(this=%p)\n", this));
  *resultingDesc = JAPHAR_MIME_DESC;
  return NS_OK;
}
  
/**
 * Returns the value of a variable associated with the plugin.
 *
 * (Corresponds to NPP_GetValue.)
 *
 * @param variable - the plugin variable to get
 * @param value - the address of where to store the resulting value
 * @result - NS_OK if this operation was successful
 */
nsresult
japharJVMPlugin::GetValue(nsPluginVariable variable, void *value)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::GetValue(this=%p)\n", this));
  return NS_OK;
}
  
/////////////////////////////////////////////////
// nsIJVMPlugin
/////////////////////////////////////////////////

nsresult
japharJVMPlugin::StartupJVM(void)
{
  static NS_DEFINE_IID(kIPluginManagerIID, NS_IPLUGINMANAGER_IID);
  static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID);
  nsresult res;

  if (initialized)
    return NS_OK;

  initialized = PR_TRUE;

  res = nsComponentManager::CreateInstance(kCPluginManagerCID, 
					   NULL, kIPluginManagerIID, (void**)&_pluginman);
  if (NS_FAILED(res))
    {
      PR_LOG (japharOJILm, PR_LOG_DEBUG, ("failed to create instance of nsIPluginManager\n"));
      return res;
    }

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("starting up JVM\n"));

  JavaVMInitArgs vm_args;
  JavaVMOption options[1];

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::StartupJVM(this=%p)\n", this));

  vm_args.version = JNI_VERSION_1_2;
  vm_args.nOptions = 1;

  /* initialize the classpath */
  /* XXX get the mozilla setting from a pref perhaps? */
  char *cp_value;
  HVMClasspath *user_classpath = HVM_ClasspathGetUserClasspath();
  HVMClasspath *sys_classpath = HVM_ClasspathGetSystemClasspath();
  user_classpath = HVM_ClasspathConcat(user_classpath, sys_classpath);
  cp_value = HVM_ClasspathToString(user_classpath);
  HVM_ClasspathDestroy(user_classpath);

  options[0].optionString = PR_smprintf("-Djava.class.path=%s%s.",
					cp_value, PATH_SEPARATOR);
  vm_args.options = options;

  if (JNI_CreateJavaVM(&_vm, &_env,
		       &vm_args) != 0)
    {
      PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning NS_ERROR_FAILURE\n"));
      return NS_ERROR_FAILURE;
    }

  /* set the security manager */
  jclass applet_security = _env->FindClass("sun/applet/AppletSecurity");
  jmethodID sec_mgr_ctor = _env->GetMethodID(applet_security, "<init>", "()V");
  jobject sec_manager = _env->NewObject(applet_security, sec_mgr_ctor);
  jclass system_cls = _env->FindClass("java/lang/System");
  jmethodID setSecurityManager = _env->GetStaticMethodID(system_cls, "setSecurityManager",
							 "(Ljava/lang/SecurityManager;)V");
  _env->CallStaticVoidMethod(system_cls,
			     setSecurityManager, sec_manager);
  if (_env->ExceptionOccurred())
    {
      _env->ExceptionDescribe();
      _env->ExceptionClear();
      return NS_ERROR_FAILURE;
    }

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("  + returning NS_OK\n"));
  return NS_OK;
}
  
// Causes the JVM to append a new directory to its classpath.
// If the JVM doesn't support this operation, an error is returned.
nsresult
japharJVMPlugin::AddToClassPath(const char* dirPath)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  HungryVM *hvm = henv->vm;

  PR_LOG (japharOJILm, PR_LOG_DEBUG,
	  ("japharJVMPlugin::AddToClassPath(this=%p, dirPath=%s)\n", this, dirPath));

  return ((HVM_ClasspathAddPath(hvm->_cp, dirPath) == PR_SUCCESS) 
	  ? NS_OK
	  : NS_ERROR_FAILURE);
}
  
// Causes the JVM to remove a directory from its classpath.
// If the JVM doesn't support this operation, an error is returned.
nsresult
japharJVMPlugin::RemoveFromClassPath(const char* dirPath)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  HungryVM *hvm = henv->vm;

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::RemoveFromClassPath(this=%p, dirPath=%s)\n", this, dirPath));

  return ((HVM_ClasspathRemovePath(hvm->_cp, dirPath) == PR_SUCCESS) 
	  ? NS_OK
	  : NS_ERROR_FAILURE);
}
  
// Returns the current classpath in use by the JVM.
nsresult
japharJVMPlugin::GetClassPath(const char* *result)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  HungryVM *hvm = henv->vm;

  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::GetClassPath(this=%p)\n", this));

  *result = hvm->_classpath;

  return NS_OK;
}

nsresult
japharJVMPlugin::GetJavaWrapper(JNIEnv* jenv,
				jint obj,
				jobject *jobj)
{
  PR_LOG (japharOJILm, PR_LOG_DEBUG, ("japharJVMPlugin::GetJavaWrapper(this=%p)\n", this));
  *jobj = NULL;
  return NS_ERROR_FAILURE;
}
  
/**
 * Gives time to the JVM from the main event loop of the browser. This is
 * necessary when there aren't any plugin instances around, but Java threads exist.
 */
nsresult
japharJVMPlugin::SpendTime(PRUint32 /*timeMillis*/)
{
  /* nothing, for now. */
  return NS_OK;
}

/**
 * This creates a new secure communication channel with Java. The second parameter,
 * nativeEnv, if non-NULL, will be the actual thread for Java communication.
 * Otherwise, a new thread should be created.
 * @param	proxyEnv	the env to be used by all clients on the browser side
 * @return	outSecureEnv	the secure environment used by the proxyEnv
 */
nsresult
japharJVMPlugin::CreateSecureEnv(JNIEnv* proxyJNI,
				 nsISecureEnv* *outSecureEnv)
{
  NS_DEFINE_IID(kISecureEnvIID, NS_ISECUREENV_IID);
  return japharSecureEnv::Create(this, proxyJNI, kISecureEnvIID, (void**)outSecureEnv);
}
