/* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; c-file-style:"stroustrup" -*-
**
** Copyright (C) 1995-2012 Opera Software AS.  All rights reserved.
**
** It is prohibited for you to distribute this file to anybody without Operas
** separate written consent.
*/

/*
 * This is a very simple example of a plugin for GOGI.
 *
 * This plugin should be built using a modified npunix.c. npunix.c from
 * the Netscape plugin developer kit defines XP_UNIX, which shouldn't be used for
 * GOGI plugins, and it does not setup the functions for dealing with windowless
 * plugins properly.
 *
 * The example animates the plugin window in different colors.
 * 
 * The plugin understands the following arguments:
 * "use_paint_buffer"   - Makes the plugin draw to a paint buffer of the requested pixel
 *                        format at startup.
 * "do_not_activate"    - Makes the plugin return false when it gets an activate event.
 *                        In this mode the plugin should never get focused.
 * "stipple"            - Makes the plugin stippled at startup. This causes it to skip every
 *                        other pixel when drawing, making the background show through.
 * "alpha_transparent"  - Makes the plugin use alpha transparency at startup, causing
 *                        the background to partially show through. This forces a paint
 *                        buffer to be used.
 *
 * The plugin can be controlled by keypresses as follows.
 * '+' - Increase the delay between frames. Slows down the animation.
 * '-' - Decrease the delay between frames. Speeds up the animation.
 * '7' - Toggle the use of the paint buffer.
 * '9' - Toggle alpha transparency mode.
 * '6' - Makes the plugin less transparent when alpha transparency is
 *       enabled (increases the alpha value).
 * '3' - Makes the plugin more transparent when alpha transparency is
 *       enabled (decreases the alpha value).
 * '4' - Toggles stippled mode.
 * '5' - Sends an event with arbitrary data to the browser interface.
 *
 * In order for the plugin to receive key press events it must first be focused. Unless the plugin
 * is started with the "do_not_activate" argument it can be focused. It is done in one of two ways:
 * Either by a mouse click on the plugin, or by navigating to it and pressing "ctrl-space". Once
 * focused the plugin can be unfocused by pressing ctrl-space again. The plugin gives some feedback
 * about which state it is currently in. If the plugin is activated, that is when it is first
 * navigated to, but hasn't been focused yet, it draws itself with a stippled border. If the plugin is
 * focused, it draws ifself with a solid border. If the plugin is neither activated nor focused
 * it doesn't draw a border at all.
 *
 * In order for keyboard navigation to work properly you need something like this in your input.ini:
 *
 * [Browser Widget]
 * Space ctrl              = Activate element
 * Left shift              = Navigate left
 * Right shift             = Navigate right
 * Up shift                = Navigate up
 * Down shift              = Navigate down
 *
 * [GOGI Plugins]
 * Space ctrl              = Unfocus plugin
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "npapi.h"
#include "npfunctions.h"

#include "gogi_plugin_api.h"

#define NUM_COLORS 3

/* The colors red, green, blue in each pixel format */
static unsigned int   bgra32_colors[NUM_COLORS] = { 0x00FF0000, 0x0000FF00, 0x000000FF };
static unsigned short bgra32_shifts[NUM_COLORS] = { 16,         8,          0 }; /* offsets of each bit field */

static unsigned int   rgba32_colors[NUM_COLORS] = { 0x000000FF, 0x0000FF00, 0x00FF0000 };
static unsigned short rgba32_shifts[NUM_COLORS] = { 0,          8,          16 };

static unsigned int   abgr32_colors[NUM_COLORS] = { 0xFF000000, 0x00FF0000, 0x0000FF00 };
static unsigned short abgr32_shifts[NUM_COLORS] = { 24,         16,          8 };

static unsigned short rgba16_colors[NUM_COLORS] = { 0x000F, 0x00F0, 0x0F00 };
static unsigned short rgba16_shifts[NUM_COLORS] = { 0,      4,      8 };

static unsigned short abgr16_colors[NUM_COLORS] = { 0xF000, 0x0F00, 0x00F0 };
static unsigned short abgr16_shifts[NUM_COLORS] = { 12,     8,      4 };

static unsigned short bgr16_colors[NUM_COLORS] = { 0xF800, 0x07E0, 0x001F };
static unsigned short rgb16_colors[NUM_COLORS] = { 0x001F, 0x07E0, 0xF800 };


/* This struct is used to store plugin specific data in instance->pdata */
struct PluginData
{
	GogiPluginWindow* window;  /* Our handle to the GOGI plugin api. */
	char use_paint_buffer;     /* Whether the plugin should use a paint buffer or not */
	GOGI_FORMAT pixel_format;  /* Pixel format to use for paint buffer */
	uint width;                /* The current width and height as we know it. We use these to determine
								  if a call to NPP_SetWindow() is because of a size change. */
	uint height;
	int color_index;           /* Specifies the color of the current frame. The actual
								  color is looked up in the *_colors arrays above */
	int animation_speed;       /* The timeout between frames */
	int timer_id;              /* The id of the last timeout. -1 means that the timer hasn't been started yet */
	int activatable;           /* Whether it should be possible to focus the plugin or not. */
	int state;                 /* 0: Neither activated nor focused. 1: Activated. 2:focused */ 
	int stipple;               /* Skip every other pixel */
	int alpha_transparent;     /* Make the plugin transparent (only when using paint buffer) */
	unsigned char alpha_value; /* The alpha value to use when the plugin is alpha_transparent */
};


/* Helper function to get the plugin window from an instance */
static GogiPluginWindow* get_plugin_window(NPP instance)
{
	struct PluginData *plugin_data;
	
	plugin_data = (struct PluginData*) instance->pdata;

	return plugin_data ? plugin_data->window : NULL;
}

/* Paints a part of the plugin to the window.
 * paint_area() is used both when the plugin draws directly to the screen,
 * and when the plugin uses a paint buffer. There is used a bit different
 * between the two modes: When the plugin draws directly to the screen it
 * calls paint_area() to redraw part of the screen when it gets a paint event.
 * When the plugin uses a paint buffer it only calls paint_area() to redraw the
 * whole frame at once and only when the plugin content changes.
 */
static void paint_area(NPP instance, void* area_ptr, struct GOGI_Rect* area)
{
	/*fprintf(stderr,"Plugin: Paint area.x=%d, area.y=%d, area.w=%d, area.h=%d!\n", area->x, area->y, area->w, area->h);*/

	struct PluginData *plugin_data;
	GogiPluginWindow* pw;
	GogiPluginPaintInfo* pi;
	char *row_ptr;
	int row;

	plugin_data = (struct PluginData*) instance->pdata;
	if ( plugin_data == NULL )
		return;

	pw = plugin_data->window;
	if ( pw == NULL )
		return;

	pi = pw->get_paint_info(pw);
	if ( pi == NULL )
		return;

		// pre-multiply the colors...
	unsigned int bgra32_premult[NUM_COLORS];
	bgra32_premult[0] = bgra32_colors[0] & (plugin_data->alpha_value << bgra32_shifts[0]);
	bgra32_premult[1] = bgra32_colors[1] & (plugin_data->alpha_value << bgra32_shifts[1]);
	bgra32_premult[2] = bgra32_colors[2] & (plugin_data->alpha_value << bgra32_shifts[2]);
	unsigned int bgra32_alpha = plugin_data->alpha_value << 24;

	unsigned int rgba32_premult[NUM_COLORS];
	rgba32_premult[0] = rgba32_colors[0] & (plugin_data->alpha_value << rgba32_shifts[0]);
	rgba32_premult[1] = rgba32_colors[1] & (plugin_data->alpha_value << rgba32_shifts[1]);
	rgba32_premult[2] = rgba32_colors[2] & (plugin_data->alpha_value << rgba32_shifts[2]);
	unsigned int rgba32_alpha = plugin_data->alpha_value << 24;

	unsigned int abgr32_premult[NUM_COLORS];
	abgr32_premult[0] = abgr32_colors[0] & (plugin_data->alpha_value << abgr32_shifts[0]);
	abgr32_premult[1] = abgr32_colors[1] & (plugin_data->alpha_value << abgr32_shifts[1]);
	abgr32_premult[2] = abgr32_colors[2] & (plugin_data->alpha_value << abgr32_shifts[2]);
	unsigned int abgr32_alpha = plugin_data->alpha_value;

	unsigned short rgba16_premult[NUM_COLORS];
	rgba16_premult[0] = rgba16_colors[0] & (plugin_data->alpha_value << rgba16_shifts[0]);
	rgba16_premult[1] = rgba16_colors[1] & (plugin_data->alpha_value << rgba16_shifts[1]);
	rgba16_premult[2] = rgba16_colors[2] & (plugin_data->alpha_value << rgba16_shifts[2]);
	unsigned short rgba16_alpha = plugin_data->alpha_value << 12;

	unsigned short abgr16_premult[NUM_COLORS];
	abgr16_premult[0] = abgr16_colors[0] & (plugin_data->alpha_value << abgr16_shifts[0]);
	abgr16_premult[1] = abgr16_colors[1] & (plugin_data->alpha_value << abgr16_shifts[1]);
	abgr16_premult[2] = abgr16_colors[2] & (plugin_data->alpha_value << abgr16_shifts[2]);
	unsigned short abgr16_alpha = plugin_data->alpha_value >> 4;

	row_ptr = (char*)area_ptr;
	row;
	for ( row = 0; row < area->h; row++ )
	{
		/*fprintf(stderr, "row = %d, row_ptr=%p\n", row, row_ptr);*/ 
		char *col_ptr = row_ptr;
		int col;
		for ( col = 0; col < area->w; col++)
		{
			/*fprintf(stderr, "col = %d,col_ptr=%p\n", col, col_ptr);*/
			int draw_black = false;
			if ( plugin_data->activatable == false && ( area->y + row == 6 || area->y + row == plugin_data->height - 7 ) )
			{
				draw_black = true;
			}
			else if ( plugin_data->activatable == false && ( area->x + col == 6 || area->x + col == plugin_data->width - 7 ) )
			{
				draw_black = true;
			}
			else if ( area->y + row < 3 || area->y + row > plugin_data->height - 4 )
			{
				/* Draw the upper or lower border */
				if ( plugin_data->state == 2 )
				{
					/* focused */
					draw_black = true;
				}
				else if ( plugin_data->state == 1 && ((area->x + col + row ) % 2) == 1)
				{
					/* activated */
					draw_black = true;
				}
				else
				{
					/* normal */
					draw_black = false;
				}
			}
			else if ( area->x + col < 3 || area->x + col > plugin_data->width - 4 )
			{
				/* Draw the left or right border */
				if ( plugin_data->state == 2 )
				{
					/* focused */
					draw_black = true;
				}
				else if ( plugin_data->state == 1 && ((area->x + col + row ) % 2) == 1)
				{
					/* activated */
					draw_black = true;
				}
				else
				{
					/* normal */
					draw_black = false;
				}
			}

			switch (pi->pixel_format)
			{
			case GOGI_FORMAT_VEGA_BGRA32:
				if ( draw_black || !plugin_data->stipple || ((area->x + col + row ) % 2) == 1 )
				{
					*((unsigned int *)col_ptr) = draw_black ? 0xFF000000 : (bgra32_premult[plugin_data->color_index] | bgra32_alpha);
				}
				else if ( pw->get_paint_buffer(pw) != NULL )
				{
					/* In stippled mode every other pixel is skipped. It makes the page show through
					 * when the plugin draws directly to the screen. However, it won't work when the plugin
					 * draws to a buffer, since the background isn't drawn into the buffer first.
					 * As a matter of fact, all the pixels in the buffer must be updated by the plugin.
					 * Otherwise, uninitialized garbage will be copied to the screen.
					 */
					*((unsigned int *)col_ptr) = 0x00000000; /* Transparent pixel */
				}
				col_ptr+=4;
				break;
			case GOGI_FORMAT_VEGA_RGBA32:
				if ( draw_black || !plugin_data->stipple || ((area->x + col + row ) % 2) == 1 )
				{
					*((unsigned int *)col_ptr) = draw_black ? 0xFF000000 : (rgba32_premult[plugin_data->color_index] | rgba32_alpha);
				}
				else if ( pw->get_paint_buffer(pw) != NULL )
				{
					*((unsigned int *)col_ptr) = 0x00000000; /* Transparent pixel */
				}
				col_ptr+=4;
				break;
			case GOGI_FORMAT_VEGA_ABGR32:
				if ( draw_black || !plugin_data->stipple || ((area->x + col + row ) % 2) == 1 )
				{
					*((unsigned int *)col_ptr) = draw_black ? 0x000000FF : (abgr32_premult[plugin_data->color_index] | abgr32_alpha);
				}
				else if ( pw->get_paint_buffer(pw) != NULL )
				{
					*((unsigned int *)col_ptr) = 0x00000000; /* Transparent pixel */
				}
				col_ptr+=4;
				break;
			case GOGI_FORMAT_VEGA_RGBA16:
				if ( draw_black || !plugin_data->stipple || ((area->x + col + row ) % 2) == 1 )
				{
					*((unsigned short *)col_ptr) = draw_black ? 0xF000 : rgba16_premult[plugin_data->color_index] | rgba16_alpha;
				}
				else if ( pw->get_paint_buffer(pw) != NULL )
				{
					*((unsigned short *)col_ptr) = 0x0000;
				}
				col_ptr+=2;
				break;
			case GOGI_FORMAT_VEGA_ABGR16:
				if ( draw_black || !plugin_data->stipple || ((area->x + col + row ) % 2) == 1 )
				{
					*((unsigned short *)col_ptr) = draw_black ? 0x000F : abgr16_premult[plugin_data->color_index] | abgr16_alpha;
				}
				else if ( pw->get_paint_buffer(pw) != NULL )
				{
					*((unsigned short *)col_ptr) = 0x0000;
				}
				col_ptr+=2;
				break;
			case GOGI_FORMAT_VEGA_RGB16:
				if ( draw_black || !plugin_data->stipple || ((area->x + col + row ) % 2) == 1 )
				{
					*((unsigned short *)col_ptr) = draw_black ? 0x0000 : rgb16_colors[plugin_data->color_index];
				}
				else if ( pw->get_paint_buffer(pw) != NULL )
				{
					*((unsigned short *)col_ptr) = 0xFFFF; /* Can't draw transparent pixels, draw white instead */
				}
				col_ptr+=2;
				break;
			case GOGI_FORMAT_VEGA_BGR16:
				if ( draw_black || !plugin_data->stipple || ((area->x + col + row ) % 2) == 1 )
				{
					*((unsigned short *)col_ptr) = draw_black ? 0x0000 : bgr16_colors[plugin_data->color_index];
				}
				else if ( pw->get_paint_buffer(pw) != NULL )
				{
					*((unsigned short *)col_ptr) = 0xFFFF; /* Can't draw transparent pixels, draw white instead */
				}
				col_ptr+=2;
				break;
			default:
				fprintf(stderr,"Unknown pixel format! %d\n", pi->pixel_format);
			}
		}
		row_ptr += pi->stride;
	}
}

/* Used by the plugin to draw a frame in the paint buffer
 * When the plugin doesn't use the paint buffer it can't
 * redraw the whole frame at once and must redraw the
 * frame piece by piece when it receives paint events.
 */
static void paint_frame(NPP instance)
{
	void *paint_buffer;
	GogiPluginPaintInfo* pi;
	struct GOGI_Rect rect;

	GogiPluginWindow* pw = get_plugin_window(instance);
	if ( pw == NULL )
		return;

	paint_buffer = pw->get_paint_buffer(pw);
	if ( paint_buffer == NULL )
		return;

	pi = pw->get_paint_info(pw);
	if ( pi == NULL )
		return;

	rect.x = 0;
	rect.y = 0;
	rect.w = pi->width;
	rect.h = pi->height;
	
	/* Draw the current frame to the paint buffer */
	paint_area(instance, paint_buffer, &rect);
}


/* Handles a paint event.
 * The plugin only have to paint anything here when it draws directly
 * to the screen. When the plugin uses a paint buffer it has already
 * ensured that the paint buffer is up to date, and Opera will autmatically
 * update the screen by copying from the paint buffer.
 */
static int handle_paint_event(NPP instance, GogiPluginEventPaint* event)
{
	GogiPluginWindow* pw = get_plugin_window(instance);
	if ( pw == NULL || pw->get_paint_buffer(pw) != NULL )
	{
		/* No need to update the area if we are using a buffer.
		 * The buffer already contains the current frame and Opera will automatically copy the data from there.
		 */
		return true;
	}

	paint_area(instance, event->area_ptr, &event->paint_area);
	return true;
}

/* Invalidates the whole plugin window so that the plugin is repainted.
 * The plugin calls this function when it switches to the next frame to
 * inform Opera that the plugin window must be repainted. Invalidating
 * will eventually result in paint events being sent to the plugin.
 *
 * invalidate_frame() doesn't just invalidate the plugin window. It also
 * redraws the paint buffer, so that it is up to date when the paint
 * buffer is eventually copied to the screen.
 */
static void invalidate_frame(NPP instance)
{
	struct PluginData *plugin_data;	
	GogiPluginWindow* pw;
	GogiPluginPaintInfo* pi;
	NPRect rect;

	plugin_data = (struct PluginData*) instance->pdata;
	if ( plugin_data == NULL )
		return;

	pw = plugin_data->window;
	if ( pw == NULL )
		return;

	pi = pw->get_paint_info(pw);
	if ( pi == NULL )
		return;

	if ( pw->get_paint_buffer(pw) )
	{
		/* We repaint the whole frame at once in buffered mode */
		/* Opera updates the screen with the relevant pieces from the buffer when needed */
		paint_frame(instance);
	}

	rect.top = 0;
	rect.left = 0;
	rect.bottom = pi->height;
	rect.right = pi->width;

	NPN_InvalidateRect(instance, &rect);
}

/* Handles a timer event.
 * The plugin switches to the next frame when a timer event is received.
 * Update the color index, invalidate and start a timer for the next frame switch.
 */
static int handle_timer_event(NPP instance, GogiPluginEventTimer* event)
{
	/*fprintf(stderr,"Got timer event for id=%d\n", event->timer_id);*/

	struct PluginData *plugin_data;
	GogiPluginWindow* pw;	

	plugin_data = (struct PluginData*) instance->pdata;
	if ( plugin_data == NULL )
		return true;

	pw = plugin_data->window;
	if ( pw == NULL )
		return true;

	/* Draw in another color */
	plugin_data->color_index++;
	if ( plugin_data->color_index >= NUM_COLORS )
	{
		plugin_data->color_index = 0;
	}

	invalidate_frame(instance);

	plugin_data->timer_id = pw->start_timer(pw, plugin_data->animation_speed);

	return true;
}

/* Handles the various key presses and sets the plugin state accordingly
 *
 * Note that the code checks the repeat count for the key down events to avoid
 * repeating the same action over and over again in case the user holds down a key for to long.
 * It is especially important for toggle actions that might otherwise toggle an even number of
 * times and basically cancel itself.
 * 
 */
static int handle_keyboard_event(NPP instance, GogiPluginEventKeyboard* event)
{
	struct PluginData *plugin_data;
	GogiPluginWindow* pw;	

	fprintf(stderr, "Plugin: Got keyboard event: event=%d, keysym=%x, keymod=%d\n", event->reason, event->keysym, event->keymod);

	plugin_data = (struct PluginData*) instance->pdata;
	if ( plugin_data == NULL )
		return false;

	pw = plugin_data->window;
	if ( pw == NULL )
		return false;

	switch ( event->keysym )
	{
	case GOGI_KEY_ADD:
	case '+':
		/* Increase frame delay */
		if ( event->reason == GOGI_KEYDOWN && plugin_data->timer_id >= 0 )
		{
			pw->stop_timer(pw, plugin_data->timer_id);
			plugin_data->animation_speed += (int)(plugin_data->animation_speed * 0.1);
			plugin_data->timer_id = pw->start_timer(pw, plugin_data->animation_speed);
		}
		return true;
	case GOGI_KEY_DEC:
	case '-':
		/* Decrease frame delay */
		if ( event->reason == GOGI_KEYDOWN && plugin_data->timer_id >= 0 )
		{
			pw->stop_timer(pw, plugin_data->timer_id);
			plugin_data->animation_speed -= (int)(plugin_data->animation_speed * 0.1);
			if ( plugin_data->animation_speed < 50 )
				plugin_data->animation_speed = 50;
			plugin_data->timer_id = pw->start_timer(pw, plugin_data->animation_speed);
		}
		return true;
	case '7':
		/* Toggle paint buffer */
		if ( event->reason == GOGI_KEYDOWN && plugin_data->timer_id >= 0 && event->repeat_count == 0 )
		{
			if ( pw->get_paint_buffer(pw) == NULL )
			{
				fprintf(stderr, "Enabling paint buffer\n");
				pw->enable_paint_buffer(pw, plugin_data->pixel_format);
			}
			else
			{
				fprintf(stderr, "Disabling paint buffer\n");
				pw->disable_paint_buffer(pw);
			}
			invalidate_frame(instance);
		}
		return true;
	case '9':
		/* Toggle alpha transparency */
		if ( event->reason == GOGI_KEYDOWN && plugin_data->timer_id >= 0 && event->repeat_count == 0 )
		{
			if ( plugin_data->alpha_transparent == true )
			{
				fprintf(stderr, "Disabling transparency\n");
				NPN_SetValue(instance, NPPVpluginTransparentBool, (void*) false);
				plugin_data->alpha_transparent = false;
			}
			else
			{
				fprintf(stderr, "Enabling transparency\n");
				NPN_SetValue(instance, NPPVpluginTransparentBool, (void*) true);
				plugin_data->alpha_transparent = true;
				if ( plugin_data->alpha_value == 255 )
					plugin_data->alpha_value = 127;
			}
			invalidate_frame(instance);
		}
		return true;
	case '6':
		/* Increase alpha */
		if ( event->reason == GOGI_KEYDOWN && plugin_data->timer_id >= 0 )
		{
			if ( plugin_data->alpha_transparent == true && plugin_data->alpha_value != 255 )
			{
				plugin_data->alpha_value = ((plugin_data->alpha_value + 1) / 8 + 1) * 8 - 1;
				invalidate_frame(instance);
			}
		}
		fprintf(stderr,"alpha = %d\n", plugin_data->alpha_value);
		return true;
	case '3':
		/* Decrease alpha */
		if ( event->reason == GOGI_KEYDOWN && plugin_data->timer_id >= 0 )
		{
			if ( plugin_data->alpha_transparent == true  && plugin_data->alpha_value != 0 )
			{
				plugin_data->alpha_value = ((plugin_data->alpha_value + 1) / 8 - 1) * 8 - 1;
				if ( plugin_data->alpha_value == 255 )
					plugin_data->alpha_value = 0;
				invalidate_frame(instance);
			}
		}
		fprintf(stderr,"alpha = %d\n", plugin_data->alpha_value);
		return true;
	case '4':
		/* Toggle stipple */
		if ( event->reason == GOGI_KEYDOWN && plugin_data->timer_id >= 0 && event->repeat_count == 0 )
		{
			plugin_data->stipple = !plugin_data->stipple;
			invalidate_frame(instance);
		}
		return true;
	case '5':
		/* Send an event to the UI */
		if ( event->reason == GOGI_KEYDOWN && plugin_data->timer_id >= 0 && event->repeat_count == 0 )
		{
			plugin_data->window->send_event(plugin_data->window, 42, NULL, NULL);
		}
		return true;
	}

	return false;
}

/** strdup using NPN_MemAlloc to allocate memory. */
static char*
npp_strdup(const char *s)
{
	char *retval;

	if ( s == NULL )
		return NULL;

	retval = (char *)NPN_MemAlloc(strlen(s) + 1);
	if ( retval == NULL )
		return NULL;

	strcpy(retval, s);

	return retval;
}

NPError
NPP_Initialize(void)
{
    return NPERR_NO_ERROR;
}

void
NPP_Shutdown(void)
{
}


/* NPP_New() is called when the plugin is created.
 * The plugin allocates its plugins specific data and initializes
 * it according to the plugin arguments.
 *
 * Note that at this time the plugin does not yet have a window, so
 * it is not possible to access the GOGI plugin API functions that
 * go through the GogiPluginWindow. We won't get a handle to the
 * GogiPluginWindow until NPP_SetWindow() is called.
 */
NPError
NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode,
		int16_t argc, char* argn[], char* argv[], NPSavedData* saved)
{
	int i;
	
	struct PluginData *plugin_data;

	NPN_SetValue(instance, NPPVpluginWindowBool, (void*) false); /* Just in case support for windowed plugins is added in the future. */
	NPN_SetValue(instance, NPPVpluginTransparentBool, (void*) false);

	plugin_data = (struct PluginData*) NPN_MemAlloc(sizeof(struct PluginData));
	if ( plugin_data == NULL )
		goto handle_oom;

	plugin_data->window = NULL;
	plugin_data->use_paint_buffer = false;
	plugin_data->pixel_format = GOGI_FORMAT_VEGA_BGRA32;
	plugin_data->width = 0;
	plugin_data->height = 0;
	plugin_data->color_index = 0;
	plugin_data->animation_speed = 1000;
	plugin_data->timer_id = -1;
	plugin_data->activatable = true;
	plugin_data->state = 0;
	plugin_data->stipple = false;
	plugin_data->alpha_transparent = false;
	plugin_data->alpha_value = 255;

	/* Go through the args */
	for ( i = 0; i < argc; i++ )
	{
		fprintf(stderr,"%s=%s\n", argn[i], argv[i]);
		if ( strcmp(argn[i], "use_paint_buffer") == 0 )
		{
			plugin_data->use_paint_buffer = true;
			if ( strcmp(argv[i], "BGRA32") == 0 )
				plugin_data->pixel_format = GOGI_FORMAT_VEGA_BGRA32;
			else if ( strcmp(argv[i], "RGBA32") == 0 )
				plugin_data->pixel_format = GOGI_FORMAT_VEGA_RGBA32;
			else if ( strcmp(argv[i], "ABGR32") == 0 )
				plugin_data->pixel_format = GOGI_FORMAT_VEGA_ABGR32;
			else if ( strcmp(argv[i], "RGBA16") == 0 )
				plugin_data->pixel_format = GOGI_FORMAT_VEGA_RGBA16;
			else if ( strcmp(argv[i], "ABGR16") == 0 )
				plugin_data->pixel_format = GOGI_FORMAT_VEGA_ABGR16;
			else if ( strcmp(argv[i], "RGB16") == 0 )
				plugin_data->pixel_format = GOGI_FORMAT_VEGA_RGB16;
			else if ( strcmp(argv[i], "BGR16") == 0 )
				plugin_data->pixel_format = GOGI_FORMAT_VEGA_BGR16;
			else
				fprintf(stderr,"Unknown pixel format '%s'\n", argv[i]);
		}
		else if ( strcmp(argn[i], "do_not_activate") == 0 )
		{
			plugin_data->activatable = false;
		}
		else if ( strcmp(argn[i], "stipple") == 0 )
		{
			NPN_SetValue(instance, NPPVpluginTransparentBool, (void*) true);
			plugin_data->alpha_transparent = true;
			plugin_data->stipple = true;
		}		
		else if ( strcmp(argn[i], "alpha_transparent") == 0 )
		{
			NPN_SetValue(instance, NPPVpluginTransparentBool, (void*) true);
			plugin_data->alpha_transparent = true;
			plugin_data->alpha_value = 127;
		}		
	}

	instance->pdata = plugin_data;

	return NPERR_NO_ERROR;
	
  handle_oom:
	NPP_Destroy(instance, NULL);
	return NPERR_OUT_OF_MEMORY_ERROR;
}


/* Called when the plugin is about to be destroyed */
NPError
NPP_Destroy(NPP instance, NPSavedData** save)
{
	struct PluginData *plugin_data;
	
	plugin_data = (struct PluginData*) instance->pdata;
	
	if ( plugin_data == NULL )
		return NPERR_NO_ERROR;

	NPN_MemFree(plugin_data);
	
	instance->pdata = NULL;
	
	return NPERR_NO_ERROR;
}


/* Sets the plugin window.
 * NPP_SetWindow() is called when the plugin window is first created and
 * when the plugin window configuration changes somehow. 
 *
 * Note that NPP_SetWindow() is called several times during the lifetime of a plugin.
 * It is not always necessary to do anything in response to a NPP_SetWindow() call. Normally
 * the plugin should check if it has gotten an GogiPluginWindow and save it for later.
 * The plugin should also check if the size of the plugin window has changed and 
 * act on that. It is especially important if the plugin uses the paint buffer, since
 * the paint buffer is reallocated when the plugin window is resized. If the plugin
 * references the old paint buffer a crash is likely to follow.
 */
NPError
NPP_SetWindow(NPP instance, NPWindow* window)
{
	struct PluginData *plugin_data;
	GogiPluginWindow* pw;

	/*fprintf(stderr,"NPP_SetWindow: %p x=%d, y=%d, width = %d, height = %d\n", window, window->x, window->y, window->width, window->height);*/

	plugin_data = (struct PluginData*) instance->pdata;

	if ( window == NULL )
	{
		plugin_data->window = NULL;
		return NPERR_NO_ERROR;
	}

	pw = (GogiPluginWindow*) window->window;


	if ( pw == NULL )
		return NPERR_NO_ERROR;

	if ( plugin_data->window != pw )
	{
		/* In case the plugin window has changed we have to set everything up. */
		plugin_data->window = pw;
		plugin_data->width = window->width;
		plugin_data->height = window->height;
		
		if ( plugin_data->use_paint_buffer || plugin_data->alpha_transparent )
		{
			if (pw->enable_paint_buffer(pw, plugin_data->pixel_format) != GOGI_STATUS_OK)
			{
				fprintf(stderr, "Failed to activate paint buffer\n");
				return NPERR_GENERIC_ERROR;
			}
			
			/* Paint the first frame in the paint buffer */
			paint_frame(instance);
		}

		plugin_data->timer_id = pw->start_timer(pw, plugin_data->animation_speed);
	}
	else
	{
		/* Apparently the window hasn't changed. It probably means that the window has been resized */
		if ( window->width != plugin_data->width || window->height != plugin_data->height )
		{
			/*fprintf(stderr,"NPP_SetWindow: size change detected\n");*/
			plugin_data->width = window->width;
			plugin_data->height = window->height;


			/* A resize causes the paint buffer to be reallocated and loose all data */
			/* To avoid garbage on the screen we must redraw the data in the paint buffer */ 
			if ( pw->get_paint_buffer(pw) != NULL )
			{
				/* Redraw the current frame in the paint_buffer */
				paint_frame(instance);
			}
		}
	}

	return NPERR_NO_ERROR;
}

NPError
NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
			  NPBool seekable, uint16_t* stype)
{
	*stype = NP_NORMAL;
	return NPERR_NO_ERROR;
}

NPError
NPP_DestroyStream(NPP instance, NPStream* stream, NPError reason)
{
	return NPERR_NO_ERROR;
}

void
NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
{
}

int32_t
NPP_WriteReady(NPP instance, NPStream* stream)
{
	return 200*1024;
}

int32_t
NPP_Write(NPP instance, NPStream* stream, int32_t offset,
		  int32_t len, void* buffer)
{
	return len;
}

void
NPP_Print(NPP instance, NPPrint* platformPrint)
{
}


/* Opera calls NPP_HandleEvent when the plugin receives an event
 *
 * Most of the event handling is done in helper functions. Those
 * have already been documented above, where the functions are defined.
 *
 * The focus cycle is the only thing that is dealt with directly in this
 * function. Basically it is only about updating the state and invalidating.
 * The is needed because the plugin draws different looking borders for the
 * different states, so the plugin must redraw its contents.
 */
int16_t
NPP_HandleEvent(NPP instance, void* evt)
{
	GogiPluginEvent* event = (GogiPluginEvent*) evt;
	struct PluginData *plugin_data;
	
	plugin_data = (struct PluginData*) instance->pdata;


	switch ( event->type )
	{
	case GOGI_PLUGIN_EVENT_PAINT:
		return handle_paint_event(instance, (GogiPluginEventPaint*) event);
	case GOGI_PLUGIN_EVENT_TIMER:
		return handle_timer_event(instance, (GogiPluginEventTimer*) event);
	case GOGI_PLUGIN_EVENT_KEYBOARD:
		return handle_keyboard_event(instance, (GogiPluginEventKeyboard*) event);
	case GOGI_PLUGIN_EVENT_MOUSE:
		fprintf(stderr,"Plugin: Got mouse event!\n");
		return false;
	case GOGI_PLUGIN_EVENT_ACTIVATE:
		fprintf(stderr,"Plugin (%p): Got activate event: %d\n", instance, ((GogiPluginEventActivate*) event)->activate);

		if ( ((GogiPluginEventActivate*) event)->activate )
			plugin_data->state = 1;
		else
			plugin_data->state = 0;
			
		invalidate_frame(instance);

		return plugin_data->activatable;
	case GOGI_PLUGIN_EVENT_FOCUS:
		fprintf(stderr, "Plugin (%p): Got focus event: %d, activatable = %d\n", instance, ((GogiPluginEventFocus*) event)->focus, plugin_data->activatable);
		if ( ((GogiPluginEventFocus*) event)->focus )
			plugin_data->state = 2;
		else
			plugin_data->state = 1;

		invalidate_frame(instance);

		return true; /* Doesn't really matter */
	case GOGI_PLUGIN_EVENT_USER:
		fprintf(stderr, "Plugin (%p): Got user event: %d, param1: %p, param2: %p\n", instance, 
			event->user.event, event->user.param1, event->user.param2);
		return true;
	default:
		fprintf(stderr,"Plugin: Unknown event!\n");
		return false;
	}

}

void
NPP_URLNotify(NPP instance, const char* url,
			  NPReason reason, void* notifyData)
{
}


/* Like Unix plugins, GOGI plugins should implement NPP_GetValue()
 * to return values for NPPVpluginNameString and NPPVpluginDescriptionString.
 */
#if NP_VERSION_MAJOR==0 && NP_VERSION_MINOR<=11
#define NPP void*
#endif

NPError
NPP_GetValue(NPP instance, NPPVariable variable, void *value)
{
	switch ( variable )
	{
		case NPPVpluginNameString:
			*((char**)value)= "Test plug-in name";
			return NPERR_NO_ERROR;
		case NPPVpluginDescriptionString:
			*((char**)value) = "Test plug-in description";
			return NPERR_NO_ERROR;
		default:
			return NPERR_INVALID_PARAM;
	};
}

NPError
NPP_SetValue(NPP instance, NPNVariable variable, void *value)
{
	return NPERR_NO_ERROR;
}
#if NP_VERSION_MAJOR==0 && NP_VERSION_MINOR<=11
#undef NPP
#endif

/* Just like Unix plugins the GOGI plugins must implement NPP_GetMIMEDescription().
 * Opera calls NPP_GetMIMEDescription() to determine the mime types the plugin supports.
 *
 * The Nestcape plugin guide is a little bit unclear about how the string should be composed.
 * The string is a list of semicolon separated mimetype specifications. Each mimetype specification
 * consists of three colon separated components. The first component is the mime type itself, the second is a
 * comma separated list of file extensions and the last part is a description string.
 * 
 * For example:
 * "application/x-type1:ext1:A supported mime type;application/x-type2:ext2,ext3,ext4:Another supported mime type"
 */
char *
NPP_GetMIMEDescription(void)
{
	return "application/x-plugintest:unk:Test plug-in type";
}

