//=--------------------------------------------------------------------------=
// CNS4Adapter::NPP_Destroy
//=--------------------------------------------------------------------------=
// Deletes a specific instance of a plug-in and returns an error value. 
//
// The plugin instance peer and plugin instance are destroyed.
// The instance's pdata is set to NULL.
//
NPError 
CNS4Adapter::NPP_Destroy(NPP instance, NPSavedData** save)
{
    TRACE("CNS4Adapter::NPP_Destroy\n");
    
    if (instance == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;
    
    JDSmartPtr<IPluginInstance> spPluginInstance = (IPluginInstance* )instance->pdata;

    if (spPluginInstance == NULL)
	return NPERR_INVALID_PLUGIN_ERROR;

    // Stop and destroy the plug-in instance
    JDSmartPtr<IUniqueIdentifier> spInst;
    if(JD_SUCCEEDED(spPluginInstance->QueryInterface(jIUniqueIdentifierIID, (void**)&spInst))) 
    { 
	*save = (NPSavedData*)m_pINS4AdapterPeer->NPN_MemAlloc(sizeof(NPSavedData));
        (*save)->buf = NULL;
        spInst->GetUniqueId((long*)&((*save)->len));
    }

    // Stop and destroy the plug-in instance
    spPluginInstance->Stop();
    spPluginInstance->Destroy();
    spPluginInstance->Release();

    instance->pdata = NULL;
    
    return NPERR_NO_ERROR;
}
//=--------------------------------------------------------------------------=
// CNS4Adapter::NPP_New
//=--------------------------------------------------------------------------=
// Creates a new instance of a plug-in and returns an error value. 
// 
// A plugin instance peer and instance peer is created.  After
// a successful instansiation, the peer is stored in the plugin
// instance's pdata.
//
NPError 
CNS4Adapter::NPP_New(NPMIMEType pluginType,
		     NPP instance,
		     JDUint16 mode,
		     int16 argc,
		     char* argn[],
		     char* argv[],
		     NPSavedData* saved)
{
    TRACE("CNS4Adapter::NPP_New\n");
    
    if (instance == NULL)
        return NPERR_INVALID_INSTANCE_ERROR;

    instance->pdata = NULL;

    // Create a new plugin instance and start it.
    JDSmartPtr<IPluginInstance> spPluginInstance;
    m_pPlugin->CreateInstance(NULL, jIPluginInstanceIID, (void**)&spPluginInstance);

    if (spPluginInstance == NULL) 
        return NPERR_OUT_OF_MEMORY_ERROR;
    
    // Create a new plugin instance peer,
    // XXX - Since np_instance is not implemented in the 4.0x browser, I
    // XXX - had to save the plugin parameter in the peer class.
    // XXX - Ask Warren about np_instance.
    JDSmartPtr<IPluginInstancePeer>  spPeer = new CNS4Adapter_PluginInstancePeer(m_pINS4AdapterPeer, 
									         instance, 
									         (JDPluginMimeType)pluginType, 
									         (JDUint16)argc, (const char** )argn, 
									         (const char** )argv);

    if (spPeer == NULL) 
	return NPERR_OUT_OF_MEMORY_ERROR;
    
    JDSmartPtr<IUniqueIdentifier> spInst;
    if(JD_SUCCEEDED(spPluginInstance->QueryInterface(jIUniqueIdentifierIID, (void**)&spInst))) 
    { 
        long id = 0;
        if(NULL != saved) {
	    id = (long)saved->len;
	    m_pINS4AdapterPeer->NPN_MemFree(saved);
        }
        spInst->SetUniqueId(id);
    }
    // Init and startup plug-in instance
    spPluginInstance->Initialize(spPeer);
    spPluginInstance->Start();
    
    // Set the user instance and store the peer in npp->pdata.
    IPluginInstance* pPluginInstance = NULL;
    spPluginInstance.CopyTo(&pPluginInstance);
   
    instance->pdata = (void*)pPluginInstance;
    
    return NPERR_NO_ERROR;
}
//=--------------------------------------------------------------------------=
// CNSAdapter_JavaPlugin::GetJavaObject
//=--------------------------------------------------------------------------=
// params: jobject a JNI object represent the Java object
// notes :
//
NS_METHOD
CNSAdapter_JavaPlugin::GetJavaObject(jobject *result)
{
    TRACE("CNSAdapter_JavaPlugin::GetJavaObject\n");
    if (m_pJavaPlugin == NULL)
	return NS_ERROR_NULL_POINTER;

    JDSmartPtr<IJVMPluginInstance> spJVMPluginInstance;

    JDresult res = m_pJavaPlugin->QueryInterface(jIJVMPluginInstanceIID, (void**)&spJVMPluginInstance);

    if (JD_SUCCEEDED(res) && spJVMPluginInstance != NULL)
	return spJVMPluginInstance->GetJavaObject(result);
		
    return res;
}
jcharArray GetBrowserAuthInfo(RemoteJNIEnv* env, jobjectArray args, IBrowserAuthenticator* pBrowserAuthenticator) {
        jstring protocol = (jstring)env->GetObjectArrayElement(args, 0);
        jstring host = (jstring)env->GetObjectArrayElement(args, 1);
        jstring port = (jstring)env->GetObjectArrayElement(args, 2);
        jstring scheme = (jstring)env->GetObjectArrayElement(args, 3);
        jstring realm = (jstring)env->GetObjectArrayElement(args, 4);

        const char* lpszProtocol = env->GetStringUTFChars(protocol, (jboolean*)0);
        const char* lpszHost = env->GetStringUTFChars(host, (jboolean*)0);
        const char* lpszScheme = env->GetStringUTFChars(scheme, (jboolean*)0);
        const char* lpszRealm = env->GetStringUTFChars(realm, (jboolean*)0);
        const char* lpszPort = env->GetStringUTFChars(port, (jboolean*)0);

        jcharArray ret = NULL;
        trace("Call browser authenticationInfo(%s, %s, %s, %s, %s)\n", 
                lpszProtocol, lpszHost, lpszPort, lpszScheme, lpszRealm);
		char szUserName[MAX_PATH];
		char szPassword[MAX_PATH];

        if(JD_SUCCEEDED(pBrowserAuthenticator->GetAuthInfo(lpszProtocol, lpszHost, atoi(lpszPort),
                lpszScheme, lpszRealm, szUserName, sizeof(szUserName), szPassword, sizeof(szPassword)))) {
			int len = strlen(szUserName) + strlen(szPassword);
			if(0 != len) {
				char* lpszBuf = new char[len + 2];
				sprintf(lpszBuf, "%s:%s", szUserName, szPassword);
				trace("Browser return: %s\n", lpszBuf);
				jstring jstrBuf = env->NewStringUTF(lpszBuf);
				const jchar * jcharsBuf = env->GetStringChars(jstrBuf, (jboolean*)0);
				const jsize unicode_len = env->GetStringLength(jstrBuf);
				ret = env->NewCharArray(unicode_len);
				env->SetCharArrayRegion(ret, 0, unicode_len, (jchar *)jcharsBuf);

				env->ReleaseStringChars(jstrBuf, jcharsBuf);
				delete[] lpszBuf;                   
            }
        }

        env->ReleaseStringUTFChars(protocol, lpszProtocol);
        env->ReleaseStringUTFChars(host, lpszHost);
        env->ReleaseStringUTFChars(scheme, lpszScheme);
        env->ReleaseStringUTFChars(realm, lpszRealm);
        env->ReleaseStringUTFChars(port, lpszPort);

        return ret;

}
//=--------------------------------------------------------------------------=
// CNS4Adapter::NPP_Initialize
//=--------------------------------------------------------------------------=
// Provides global initialization for a plug-in, and returns an error value. 
//
// This function is called once when a plug-in is loaded, before the first instance
// is created. m_pPluginManager and m_pPlugin are both initialized.
//
//
NPError
CNS4Adapter::NPP_Initialize(void)
{
    TRACE("CNS4Adapter::NPP_Initialize\n");

    ASSERT(m_pINS4AdapterPeer != NULL);

    // Only call initialize the plugin if it hasn't been created.
    // This could happen if GetJavaClass() is called before
    // NPP Initialize.  
    if (m_pPluginManager == NULL) 
    {
        // Create the plugin manager and plugin classes.
        m_pPluginManager = new CNS4Adapter_PluginManager(m_pINS4AdapterPeer);	
     
        if (m_pPluginManager == NULL) 
            return NPERR_OUT_OF_MEMORY_ERROR;  

        m_pPluginManager->AddRef();
    }

    JDresult error = JD_OK;  
    
    // On UNIX the plugin might have been created when calling NPP_GetMIMEType.
    if (m_pPlugin == NULL) 
    {
        // create IPlugin factory from ns4 adapter
	static NSGetFactoryProc pProc = NULL;

	if (pProc == NULL)
	{
	    // Load jpins32.dll
            HINSTANCE hDLL = NULL;
	    if (LoadPluginLibrary(JPINS_SERVER_NAME, &hDLL) != ERROR_SUCCESS)
		return (NPError)JD_ERROR_FAILURE;

		// Get the proc address of NSGetFactory(IPluginServiceProvider*, IFactory**);	
		pProc = (NSGetFactoryProc)
			GetProcAddress(hDLL, NSGetFactory_NAME );
                if (pProc == NULL)
                    return NPERR_GENERIC_ERROR;
	}

	JDSmartPtr<IFactory> spIFactory;
	JDSmartPtr<IPluginServiceProvider> spPluginServiceProvider(new CPluginServiceProvider(m_pPluginManager));
    
	if (spPluginServiceProvider == NULL)
	    return (NPError)JD_ERROR_OUT_OF_MEMORY;

	// Calling NSGetFactory to get a Factory object
	error = pProc(spPluginServiceProvider, &spIFactory);
    
	if (JD_SUCCEEDED(error) && spIFactory)
	{
	    // Query IPlugin
	    error = spIFactory->QueryInterface(jIPluginIID, (void**)&m_pPlugin);
	    if (JD_SUCCEEDED(error) && m_pPlugin)
		m_pPlugin->Initialize();
	}

    }
    return (NPError) error;	
}
/* Handle the next message from Java to JS */
void
JSHandler(RemoteJNIEnv* env) {
	/* 2 cases 
	   - if this is recursive then we have sole ownership of the
	   pipe. 
	   - If this is spontaneous, we should also have sole ownership of
	   the pipe since it is locked at the other end 
	   - Hence, there should be no need for locking here
	*/
	/* Get the plugin index */
	int pluginIndex;
	get_msg(env, (char*)&pluginIndex, 4);
	JavaPluginFactory5* plugin_factory = get_global_factory();
	JavaPluginInstance5* inst = plugin_factory->GetInstance(pluginIndex);
	int code;
	get_msg(env, (char*)&code, 4);
  
	/*
	 * when  JSObject is GCed after plugin is destroyed, inst is NULL 
	 * and code is JAVA_PLUGIN_JNIJS_FINALIZE. In this case, we still
	 * need consume message from spontaneous pipe and ask browser to 
	 * release native JSObject,  or spontaneous pipe will be corrupted
	 * and resource leak
	 */
	if (code != JAVA_PLUGIN_JNIJS_FINALIZE) {

		if (inst == NULL || inst->IsDestroyPending()) {

			// Consume the message
			int raw_msg_len;
			char* raw_msg;
			get_msg(env, &raw_msg_len, 4);
			raw_msg = (char *) checked_malloc(raw_msg_len);
			/* Swallow the whole message */
			get_msg(env, raw_msg, raw_msg_len);
			int replyID = 0;
			memcpy(&replyID, raw_msg, 4);
			free(raw_msg);
			jobject nullret = NULL;
			send_jnijsOK_res(env, replyID, &nullret, sizeof(jobject));
			return;
		}
	}
  
	JSMessage msg;
	trace("JSObject:Entering JSHandler()\n");

	UnpackJSMessage(env, &msg);

	// Create ProxyJNIEnv
	JNIEnv* pProxyJNIEnv = NULL;
	IJVMManager* jvm_manager;
	jvm_manager = plugin_factory->GetJVMManager();
    
	ILiveconnect* pLiveConnect = NULL;
	ISecurityContext* pContext = NULL;
    
	if (JD_SUCCEEDED(jvm_manager->CreateProxyJNI(NULL, &pProxyJNIEnv)) ) {
		trace("JSHandler(): JS command: %X %s\n", code, jscode_to_str(code));
	
		if (inst == NULL) {
			IPluginServiceProvider* pProvider = plugin_factory->GetServiceProvider();

			if (pProvider == NULL) {
				trace("JSHandler(): cannot get pProvider when inst is NULL\n");
				return;
			}

			if (JD_FAILED(pProvider->QueryService(jCLiveconnectCID,
					jILiveconnectIID, (ISupports**)&pLiveConnect))) {
				trace("JSHandler(): cannot get liveconnect when inst is NULL\n");
				return;
			}

		} else {
	
			if (JD_FAILED(inst->GetJSDispatcher(&pLiveConnect))) {
				return;
			}
		}

		if (msg.utfstr != NULL)
			CreateSecurityContext(msg.utfstr, (int) msg.ctx, &pContext);

		int replyID = msg.requestID;

		switch(code){
		case JAVA_PLUGIN_JNIJS_GET_NATIVE:
			{
				// CLiveconnect	  
				jsobject ret = 0;

				/* Get the JS object, which represents a window associated
				   with the given plugin instance */

				JDresult nr = pLiveConnect->GetWindow(pProxyJNIEnv, 
													  (IPluginInstance*) inst, 
													  (void **)NULL, 
													  0, 
													  (ISecurityContext*) pContext, 
													  &ret);
				if(JD_FAILED(nr) || ret == 0) {
					trace("JSObject::ILiveConnect::GetWindow FAILED\n");
				}
  
				send_jnijsOK_res(env, replyID, &ret, sizeof(jsobject));
				break;
			}
		case JAVA_PLUGIN_JNIJS_TOSTRING:
			{
				jstring ret = NULL;
	  
				JDresult nr = pLiveConnect->ToString(pProxyJNIEnv,
													 (jsobject) msg.nativeJSObject,
													 &ret);
				if(!JD_SUCCEEDED(nr)) {
					trace("JSObject::ILiveConnect::ToString FAILED\n");
				}
	  	  	
				send_jnijsOK_res(env, replyID, &ret, sizeof(jstring));
				break;
			}
		case JAVA_PLUGIN_JNIJS_FINALIZE:
			{		 
				jobject dummy = NULL;
				JDresult nr = pLiveConnect->FinalizeJSObject(pProxyJNIEnv, 
															 (jsobject) msg.nativeJSObject);
				if(!JD_SUCCEEDED(nr)) {
					trace("JSObject::ILiveConnect::FinalizeJSObject() FAILED\n");
				}
		
				send_jnijsOK_res(env, replyID, &dummy, sizeof(jobject));
				break;
			}
		case JAVA_PLUGIN_GET_BROWSER_AUTHINFO: 
			{
				IBrowserAuthenticator* pBrowserAuthenticator;
				jobject ret = NULL;
				if (inst != NULL) {
					IPluginServiceProvider* service_provider =  plugin_factory->GetServiceProvider();

					trace("Handle native call: GetBrowserAuthenticat()");
					if(service_provider != NULL && 
					   JD_SUCCEEDED(service_provider->QueryService(jBrowserAuthenticatorCID, 
																   jBrowserAuthenticatorIID,
																   (ISupports**)&pBrowserAuthenticator))) {                       
						trace("Interface IBrowserAuthenticator found");
						ret = GetBrowserAuthInfo(env, msg.jarr, pBrowserAuthenticator);
						service_provider->ReleaseService(jBrowserAuthenticatorCID, pBrowserAuthenticator);
					} else {
						trace("Interface IBrowserAuthenticator not found");
					}
				}

				send_jnijsOK_res(env, replyID, &ret, sizeof(jobject));
				break;
			}
		case JAVA_PLUGIN_JNIJS_CALL:
			{
				jobject ret = NULL;
				JDresult nr = pLiveConnect->Call(pProxyJNIEnv, 
												 (jsobject)msg.nativeJSObject, 
												 msg.charstr, msg.charstr_len, 
												 msg.jarr,
												 (void**)NULL, 0, 
												 (ISecurityContext*)pContext, &ret);
				if(!JD_SUCCEEDED(nr)) {
					trace("JSObject::ILiveConnect::Call() FAILED\n");
				}
			
				send_jnijsOK_res(env, replyID, &ret, sizeof(jobject));
				break;
			}
		case JAVA_PLUGIN_JNIJS_EVAL:
			{
				jobject ret = NULL;
				JDresult nr = pLiveConnect->Eval(pProxyJNIEnv, 
												 (jsobject)msg.nativeJSObject,
												 msg.charstr, msg.charstr_len,
												 (void**)NULL, 0,
												 (ISecurityContext*)pContext, &ret);
				if(!JD_SUCCEEDED(nr)) {
					trace("JSObject::ILiveConnect::Eval() FAILED\n");
				}
		
				send_jnijsOK_res(env, replyID, &ret, sizeof(jobject));
				break;
			}
		case JAVA_PLUGIN_JNIJS_GETMEMBER:
			{
				jobject ret = NULL;
		 
				JDresult nr = pLiveConnect->GetMember(pProxyJNIEnv,
													  (jsobject)msg.nativeJSObject,
													  msg.charstr, msg.charstr_len,
													  (void**)NULL, 0,
													  (ISecurityContext*)pContext, &ret);
				if(!JD_SUCCEEDED(nr)) {
					trace("JSObject::ILiveConnect::GetMember() FAILED\n");
				}
		
				send_jnijsOK_res(env, replyID, &ret, sizeof(jobject));
				break;
			}
		case JAVA_PLUGIN_JNIJS_SETMEMBER:
			{
				jobject dummy = NULL;
	 
				JDresult nr = pLiveConnect->SetMember(pProxyJNIEnv,
													  (jsobject)msg.nativeJSObject,
													  msg.charstr, msg.charstr_len, 
													  msg.value,
													  (void**)NULL, 0,
													  (ISecurityContext*)pContext);
				if(!JD_SUCCEEDED(nr)) {
					trace("JSObject::ILiveConnect::SetMember() FAILED\n");
				}
	  
				send_jnijsOK_res(env, replyID, &dummy, sizeof(jobject));
				break;
			}
		case JAVA_PLUGIN_JNIJS_REMOVEMEMBER:
			{
				jobject dummy = NULL;
		 
				JDresult nr = pLiveConnect->RemoveMember(pProxyJNIEnv, 
														 (jsobject)msg.nativeJSObject, 
														 msg.charstr, msg.charstr_len, 
														 (void**)NULL, 0, 
														 (ISecurityContext*)pContext);
				if(!JD_SUCCEEDED(nr)) {
					trace("JSObject::ILiveConnect::RemoveMember() FAILED\n");
				}
		  
				send_jnijsOK_res(env, replyID, &dummy, sizeof(jobject));
				break;
			}
		case JAVA_PLUGIN_JNIJS_GETSLOT:
			{
				jobject ret = NULL;
	 
				JDresult nr = pLiveConnect->GetSlot(pProxyJNIEnv, 
													(jsobject)msg.nativeJSObject, 
													msg.slotindex,
													(void**)NULL, 0, 
													(ISecurityContext*)pContext, &ret);
				if(!JD_SUCCEEDED(nr)) {
					trace("JSObject::ILiveConnect::GetSlot() FAILED");
				}
				send_jnijsOK_res(env, replyID, &ret, sizeof(jobject));
				break;
			}
		case JAVA_PLUGIN_JNIJS_SETSLOT:
			{
				jobject dummy = NULL;
	  
				JDresult nr = pLiveConnect->SetSlot(pProxyJNIEnv, 
													(jsobject)msg.nativeJSObject, 
													msg.slotindex, 
													msg.value,
													(void**)NULL, 0, 
													(ISecurityContext*)pContext);
				if(!JD_SUCCEEDED(nr)) {
					trace("JSObject::ILiveConnect::SetSlot() FAILED\n");
				}
				send_jnijsOK_res(env, replyID, &dummy, sizeof(jobject));
				break;
			}
		default:
			plugin_error("Error in handler for JS calls!\n");
	
			if (pContext != NULL)
				pContext->Release();
			/* End of the handler for JS method calls */
		}
	}
	else {
		trace("Can not get ProxyJNI\n");
	}
	if(pLiveConnect != NULL)
		pLiveConnect->Release();

	if (pContext)
		pContext->Release();

	FreeJSMessage(&msg);
}