/* Swfdec
 * 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 <swfdec/swfdec.h>
#include <swfdec-fb/swfdec-fb.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "swfdec_np_scripting.h"
#include "flash_event.h"
#include "swfdec_debug.h"

static GMainLoop *loop = NULL;
static SwfdecNPScripting * g_pNPS = NULL;
static char *variables = NULL;
static char *base_url = NULL;
int streamfd = -1;
int eventfd = -1;
int evretfd = -1;
int externalfd = -1;
int exterretfd = -1;
int terminate_request = 0;
int fb_x = -1, fb_y = -1, fb_w = -1, fb_h = -1;
int g_receivekey = 0;
unsigned long StartTime;

void terminate_signal(int signo) 
{
  printf("terminate_signal, received, signo = %d\n", signo);
  terminate_request = 1;
}

void terminate_forced(int signo) 
{
  printf("terminate_forced, received, signo = %d\n", signo);
  exit(-2);
}

static gboolean
parse_parameter(const gchar *option_name, const gchar *value, gpointer data,
    GError **error)
{
  char *p = NULL;
  int len = 0;
  if (value == NULL)
	return TRUE;
  g_printerr("Parameter:%s\n", value);
  p = strchr (value, '=');
  if (p == NULL)
	return TRUE;
  len = p - value;
  p++;
  if (*p == '\0')
	return TRUE;
  if (g_ascii_strncasecmp (value, "flashvars", 9) == 0) 
  {
	variables = g_strdup(p);
  }
  else if (g_ascii_strncasecmp (value, "base", 4) == 0)
  {
	base_url = g_strdup(p);
  }
  else if (g_ascii_strncasecmp (value, "scale", 5) == 0)
  {
	/* todo */
  }
  else if (g_ascii_strncasecmp (value, "salign", 6) == 0)
  {
	/* todo */
  }
  return TRUE;
}

static void
set_playing (SwfdecFbWidget *widget, GParamSpec *pspec, gpointer player)
{
  swfdec_fb_player_set_playing (player, TRUE);
}

void
set_widget_size(SwfdecFbWidget *widget,
        const SwfdecRectangle *rect, guint n_rects, SwfdecPlayer *player)
{
  g_print ("%s\n", "set_widget_size");
  if (fb_x != -1 && fb_y != -1 && fb_w != -1 && fb_h != -1)
  {
	swfdec_fb_widget_resize(widget, fb_x, fb_y, fb_w, fb_h);
  }
}

static GObject *
view_swf (SwfdecPlayer *player, gboolean use_image, 
	int j, int k, int m, int n)
{
  GObject *widget;

  if ((j != 0 && k != 0) || (m != 0 && n != 0))
  	widget = swfdec_fb_widget_new_withposition (player, m, n, j, k);
  else
  	widget = swfdec_fb_widget_new (player);
  if (use_image)
    swfdec_fb_widget_set_renderer (SWFDEC_FB_WIDGET (widget), CAIRO_SURFACE_TYPE_IMAGE);
  g_signal_connect (widget, "notify::has-focus", G_CALLBACK (set_playing), player);
  g_signal_connect (widget, "initialized", G_CALLBACK (set_widget_size), player);

  return widget;
}

static void
do_trace (SwfdecPlayer *player, const char *message, gpointer unused)
{
  g_print ("%s\n", message);
}

static void 
notify_init_ok()
{
  if (externalfd == -1 || terminate_request)
  	return;
  const char *msg = "flash:init_ok\n";
  g_printerr ("<swfdec> The first stage cost %lu (msec)\n", timeGetTime() - StartTime);
  write(externalfd, msg, strlen(msg));
}

static void
do_fscommand (SwfdecPlayer *player, const char *command, const char *value, gpointer window)
{
  if (g_str_equal (command, "quit")) {
    g_assert (loop);
    if (g_main_loop_is_running (loop)) {
      g_main_loop_quit (loop);
    }
  }
  else if (g_str_equal (command, "notify_init_ok")) {
	notify_init_ok();
  }
  /* FIXME: add more */
}

gboolean
handle_stream(GIOChannel   *source,
        GIOCondition  condition,
        gpointer      data)
{
  return TRUE;
}

gboolean
handle_event_void(GIOChannel   *source,
        gchar *buf,
        gpointer data)
{
#if 0
  SwfdecFbWidget *widget = (SwfdecFbWidget *)data;
  SwfdecPlayer *player = swfdec_fb_widget_get_player(widget);
#endif
  return TRUE;
}

gboolean
handle_event_resize(GIOChannel   *source,
        gchar *buf,
        gpointer data)
{
  SwfdecFbWidget *widget = (SwfdecFbWidget *)data;
  SwfdecPlayer *player = swfdec_fb_widget_get_player(widget);
  FlashEvent * evt = (FlashEvent *)buf;
  fb_x = evt->resize.x;
  fb_y = evt->resize.y;
  fb_w = evt->resize.w;
  fb_h = evt->resize.h;
  if (swfdec_player_is_initialized(player)
	&& (fb_x != -1 && fb_y != -1 && fb_w != -1 && fb_h != -1))
  {
	swfdec_fb_widget_resize(widget, fb_x, fb_y, fb_w, fb_h);
  }
  return TRUE;
}

gboolean
handle_event_key(GIOChannel   *source,
        gchar *buf,
        gpointer data)
{
  SwfdecFbWidget *widget = (SwfdecFbWidget *)data;
  SwfdecPlayer *player = swfdec_fb_widget_get_player(widget);
  FlashEvent * evt = (FlashEvent *)buf;
  if (!g_receivekey)
	return TRUE;
  if (swfdec_player_is_initialized(player))
	swfdec_fb_widget_emit_key(widget, 
		evt->key.p1, 
		evt->key.p2, 
		evt->key.p3 == 0 ? FALSE : TRUE);
  return TRUE;
}

gboolean
handle_event_mouse(GIOChannel   *source,
        gchar *buf,
        gpointer data)
{
  SwfdecFbWidget *widget = (SwfdecFbWidget *)data;
  SwfdecPlayer *player = swfdec_fb_widget_get_player(widget);
  FlashEvent * evt = (FlashEvent *)buf;
  if (swfdec_player_is_initialized(player))
	swfdec_fb_widget_emit_mouse(widget, 
		evt->mouse.p1, 
		evt->mouse.p2, 
		evt->mouse.p3);
  return TRUE;
}

gboolean
handle_event_focus(GIOChannel   *source,
        gchar *buf,
        gpointer data)
{
#if 0
  SwfdecFbWidget *widget = (SwfdecFbWidget *)data;
  SwfdecPlayer *player = swfdec_fb_widget_get_player(widget);
#endif
  g_receivekey = 1;
  return TRUE;
}

gboolean
handle_event_blur(GIOChannel   *source,
        gchar *buf,
        gpointer data)
{
#if 0
  SwfdecFbWidget *widget = (SwfdecFbWidget *)data;
  SwfdecPlayer *player = swfdec_fb_widget_get_player(widget);
#endif
  g_receivekey = 0;
  return TRUE;
}

gboolean
handle_event_call(GIOChannel   *source,
        gchar *buf,
        gpointer data)
{
#if 0
  SwfdecFbWidget *widget = (SwfdecFbWidget *)data;
  SwfdecPlayer *player = swfdec_fb_widget_get_player(widget);
#endif
  swfdec_np_scripting_invoke(g_pNPS, buf);
  return TRUE;
}

gboolean
handle_event_ret(GIOChannel   *source,
        gchar *buf,
        gpointer data)
{
#if 0
  SwfdecFbWidget *widget = (SwfdecFbWidget *)data;
  SwfdecPlayer *player = swfdec_fb_widget_get_player(widget);
#endif
  return TRUE;
}

gboolean
handle_event_seturl(GIOChannel   *source,
        gchar *buf,
        gpointer data)
{
  SwfdecFbWidget *widget = (SwfdecFbWidget *)data;
  SwfdecPlayer *player = swfdec_fb_widget_get_player(widget);
  FlashEvent * evt = (FlashEvent *)buf;
  SwfdecURL *url;
  url = swfdec_url_new_from_input (evt->seturl.data);
  swfdec_player_set_url (player, url);
  swfdec_url_free (url);
  swfdec_fb_player_set_playing (SWFDEC_FB_PLAYER(player), TRUE);
  return TRUE;
}

gboolean
handle_event_exit(GIOChannel   *source,
        gchar *buf,
        gpointer data)
{
#if 0
  SwfdecFbWidget *widget = (SwfdecFbWidget *)data;
  SwfdecPlayer *player = swfdec_fb_widget_get_player(widget);
#endif
  if (g_main_loop_is_running (loop)) 
  {
	g_main_loop_quit (loop);
  }
  return TRUE;
}

typedef gboolean (*HANDLE_EVENT_FUNC)(GIOChannel *source, gchar *buf, gpointer data);

HANDLE_EVENT_FUNC funcs[10] = 
{
	handle_event_void,
	handle_event_resize,
	handle_event_key,
	handle_event_mouse,
	handle_event_focus,
	handle_event_blur,
	handle_event_call,
	handle_event_ret,
	handle_event_seturl,
	handle_event_exit,
};

gboolean
handle_event(GIOChannel   *source,
        GIOCondition  condition,
        gpointer      data)
{
  gchar str[MAX_FLASH_EVENT_SIZE], *buf = NULL;;
  gsize len = 0;
  len = read(eventfd, str, sizeof(str));
  g_printerr("handle_event, chars len = %d\n", len);
  if (len > 0)
  {
	buf = str;
	while (len >= 8)
	{
	  FlashEvent * evt = (FlashEvent *)buf;
	  g_printerr("handle_event, type = 0x%08x, len = %d\n", 
		evt->type, evt->len);
	  if (evt->type >= 0 && evt->type <= 9)
		(*funcs[evt->type])(source, buf, data);
	  len -= evt->len;
	  buf += evt->len;
	}
  }
  return TRUE;
}

int 
main (int argc, char *argv[])
{
  StartTime = timeGetTime ();
  int speed = 50;//100;
  int max_runtime = 0;
  SwfdecPlayer *player;
  GError *error = NULL;
  gboolean use_image = FALSE;
  gboolean trace = FALSE;
  gboolean gc = FALSE;
  int j = 0, k = 0, m = 0, n = 0;
  SwfdecURL *url;
  GObject *widget = NULL;
  GIOChannel *channels[2];

  GOptionEntry options[] = {
    { "trace", 't', 0, G_OPTION_ARG_NONE, &trace, "print trace output to stdout", NULL },
    { "width", 'j', 0, G_OPTION_ARG_INT, &j, "width", "PIXELS" },
    { "height", 'k', 0, G_OPTION_ARG_INT, &k, "height", "PIXELS" },
    { "xpos", 'm', 0, G_OPTION_ARG_INT, &m, "xpos", "PIXELS" },
    { "ypos", 'n', 0, G_OPTION_ARG_INT, &n, "ypos", "PIXELS" },
    { "stream", 0, 0, G_OPTION_ARG_INT, &streamfd, "stream fd", "NUMBER" },
    { "event", 0, 0, G_OPTION_ARG_INT, &eventfd, "event fd", "NUMBER" },
    { "evret", 0, 0, G_OPTION_ARG_INT, &evretfd, "event return fd", "NUMBER" },
    { "external", 0, 0, G_OPTION_ARG_INT, &externalfd, "external fd", "NUMBER" },
    { "exterret", 0, 0, G_OPTION_ARG_INT, &exterretfd, "external return fd", "NUMBER" },
    { "parameter", 'P', 0, G_OPTION_ARG_CALLBACK, parse_parameter, "parameter to pass to player", "VAR=NAME[&VAR=NAME..]" },
    { NULL }
  };
  GOptionContext *ctx;

  signal(SIGINT, SIG_IGN);
  signal(SIGTERM, SIG_IGN);
  signal(SIGUSR1, terminate_signal);
  signal(SIGUSR2, terminate_forced);

  ctx = g_option_context_new ("");
  g_option_context_add_main_entries (ctx, options, "options");
  g_option_context_parse (ctx, &argc, &argv, &error);
  g_option_context_free (ctx);

  if (error) {
    g_printerr ("Error parsing command line arguments: %s\n", error->message);
    g_error_free (error);
    return 1;
  }

  swfdec_init ();
  
  loop = g_main_loop_new (NULL, TRUE);

  player = swfdec_fb_player_new (NULL);
  /* this allows the player to continue fine when running in gdb */
  if (max_runtime >= 0)
    swfdec_player_set_maximum_runtime (player, max_runtime * 1000);
#if 0
  if (gc)
    g_object_set (player, "memory-until-gc", (gulong) 0, NULL);
#else
  g_object_set (player, "memory-until-gc", (gulong) 1 * 1024 * 1024, NULL);
#endif
  if (trace)
    g_signal_connect (player, "trace", G_CALLBACK (do_trace), NULL);
  swfdec_fb_player_set_speed (SWFDEC_FB_PLAYER (player), speed / 100.);

  swfdec_fb_player_set_audio_enabled (SWFDEC_FB_PLAYER (player), FALSE);

  widget = view_swf (player, use_image, j, k, m, n);

  g_signal_connect (player, "fscommand", G_CALLBACK (do_fscommand), widget);

  // scripting
  g_pNPS=g_object_new (SWFDEC_TYPE_NP_SCRIPTING,0);
  swfdec_player_set_scripting(player,SWFDEC_PLAYER_SCRIPTING(g_pNPS));
  swfdec_np_scripting_set_np(g_pNPS,player);

  if (variables != NULL)
  {
	g_printerr("varaiables:%s\n", variables);
    swfdec_player_set_variables (player, variables);
  }

  if (base_url != NULL)
  {
	SwfdecURL *url;
	g_printerr("base_url:%s\n", base_url);
	url = swfdec_url_new_from_input (base_url);
	swfdec_player_set_base_url(player, url);
	swfdec_url_free (url);
  }

  // set url if exists
  if (argc > 1)
  {
  	url = swfdec_url_new_from_input (argv[1]);
  	swfdec_player_set_url (player, url);
  	swfdec_fb_player_set_missing_plugins_window (SWFDEC_FB_PLAYER (player),
    	  NULL);
  	swfdec_url_free (url);
 	swfdec_fb_player_set_playing (SWFDEC_FB_PLAYER(player), TRUE);
  }
 
  if (streamfd > 0)
  {
  	channels[0] = g_io_channel_unix_new (streamfd);
  	g_io_channel_set_close_on_unref (channels[0], TRUE);
	g_io_add_watch (channels[0], G_IO_IN | G_IO_HUP,
   		handle_stream, player);
  }

  if (eventfd > 0)
  {
  	channels[1] = g_io_channel_unix_new (eventfd);
  	g_io_channel_set_close_on_unref (channels[1], TRUE);
	g_io_add_watch (channels[1], G_IO_IN | G_IO_HUP,
   		handle_event, widget);
  }

  if (g_main_loop_is_running (loop))
    g_main_loop_run (loop);

  if (streamfd > 0)
  {
	g_io_channel_unref (channels[0]);
  }

  if (eventfd > 0)
  {
	g_io_channel_unref (channels[1]);
  }

  g_signal_handlers_disconnect_by_func (widget, set_playing, player);
  g_signal_handlers_disconnect_by_func (widget, set_widget_size, player);
  g_object_unref (widget);
  g_object_unref (player);
  g_main_loop_unref (loop);
  loop = NULL;
  player = NULL;
  widget = NULL;
  /* resource */
  if (evretfd > 0)
	close(evretfd);
  if (externalfd > 0)
	close(externalfd);
  if (exterretfd > 0)
	close(exterretfd);
  if (variables != NULL)
	free(variables);
  if (base_url != NULL)
	free(base_url);
  return 0;
}

