/* Swfdec Mozilla Plugin
 * Copyright (C) 2006 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 <math.h>
#include <unistd.h>
#include <errno.h>
#include "swfmoz_player.h"
#include "swfmoz_loader.h"
#include "plugin.h"

/*** SWFMOZ_PLAYER ***/

G_DEFINE_TYPE (SwfmozPlayer, swfmoz_player, SWFDEC_TYPE_FB_PLAYER)

void
io_pipe (GIOChannel **channels)
{
  gint fds[2];

  if (pipe(fds) < 0)
  {
  	g_warning ("Cannot create pipe %s\n", g_strerror (errno));
	return;
  }

  channels[0] = g_io_channel_unix_new (fds[0]);
  channels[1] = g_io_channel_unix_new (fds[1]);

  g_io_channel_set_close_on_unref (channels[0], TRUE);
  g_io_channel_set_close_on_unref (channels[1], TRUE);
}

gpointer
mainloop_thread (gpointer data)
{
  SwfmozPlayer *player = SWFMOZ_PLAYER (data);
  player->notified = FALSE;
  while (player->running)
  {
	GIOError err;
	gsize bytes_written = 0;
	if ((!player->notified) 
		&& g_main_context_pending(player->context))
	{
	  player->notified = TRUE;
	  err = g_io_channel_write (player->channels[1],
							"event\n",
							6,
							&bytes_written);
	}
	usleep(100000);
  }
  return NULL;  	
}

void
mainloop_execute(void *data)
{
  SwfmozPlayer *player = SWFMOZ_PLAYER (data);
  gchar* str = NULL;
  gsize len;
  g_io_channel_read_line(player->channels[0], &str, &len, NULL, NULL);
  g_free(str);
  if (g_main_context_pending(player->context))
  {
 	g_main_context_iteration(player->context, FALSE);
  }
  player->notified = FALSE;
}

static SwfdecBuffer *
swfmoz_player_format_headers (guint header_count, const char **header_names,
    const char **header_values, gsize content_length)
{
  GString *string;
  guint i;
  gsize len;

  g_return_val_if_fail (header_count == 0 || header_names != NULL, NULL);
  g_return_val_if_fail (header_count == 0 || header_values != NULL, NULL);

  string = g_string_new ("Content-Length: ");
  g_string_append_printf (string, "%"G_GSIZE_FORMAT, content_length);
  g_string_append (string, "\n");
  for (i = 0; i < header_count; i++) {
    g_string_append (string, header_names[i]);
    g_string_append (string, ": ");
    g_string_append (string, header_values[i]);
    g_string_append (string, "\n");
  }
  g_string_append (string, "\n");

  len = string->len;
  return swfdec_buffer_new_for_data (
      (unsigned char *)g_string_free (string, FALSE), len);
}

/* Returns a new buffer with the headers prepended, suitable for passing to
 * plugin_post_url_notify */
SwfdecBuffer *
swfmoz_player_add_headers (SwfdecBuffer *data, guint header_count,
    const char **header_names, const char **header_values)
{
  SwfdecBufferQueue *queue;
  SwfdecBuffer *buffer;

  g_return_val_if_fail (data != NULL, NULL);

  queue = swfdec_buffer_queue_new ();
  swfdec_buffer_queue_push (queue, swfmoz_player_format_headers (
	header_count, header_names, header_values, data->length));
  swfdec_buffer_queue_push (queue, swfdec_buffer_ref (data));

  buffer = swfdec_buffer_queue_pull (queue,
      swfdec_buffer_queue_get_depth (queue));
  swfdec_buffer_queue_unref (queue);

  return buffer;
}

static void
swfmoz_player_launch (SwfmozPlayer *player, const char *url,
    const char *target, SwfdecBuffer *data, guint header_count,
    const char **header_names, const char **header_values,  gpointer unused)
{
  if (data) {
	// use the notify version of the function so headers can be sent
	// we don't actually care about the notify
    plugin_post_url_notify (player->instance, url, target,
	(const char *)data->data, data->length, NULL);
  } else {
    // FIXME: no way to send headers here?
    plugin_get_url (player->instance, url, target);
  }
}

static void
swfmoz_player_invalidate (SwfmozPlayer *player)
{
  SwfdecRectangle rect = { 0, 0, player->target_rect.width, player->target_rect.height};
  swfdec_fb_widget_emit_invalidate (SWFDEC_FB_WIDGET(player->widget), &rect, 1);
}

static void
swfmoz_player_get_property (GObject *object, guint param_id, GValue *value, 
    GParamSpec * pspec)
{
  //SwfmozPlayer *player = SWFMOZ_PLAYER (object);
  
  switch (param_id) {
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
      break;
  }
}

static void
swfmoz_player_set_property (GObject *object, guint param_id, const GValue *value,
    GParamSpec *pspec)
{
  //SwfmozPlayer *player = SWFMOZ_PLAYER (object);

  switch (param_id) {
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
      break;
  }
}

static void
swfmoz_player_dispose (GObject *object)
{
  SwfmozPlayer *player = SWFMOZ_PLAYER (object);

  g_printerr("mozplay dispose!\n");
  /* the player might be refed elsewhere */
  g_signal_handlers_disconnect_by_func (player, swfmoz_player_launch, NULL);
  if (player->target) {
    player->target = NULL;
  }
//if (player->loop)
  {
  	player->running = FALSE;
// 	if (g_main_loop_is_running (player->loop)) {
//    	g_main_loop_quit (player->loop);
// 	}
   	g_thread_join (player->thread);
// 	g_main_loop_unref(player->loop);
// 	player->loop = NULL;
  }
  g_printerr("swfplay dispose!\n");
  G_OBJECT_CLASS (swfmoz_player_parent_class)->dispose (object);
}

static void
swfmoz_player_class_init (SwfmozPlayerClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->dispose = swfmoz_player_dispose;
  object_class->get_property = swfmoz_player_get_property;
  object_class->set_property = swfmoz_player_set_property;
}

static void
swfmoz_player_init (SwfmozPlayer *player)
{
  g_signal_connect (player, "launch", G_CALLBACK (swfmoz_player_launch), NULL);
}

SwfdecPlayer *
swfmoz_player_new (NPP instance, gboolean windowless, gboolean opaque)
{
  SwfmozPlayer *ret;
  SwfdecSystem *system;
  GError *err = NULL;

  system = swfdec_fb_system_new (NULL);
  g_object_set (system, "player-type", "PlugIn", NULL);
  ret = g_object_new (SWFMOZ_TYPE_PLAYER,
      "loader-type", SWFMOZ_TYPE_LOADER, "loader2-type", SWFDEC_TYPE_FB_LOADER2,
      "socket-type", SWFDEC_TYPE_SOCKET,
      "system", system,
      NULL);
  // loop
  io_pipe(ret->channels);
  //ret->loop = g_main_loop_new (NULL, TRUE);
  //ret->context = g_main_loop_get_context (ret->loop);
  ret->context = g_main_context_default();
  ret->instance = instance;
  ret->windowless = windowless;
  ret->opaque = opaque;
  ret->target = NULL;
  swfdec_rectangle_init_empty(&ret->target_rect);
  ret->widget = swfdec_fb_widget_new (SWFDEC_PLAYER(ret));
  swfdec_fb_player_set_audio_enabled (SWFDEC_FB_PLAYER(ret), FALSE);
  // mainloop
  ret->running = TRUE;
  ret->thread = g_thread_create (mainloop_thread, ret, TRUE, &err); 
  return SWFDEC_PLAYER (ret);
}

gboolean
swfmoz_player_set_initial_stream (SwfmozPlayer *player, NPStream *stream)
{
  SwfdecURL *url;

  g_return_val_if_fail (SWFMOZ_IS_PLAYER (player), FALSE);
  g_return_val_if_fail (stream != NULL, FALSE);

  if (player->initial || swfdec_player_get_url (SWFDEC_PLAYER (player)))
    return FALSE;
  player->initial = stream;
  url = swfdec_url_new (stream->url);
  if (url == NULL) {
    g_printerr ("%s is either a broken URL or Swfdec can't parse it\n",
	stream->url);
    return FALSE;
  }
  swfdec_player_set_url (SWFDEC_PLAYER (player), url);
  swfdec_fb_player_set_playing (SWFDEC_FB_PLAYER (player), TRUE);
  swfdec_url_free (url);
  swfmoz_player_invalidate (player);

  return TRUE;
}

void
swfmoz_player_set_target (SwfmozPlayer *player, void *target, 
    int x, int y, int width, int height)
{
  g_return_if_fail (SWFMOZ_IS_PLAYER (player));
//g_return_if_fail (target == NULL);

  if (target != player->target) {
    player->target = target;
  }
  if (width == 0 || height == 0)
	return;
  player->target_rect.x = x;
  player->target_rect.y = y;
  player->target_rect.width = width;
  player->target_rect.height = height;  
  swfdec_player_set_size (SWFDEC_PLAYER (player), width, height);
  swfdec_fb_widget_set_position(SWFDEC_FB_WIDGET(player->widget), x, y);
}

void
swfmoz_player_set_allow_popups (SwfmozPlayer *player, gboolean allow)
{
  g_return_if_fail (SWFMOZ_IS_PLAYER (player));

  swfdec_player_set_allow_fullscreen (SWFDEC_PLAYER (player), allow);
  if (allow)
    plugin_push_allow_popups (player->instance, TRUE);
  else
    plugin_pop_allow_popups (player->instance);
}

char *
swfmoz_player_get_filename (SwfmozPlayer *player)
{
  const SwfdecURL *url;

  g_return_val_if_fail (SWFMOZ_IS_PLAYER (player), NULL);

  url = swfdec_player_get_url (SWFDEC_PLAYER (player));
  if (url == NULL)
    return g_strdup ("unknown.swf");

  return swfdec_url_format_for_display (url);
}

void
swfmoz_player_remove (SwfmozPlayer *player)
{
  /* This function is called when the player is removed from screen.
   * It may still be necessary to keep it around if dialogs are open.
   */
  g_return_if_fail (SWFMOZ_IS_PLAYER (player));

  swfdec_fb_player_set_playing (SWFDEC_FB_PLAYER (player), FALSE);
  swfdec_fb_player_set_audio_enabled (SWFDEC_FB_PLAYER (player), FALSE);
  if (player->widget)
  {
  	g_object_unref (player->widget);
  	player->widget = NULL;
  }
  player->instance = NULL;
  g_object_unref (player);
}

