/* Swfdec Mozilla Plugin
 * Copyright (C) 2003-2006 David Schleef <ds@schleef.org>
 *		 2005-2006 Eric Anholt <eric@anholt.net>
 *		 2006-2008 Benjamin Otte <otte@gnome.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
 * Boston, MA  02110-1301  USA
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <errno.h>

#include <npapi.h>
#include <npupp.h>

#include "plugin.h"
#include "plugin_fb.h"
#include "fltktool.h"

/* This is here so we can quickly disable windowless support. For now it's 
 * missing some features (cursor support) and redraws seem to be buggy */
//#define DISABLE_WINDOWLESS
#define DISABLE_WINDOWLESS

NPNetscapeFuncs mozilla_funcs;

/*** forward declarations for plugin API ***/

void
plugin_get_url (NPP instance, const char *url, const char *target)
{
  CallNPN_GetURLProc (mozilla_funcs.geturl, instance, url, target);
}

void
plugin_get_url_notify (NPP instance, const char *url,
    const char *target, void *data)
{
  CallNPN_GetURLNotifyProc (mozilla_funcs.geturlnotify, instance, 
      url, target, data);
}

void
plugin_post_url (NPP instance, const char *url,
    const char *target, const char *data, unsigned int data_len)
{
  CallNPN_PostURLProc (mozilla_funcs.posturl, instance, 
      url, target, data_len, data ? data : "", FALSE);
}

void
plugin_post_url_notify (NPP instance, const char *url,
    const char *target, const char *data, unsigned int data_len, void *user_data)
{
  CallNPN_PostURLNotifyProc (mozilla_funcs.posturlnotify, instance, 
      url, target, data_len, data ? data : "", FALSE, user_data);
}

void
plugin_destroy_stream (NPP instance, NPStream *stream)
{
  CallNPN_DestroyStreamProc (mozilla_funcs.destroystream, instance, stream, NPRES_DONE);
}

void
plugin_invalidate_rect (NPP instance, NPRect *rect)
{
  CallNPN_InvalidateRectProc (mozilla_funcs.invalidaterect, instance, rect);
}

bool
plugin_get_value (NPP instance, NPNVariable var, void * data)
{
  return CallNPN_GetValueProc(mozilla_funcs.getvalue, instance, var, data) == NPERR_NO_ERROR;
}

int plugin_evaluate(NPP npp, NPObject* obj, NPString *script,
                  NPVariant *result)
{
  return mozilla_funcs.evaluate(npp, obj, script, result);
}

NPIdentifier plugin_get_string_identifier(const NPUTF8 *name)
{
  return mozilla_funcs.getstringidentifier(name);
}

/*** scriptable object ***/

static NPP g_Instance;
static NPObject * g_ScriptObject=0;

static
void Scriptable_Deallocate(NPObject *npobj)
{
	// Call the virtual destructor.
	free(npobj);
	if(g_ScriptObject==npobj)
	  g_ScriptObject=0;
}

static
void Scriptable_Invalidate(NPObject *npobj)
{
	
}

void plugin_focus(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);
void plugin_blur(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);

static
bool Scriptable_HasMethod(NPObject *npobj, NPIdentifier name)
{
  printf("------------Scriptable_HasMethod\n");
  NPUTF8 *utf8 = mozilla_funcs.utf8fromidentifier(name);
  if (utf8)
  {
	printf("----has func %s\n", utf8);
	if (strcmp(utf8, "focus") == 0)
	{
    	return true;
	}
	if (strcmp(utf8, "blur") == 0)
	{
    	return true;
	}
  }
  return plugin_fb_has_method(g_Instance->pdata, name);
}

static
bool Scriptable_Invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
  printf("------------Scriptable_Invoke\n");
  NPUTF8 *utf8 = mozilla_funcs.utf8fromidentifier(name);
  if (utf8)
  {
	printf("----call func %s\n", utf8);
	if (strcmp(utf8, "focus") == 0)
	{
    	plugin_focus(name, args, argCount, result);
    	return true;
	}
	if (strcmp(utf8, "blur") == 0)
	{
    	plugin_blur(name, args, argCount, result);
    	return true;
	}
  }
  return plugin_fb_invoke(g_Instance->pdata, utf8, args, argCount, result);
}

static
bool Scriptable_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
  printf("------------Scriptable_InvokeDefault\n");
  return false;
}

static
bool Scriptable_HasProperty(NPObject * npobj, NPIdentifier name)
{
  printf("------------Scriptable_HasProperty\n");
  return false;
}

static
bool Scriptable_GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result)
{
  printf("------------Scriptable_GetProperty\n");
  return false;
}

static
bool Scriptable_SetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value)
{
  printf("------------Scriptable_SetProperty\n");
  return false;
}

static
bool Scriptable_RemoveProperty(NPObject *npobj, NPIdentifier name)
{
  printf("------------Scriptable_RemoveProperty\n");
  return false;
}

static NPObject* AllocateScriptablePluginObject(NPP npp, NPClass *aClass)
{
  printf("------------AllocateScriptablePluginObject\n");
  return malloc(sizeof(NPObject));
}

static NPClass plugin_NPClass = {
	NP_CLASS_STRUCT_VERSION,
	AllocateScriptablePluginObject,
	Scriptable_Deallocate,
	Scriptable_Invalidate,
	Scriptable_HasMethod,
	Scriptable_Invoke,
	Scriptable_InvokeDefault,
	Scriptable_HasProperty,
	Scriptable_GetProperty,
	Scriptable_SetProperty,
	Scriptable_RemoveProperty
};


/*** plugin implementation ***/

char *
NP_GetMIMEDescription (void)
{
  return (char *) "application/x-shockwave-flash:swf:Adobe Flash movie;application/futuresplash:spl:FutureSplash movie";
}

NPError 
NP_GetValue (void* reserved, NPPVariable var, void* out)
{
  char **val;

  if (out == NULL)
    return NPERR_INVALID_PARAM;
	          
  val = (char**)out;
		      
  switch (var) {
    case NPPVpluginNameString:
      *val = (char *) "Shockwave Flash";
      break;
    case NPPVpluginDescriptionString:
      /* FIXME: find a way to encode the Swfdec version without breaking stupid JS scripts */
      *val = (char *) "Shockwave Flash 9.0 r999";
      break;
    case NPPVpluginNeedsXEmbed:
      *((PRBool*) val) = PR_FALSE;
      break;
    case NPPVpluginWindowBool:
    case NPPVpluginTransparentBool:
    case NPPVjavaClass:
    case NPPVpluginWindowSize:
    case NPPVpluginTimerInterval:
      break;
    case NPPVpluginScriptableInstance:
      {
        if(!g_ScriptObject)
          g_ScriptObject=mozilla_funcs.createobject(g_Instance, &plugin_NPClass);
          
        if(g_ScriptObject)
          mozilla_funcs.retainobject(g_ScriptObject);
         
         printf("---------------NPPVpluginScriptableInstance %p %p\n",g_ScriptObject,g_Instance);
         *(NPObject **)val =g_ScriptObject;
      }
      break;
    case NPPVpluginScriptableIID:
    case NPPVjavascriptPushCallerBool:
    case NPPVpluginKeepLibraryInMemory:
      break;
    case NPPVpluginScriptableNPObject:
      {
        if(!g_ScriptObject)
          g_ScriptObject=mozilla_funcs.createobject(g_Instance, &plugin_NPClass);
        if(g_ScriptObject)
          mozilla_funcs.retainobject(g_ScriptObject);
         printf("---------------NPPVpluginScriptableNPObject %p %p\n",g_ScriptObject,g_Instance);
         *(NPObject **)val =g_ScriptObject;
      }
      break;
    case NPPVformValue:
    default:
      return NPERR_INVALID_PARAM;
      break;
  }
  return NPERR_NO_ERROR;
}

static void do_callback(int fd, void * data)
{
  NPP instance = (NPP)data;
  PLUGIN_PDATA *pdata = (PLUGIN_PDATA *)instance->pdata;
  fprintf(stderr, "%s %d: %s", __FUNCTION__, __LINE__, pdata->buffer);
  int linelen = strlen(pdata->buffer);
  if (linelen < 4)
	return;
  linelen--;
  pdata->buffer[linelen] = '\0';
  if(strncmp (pdata->buffer, "flash:init_ok", 13) == 0)
  {
    NPN_SetValue(instance, 10000, (void *)1);
    return;
  }
  else if(strncmp (pdata->buffer, "javascript_cb:", 14) == 0)
  {
	int i = 0;
	printf("name = %s\n", &pdata->buffer[14]);
	NPIdentifier name = plugin_get_string_identifier(&pdata->buffer[14]);
	for (i = 0; i < pdata->invoke_cnt; i++)
	{
	  if (name == pdata->invoke_name[i])
		break;	
	}
	if (i == pdata->invoke_cnt)
	{
	  pdata->invoke_cnt++;
	  pdata->invoke_name = realloc(pdata->invoke_name, sizeof(NPIdentifier) * pdata->invoke_cnt);
	  pdata->invoke_name[pdata->invoke_cnt - 1] = name;
	}
	//ToDo: Return the right value
	send(pdata->exterretfd,"ret",3,0);
  }
  else if(strncmp (pdata->buffer, "javascript", 10) == 0)
  {
	NPString script1;
	script1.utf8length = linelen;
	script1.utf8characters = strdup(pdata->buffer);
	NPVariant result;
	plugin_evaluate(instance, pdata->windowObj, &script1, &result);
	if(result.type==NPVariantType_String)
	{
	}
	else if(result.type==NPVariantType_Void)
	{
	}
	mozilla_funcs.releasevariantvalue(&result);
	//ToDo: Return the right value
	send(pdata->exterretfd,"ret",3,0);
  }
}

static void callback(int fd, void * data)
{
  NPP instance = (NPP)data;
  PLUGIN_PDATA *pdata = (PLUGIN_PDATA *)instance->pdata;
  if (pdata->fstream == NULL)
	return;
  while (1)
  {
  	memset(pdata->buffer, 0, sizeof(pdata->buffer));
  	if (fgets(pdata->buffer, sizeof(pdata->buffer), pdata->fstream) == NULL)
	  break;
  	do_callback(fd, data);
  }
}

static NPError
plugin_new (NPMIMEType mime_type, NPP instance,
    uint16_t mode, int16_t argc, char *argn[], char *argv[],
    NPSavedData * saved)
{
  int i, ret = -1;
  int p_stream[2], p_event[2], p_evret[2], p_external[2], p_exterret[2];
  PLUGIN_PDATA *pdata = NULL;
  NPObject *windowObj = NULL;
  const char *path = NULL;
  char p[128] = "/root/net_app/owb";
  int maxargc = 0, actargc = 0;
  const char **maxargv = NULL;
  char *buf = NULL;
  char fd_buf[5][16];
  int numfailed = 0, closed = 0;
  int anfd = fileno(stderr)+1;

  printf("plugin_new\n");

  NPN_SetValue(instance, 10000, (void *)0);
 
  if (instance == NULL)
    return NPERR_INVALID_INSTANCE_ERROR;
  
  path = getenv("VA_RUNTIME_ROOT");
  if (path != NULL && strlen(path) < sizeof(p) - 20)
  {
	strcpy(p, path);			
  }
  strcat(p, "/bin/fbplay");
  path = p;

  if (getenv("SWFDEC_PLAYER") != NULL)
  {
 	path = getenv("SWFDEC_PLAYER");
  }

  instance->pdata = pdata = (PLUGIN_PDATA *)malloc(sizeof(PLUGIN_PDATA));
  if (instance->pdata == NULL)
	return NPERR_INVALID_INSTANCE_ERROR;
  pdata->window = NULL;
  pdata->windowObj = NULL;
  pdata->invoke_cnt = 0;
  pdata->invoke_name = NULL;
  pdata->childpid = -1;
  pdata->streamfd = -1;
  pdata->eventfd = -1;
  pdata->evretfd = -1;
  pdata->externalfd = -1;
  pdata->exterretfd = -1;
  pdata->fstream = NULL;

  ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, p_stream);
  if (ret < 0)
  {
	return NPERR_INVALID_INSTANCE_ERROR;
  }
  pdata->streamfd = p_stream[1];
  
  ret = socketpair(AF_LOCAL, SOCK_DGRAM, 0, p_event);
  if (ret < 0)
  {
	return NPERR_INVALID_INSTANCE_ERROR;
  }
  pdata->eventfd = p_event[1];

  ret = socketpair(AF_LOCAL, SOCK_DGRAM, 0, p_evret);
  if (ret < 0)
  {
	return NPERR_INVALID_INSTANCE_ERROR;
  }
  pdata->evretfd = p_evret[0];

  ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, p_external);
  if (ret < 0)
  {
	return NPERR_INVALID_INSTANCE_ERROR;
  }
  pdata->externalfd = p_external[0];

  ret = socketpair(AF_LOCAL, SOCK_DGRAM, 0, p_exterret);
  if (ret < 0)
  {
	return NPERR_INVALID_INSTANCE_ERROR;
  }
  pdata->exterretfd = p_exterret[1];

  maxargc = 1 + 10 + argc * 2 + 1;
  maxargv = (const char **)malloc(sizeof(const char *)*maxargc);
  actargc = 0;
  maxargv[actargc++] = path;
  maxargv[actargc++] = "--stream";
  sprintf(fd_buf[0], "%d", p_stream[0]);
  maxargv[actargc++] = fd_buf[0];
  maxargv[actargc++] = "--event";
  sprintf(fd_buf[1], "%d", p_event[0]);
  maxargv[actargc++] = fd_buf[1];
  maxargv[actargc++] = "--evret";
  sprintf(fd_buf[2], "%d", p_evret[1]);
  maxargv[actargc++] = fd_buf[2];
  maxargv[actargc++] = "--external";
  sprintf(fd_buf[3], "%d", p_external[1]);
  maxargv[actargc++] = fd_buf[3];
  maxargv[actargc++] = "--exterret";
  sprintf(fd_buf[4], "%d", p_exterret[0]);
  maxargv[actargc++] = fd_buf[4];

  /* set the properties we support */
  /* FIXME: figure out how variables override each other */
  for (i = 0; i < argc; i++) {
    if (argn[i] == NULL)
      continue;
	/* print out */
	printf("arg[%d] = [%s, %s]\n", i, argn[i], argv[i]);
	/* key/val */
  	maxargv[actargc++] = "-P";
  	buf = (char *)malloc(strlen(argn[i])+1+strlen(argv[i])+1);
	sprintf(buf, "%s=%s", argn[i], argv[i]);
	maxargv[actargc++] = (const char *)buf;
  }
  maxargv[actargc++] = 0;

  pdata->childpid = fork();
  if (pdata->childpid == -1)
  {
    printf("ERROR: fork() failed: %s\n", strerror(errno));
    return NPERR_INVALID_INSTANCE_ERROR;
  }
 
  // parent
  if (pdata->childpid > 0)
  {
	int flags;
	if (buf != NULL)
	  free(buf);
	if (maxargv != NULL)
	  free(maxargv);
	ret = close(p_stream[0]);
	ret = close(p_event[0]);
	ret = close(p_evret[1]);
	ret = close(p_external[1]);
	ret = close(p_exterret[0]);
	flags = fcntl(pdata->externalfd, F_GETFL, 0);
	flags |= O_NONBLOCK;
	fcntl(pdata->externalfd, F_SETFL, flags);
  	fltk_add_fd(pdata->externalfd, POLLIN, callback, instance);
	pdata->fstream = fdopen(pdata->externalfd, "r");
  	plugin_get_value(instance, NPNVWindowNPObject, &windowObj);
  	pdata->windowObj = windowObj;
  	g_Instance = instance;
  	return NPERR_NO_ERROR;
  }
  // child
  ret = close(p_stream[1]);
  ret = close(p_event[1]);
  ret = close(p_evret[0]);
  ret = close(p_external[0]);
  ret = close(p_exterret[1]);
  for ( ; numfailed < 10; anfd++)
  {
    if ( anfd == p_stream[0] ) continue;
    if ( anfd == p_event[0] ) continue;
    if ( anfd == p_evret[1] ) continue;
    if ( anfd == p_external[1] ) continue;
    if ( anfd == p_exterret[0] ) continue;
    ret = close (anfd);
    if (ret < 0) 
		numfailed++;
    else
    {
        numfailed = 0;
        closed++;
    }
  }
  printf("Closed %d files.\n", closed);

  printf("Starting process: ");
  for (i = 0; maxargv[i] != 0; ++i)
  {
    printf("%s ", maxargv[i]);
  }
  printf("\n");
 
  execv(maxargv[0], (char**)(maxargv));

  // if execv returns, an error has occurred.
  perror("executing standalone swfdec");

  exit(-1);
}

void terminate_child(int signo)
{
  PLUGIN_PDATA *pdata = NULL;
  printf("terminate_child, received, signo = %d\n", signo);
  if (g_Instance == NULL || g_Instance->pdata == NULL)
    return ;
  pdata = (PLUGIN_PDATA *)g_Instance->pdata;
  pthread_cond_signal(&pdata->cond);
}

void wait_child_exit(PLUGIN_PDATA *pdata)
{
  int status;
  int ret = 0, i = 0;
  int signo[3] = {SIGUSR1, SIGUSR2, SIGKILL};
#if 0
  int wtime[3] = {30, 10, 5};
#else
  int wtime[3] = {10, 3, 1};
#endif
  struct timeval now;
  struct timespec timeout;
  plugin_fb_exit(pdata);
  for (i = 0; i < 3; i++)
  {
    kill(pdata->childpid, signo[i]);
    gettimeofday(&now, NULL);
    timeout.tv_sec = now.tv_sec + wtime[i];
    timeout.tv_nsec =  now.tv_usec * 1000UL;
    ret = pthread_cond_timedwait(&pdata->cond, &pdata->mutex, &timeout);
	if (ret != ETIMEDOUT)
	  break;
  }
  waitpid(pdata->childpid, &status, 0);
  printf("Child process exited with status %d.\n", status);
}

static NPError 
plugin_destroy (NPP instance, NPSavedData **save)
{
  printf("plugin_destory\n");
 
  NPN_SetValue(instance, 10000, (void *)1);

  if (instance == NULL || instance->pdata == NULL)
    return NPERR_INVALID_INSTANCE_ERROR;

  PLUGIN_PDATA *pdata = (PLUGIN_PDATA *)instance->pdata;
  if (pdata->externalfd != -1)
  	fltk_remove_fd(pdata->externalfd, POLLIN);
  plugin_fb_teardown (pdata);
  if (pdata->childpid > 0)
  {
	sighandler_t old;
	pthread_mutex_init(&pdata->mutex, NULL);
	pthread_cond_init(&pdata->cond, NULL);
	old = signal(SIGCHLD, terminate_child);
	wait_child_exit(pdata);
	signal(SIGCHLD, old);
	pthread_mutex_destroy(&pdata->mutex);
	pthread_cond_destroy(&pdata->cond);
  }
  if (pdata->invoke_name != NULL)
  {
	free(pdata->invoke_name);
  }
  if (pdata->streamfd != -1)
  {
 	close(pdata->streamfd);
  }
  if (pdata->eventfd != -1)
  {
 	close(pdata->eventfd);
  }
  if (pdata->evretfd != -1)
  {
 	close(pdata->eventfd);
  }
  if (pdata->externalfd != -1)
  {
 	close(pdata->externalfd);
  }
  if (pdata->exterretfd != -1)
  {
 	close(pdata->exterretfd);
  }
  free(instance->pdata);
  instance->pdata = NULL;
  return NPERR_NO_ERROR;
}

static NPError 
plugin_new_stream (NPP instance, NPMIMEType type, NPStream* stream, 
    NPBool seekable, uint16* stype)
{
  if (instance == NULL || instance->pdata == NULL)
    return NPERR_INVALID_INSTANCE_ERROR;
  printf("new stream:\n");
  plugin_fb_seturl(instance->pdata, (char *)stream->url);
  if (stype)
    *stype = NP_ASFILE;
  return NPERR_NO_ERROR;
}

static NPError 
plugin_destroy_stream_cb (NPP instance, NPStream* stream, NPReason reason)
{
  if (instance == NULL || instance->pdata == NULL)
    return NPERR_INVALID_INSTANCE_ERROR;
  printf("destroy stream:\n");
  return NPERR_NO_ERROR;
}

static int32
plugin_write_ready (NPP instance, NPStream* stream)
{
  if (instance == NULL || instance->pdata == NULL)
    return -1;
  return 0x0fffffff;
}

static int32
plugin_write (NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
{
  if (instance == NULL || instance->pdata == NULL)
    return -1;
  printf("plugin stream: size = %d\n", len);
  return len;
}

static void
plugin_stream_as_file (NPP instance, NPStream *stream, const char *filename)
{
  if (instance == NULL || instance->pdata == NULL)
    return;
}

static NPError 
plugin_set_window (NPP instance, NPWindow *window)
{
  if (instance == NULL || instance->pdata == NULL)
    return NPERR_INVALID_INSTANCE_ERROR;

  if (window) {
    plugin_fb_setup_windowed (instance->pdata, window->window, 
		window->x, window->y, window->width, window->height);
  } else {
    plugin_fb_teardown (instance->pdata);
  }
  return NPERR_NO_ERROR;
}

static int16
plugin_handle_event (NPP instance, void *eventp)
{
  if (instance == NULL || instance->pdata == NULL)
    return FALSE;

  plugin_fb_handle_event (instance->pdata, eventp);

  return TRUE;
}

void plugin_focus(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
  if (g_Instance)
  {
  	fltk_focus(((PLUGIN_PDATA *)g_Instance->pdata)->window);
	plugin_fb_focus(g_Instance->pdata);
  }
}

void plugin_blur(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
  if (g_Instance)
  {
  	fltk_unfocus(((PLUGIN_PDATA *)g_Instance->pdata)->window);
	plugin_fb_blur(g_Instance->pdata);
  }
}

static void
plugin_url_notify (NPP instance, const char* url, NPReason reason, void* notifyData)
{
  // plugin_post_url_notify is used for launch so headers can be sent, but we
  // don't actually care about the notify
  if (notifyData == NULL)
    return;

  if (reason == NPRES_NETWORK_ERR) {
    printf("Network error\n");
  } else if (reason == NPRES_USER_BREAK) {
    printf("User interrupt\n");
  }
}

NPError
NP_Initialize (NPNetscapeFuncs * moz_funcs, NPPluginFuncs * plugin_funcs)
{
  if (moz_funcs == NULL || plugin_funcs == NULL)
    return NPERR_INVALID_FUNCTABLE_ERROR;

  if ((moz_funcs->version >> 8) > NP_VERSION_MAJOR)
    return NPERR_INCOMPATIBLE_VERSION_ERROR;
  if (moz_funcs->size < sizeof (NPNetscapeFuncs))
    return NPERR_INVALID_FUNCTABLE_ERROR;
  if (plugin_funcs->size < sizeof (NPPluginFuncs))
    return NPERR_INVALID_FUNCTABLE_ERROR;

  mozilla_funcs = *moz_funcs;

  memset (plugin_funcs, 0, sizeof (NPPluginFuncs));
  plugin_funcs->size = sizeof (NPPluginFuncs);
  plugin_funcs->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
  plugin_funcs->newp = NewNPP_NewProc (plugin_new);
  plugin_funcs->destroy = NewNPP_DestroyProc (plugin_destroy);

  plugin_funcs->newstream = NewNPP_NewStreamProc (plugin_new_stream);
  plugin_funcs->destroystream =
      NewNPP_DestroyStreamProc (plugin_destroy_stream_cb);
  plugin_funcs->writeready = NewNPP_WriteReadyProc (plugin_write_ready);
  plugin_funcs->write = NewNPP_WriteProc (plugin_write);
  plugin_funcs->asfile = NewNPP_StreamAsFileProc (plugin_stream_as_file);
  plugin_funcs->setwindow = NewNPP_SetWindowProc (plugin_set_window);
  plugin_funcs->event = NewNPP_HandleEventProc (plugin_handle_event);
  plugin_funcs->urlnotify = NewNPP_URLNotifyProc (plugin_url_notify);
  plugin_funcs->getvalue   = NewNPP_GetValueProc(NP_GetValue);
#if 0
  plugin_funcs->print = NULL;
  plugin_funcs->javaClass = NULL;
  plugin_funcs->setvalue = NewNPP_SetValueProc (plugin_set_value);
#endif

  return NPERR_NO_ERROR;
}

NPError
NP_Shutdown (void)
{
  /* Haha, we stay in memory anyway, no way to get rid of us! */
  return NPERR_NO_ERROR;
}

