/* Swfdec
 * Copyright (C) 2007 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 "swfdec-fb/swfdec_fb_loader.h"
#include "swfdec-fb/swfdec_fb_loader2.h"
#include "swfdec-fb/swfdec_fb_player.h"
#include "swfdec-fb/swfdec_fb_socket.h"
#include "swfdec-fb/swfdec_fb_system.h"
#include "swfdec-fb/swfdec_playback.h"
#include "swfdec-fb/swfdec_source.h"

struct _SwfdecFbPlayerPrivate
{
  GSource *		source;		/* source if playing, NULL otherwise */
  SwfdecPlayback *	playback;	/* audio playback object */
  gboolean		audio_enabled;	/* TRUE if audio should be played */
  double		speed;		/* desired playback speed */

  gboolean		needs_resume;	/* restart playback after plugin install is done? */
};

enum {
  PROP_0,
  PROP_PLAYING,
  PROP_AUDIO,
  PROP_SPEED,
  PROP_MISSING_PLUGINS_WINDOW
};

/*** fb-doc ***/

/**
 * SECTION:SwfdecFbPlayer
 * @title: SwfdecFbPlayer
 * @short_description: an improved #SwfdecPlayer
 *
 * The #SwfdecFbPlayer adds common functionality to the rather barebones 
 * #SwfdecPlayer class, such as automatic playback and audio handling. Note 
 * that by default, the player will be paused, so you need to call 
 * swfdec_fb_player_set_playing () on the new player.
 *
 * @see_also: SwfdecPlayer
 */

/**
 * SwfdecFbPlayer:
 *
 * The structure for the Swfdec Fb player contains no public fields.
 */

/**
 * SWFDEC_FB_PRIORITY_ITERATE:
 *
 * The priority used for advancing a player that is playing.
 */

/**
 * SWFDEC_FB_PRIORITY_REDRAW:
 *
 * The priority used for scheduling repaint operations resulting from advancing
 * a player.
 */

/*** MISSING PLUGINS ***/

static void
swfdec_fb_player_missing_plugins (SwfdecPlayer *player, const char **details)
{
  /* That should only happen if users don't listen and then we don't listen either */
  /* no automatic install desired */
  return;
}

/*** SWFDEC_FB_PLAYER ***/

G_DEFINE_TYPE (SwfdecFbPlayer, swfdec_fb_player, SWFDEC_TYPE_PLAYER)

static void
swfdec_fb_player_get_property (GObject *object, guint param_id, GValue *value, 
    GParamSpec * pspec)
{
  SwfdecFbPlayerPrivate *priv = SWFDEC_FB_PLAYER (object)->priv;
  
  switch (param_id) {
    case PROP_PLAYING:
      g_value_set_boolean (value, priv->source != NULL);
      break;
    case PROP_AUDIO:
      g_value_set_boolean (value, priv->audio_enabled);
      break;
    case PROP_SPEED:
      g_value_set_double (value, priv->speed);
      break;
    case PROP_MISSING_PLUGINS_WINDOW:
      g_value_set_object (value, G_OBJECT (NULL));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
      break;
  }
}

static void
swfdec_fb_player_set_property (GObject *object, guint param_id, const GValue *value,
    GParamSpec *pspec)
{
  SwfdecFbPlayer *player = SWFDEC_FB_PLAYER (object);
  
  switch (param_id) {
    case PROP_PLAYING:
      swfdec_fb_player_set_playing (player, g_value_get_boolean (value));
      break;
    case PROP_AUDIO:
      swfdec_fb_player_set_audio_enabled (player, g_value_get_boolean (value));
      break;
    case PROP_SPEED:
      swfdec_fb_player_set_speed (player, g_value_get_double (value));
      break;
    case PROP_MISSING_PLUGINS_WINDOW:
      swfdec_fb_player_set_missing_plugins_window (player, g_value_get_object (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
      break;
  }
}

static void
swfdec_fb_player_dispose (GObject *object)
{
  SwfdecFbPlayer *player = SWFDEC_FB_PLAYER (object);

  swfdec_fb_player_set_playing (player, FALSE);
  swfdec_fb_player_set_missing_plugins_window (player, NULL);
  g_assert (player->priv->playback == NULL);

  G_OBJECT_CLASS (swfdec_fb_player_parent_class)->dispose (object);
}

static void
swfdec_fb_player_class_init (SwfdecFbPlayerClass * g_class)
{
  GObjectClass *object_class = G_OBJECT_CLASS (g_class);
  SwfdecPlayerClass *player_class = SWFDEC_PLAYER_CLASS (g_class);

  g_type_class_add_private (g_class, sizeof (SwfdecFbPlayerPrivate));

  object_class->dispose = swfdec_fb_player_dispose;
  object_class->get_property = swfdec_fb_player_get_property;
  object_class->set_property = swfdec_fb_player_set_property;

  g_object_class_install_property (object_class, PROP_PLAYING,
      g_param_spec_boolean ("playing", "playing", "TRUE if the player is playing (d'oh)",
	  FALSE, G_PARAM_READWRITE));
  g_object_class_install_property (object_class, PROP_AUDIO,
      g_param_spec_boolean ("audio-enabled", "audio enabled", "TRUE if automatic audio handling is enabled",
	  TRUE, G_PARAM_READWRITE));
  g_object_class_install_property (object_class, PROP_SPEED,
      g_param_spec_double ("speed", "speed", "desired playback speed",
	  G_MINDOUBLE, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE));
  g_object_class_install_property (object_class, PROP_MISSING_PLUGINS_WINDOW,
      g_param_spec_object ("missing-plugins-window", "missing plugins window", 
	  "window on which to do autmatic missing plugins installation",
	  G_TYPE_OBJECT, G_PARAM_READWRITE));

  player_class->missing_plugins = swfdec_fb_player_missing_plugins;
}

static void
swfdec_fb_player_init (SwfdecFbPlayer * player)
{
  SwfdecFbPlayerPrivate *priv;

  player->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (player, SWFDEC_TYPE_FB_PLAYER, SwfdecFbPlayerPrivate);

  priv->speed = 1.0;
  priv->audio_enabled = TRUE;
}

/*** PUBLIC API ***/

/**
 * swfdec_fb_player_new:
 * @debugger: %NULL or a #SwfdecAsDebugger to debug this player
 *
 * Creates a new Swfdec Fb player.
 * This function calls swfdec_init () for you if it wasn't called before.
 *
 * Returns: The new player
 **/
SwfdecPlayer *
swfdec_fb_player_new (SwfdecAsDebugger *debugger)
{
  SwfdecPlayer *player;
  SwfdecSystem *sys;
  
  swfdec_init ();

  sys = swfdec_fb_system_new (NULL);
  player = g_object_new (SWFDEC_TYPE_FB_PLAYER, 
      "loader-type", SWFDEC_TYPE_FB_LOADER, "loader2-type", SWFDEC_TYPE_FB_LOADER2,
      "socket-type", SWFDEC_TYPE_FB_SOCKET,
      "system", sys, "debugger", debugger, NULL);
  g_object_unref (sys);

  return player;
}

static void
swfdec_fb_player_update_audio (SwfdecFbPlayer *player)
{
  SwfdecFbPlayerPrivate *priv = player->priv;
  gboolean should_play = priv->audio_enabled;
  
  should_play &= (priv->source != NULL);
  should_play &= (priv->speed == 1.0);

  if (should_play && priv->playback == NULL) {
    priv->playback = swfdec_playback_open (SWFDEC_PLAYER (player),
	g_main_context_default ());
  } else if (!should_play && priv->playback != NULL) {
    swfdec_playback_close (priv->playback);
    priv->playback = NULL;
  }
}

/**
 * swfdec_fb_player_set_playing:
 * @player: a #SwfdecFbPlayer
 * @playing: if the player should play or not
 *
 * Sets if @player should be playing or not. If the player is playing, a timer 
 * will be attached to the default main loop that periodically calls 
 * swfdec_player_advance().
 **/
void
swfdec_fb_player_set_playing (SwfdecFbPlayer *player, gboolean playing)
{
  SwfdecFbPlayerPrivate *priv;

  g_return_if_fail (SWFDEC_IS_FB_PLAYER (player));

  priv = player->priv;
  if (playing && priv->source == NULL) {
    priv->source = swfdec_iterate_source_new (SWFDEC_PLAYER (player), priv->speed);
    g_source_set_priority (priv->source, SWFDEC_FB_PRIORITY_ITERATE);
    g_source_attach (priv->source, NULL);
  } else if (!playing && priv->source != NULL) {
    g_source_destroy (priv->source);
    g_source_unref (priv->source);
    priv->source = NULL;
  } else {
    return;
  }
  priv->needs_resume = FALSE;
  swfdec_fb_player_update_audio (player);
  g_object_notify (G_OBJECT (player), "playing");
}

/**
 * swfdec_fb_player_get_playing:
 * @player: a #SwfdecFbPlayer
 *
 * Queries if the player is playing.
 *
 * Returns: %TRUE if the player is playing
 **/
gboolean
swfdec_fb_player_get_playing (SwfdecFbPlayer *player)
{
  g_return_val_if_fail (SWFDEC_IS_FB_PLAYER (player), FALSE);

  return player->priv->source != NULL;
}

/**
 * swfdec_fb_player_set_audio_enabled:
 * @player: a #SwfdecFbPlayer
 * @enabled: %TRUE to enable audio
 *
 * Enables or disables automatic audio playback.
 **/
void
swfdec_fb_player_set_audio_enabled (SwfdecFbPlayer *player, gboolean enabled)
{
  g_return_if_fail (SWFDEC_IS_FB_PLAYER (player));

  if (player->priv->audio_enabled == enabled)
    return;
  player->priv->audio_enabled = enabled;
  swfdec_fb_player_update_audio (player);
  g_object_notify (G_OBJECT (player), "audio-enabled");
}

/**
 * swfdec_fb_player_get_audio_enabled:
 * @player: a #SwfdecFbPlayer
 *
 * Queries if audio playback for @player is enabled or not.
 *
 * Returns: %TRUE if audio playback is enabled
 **/
gboolean
swfdec_fb_player_get_audio_enabled (SwfdecFbPlayer *player)
{
  g_return_val_if_fail (SWFDEC_IS_FB_PLAYER (player), FALSE);

  return player->priv->audio_enabled;
}

/**
 * swfdec_fb_player_set_speed:
 * @player: a #SwfdecFbPlayer
 * @speed: the new playback speed
 *
 * Sets the new playback speed. 1.0 means the default speed, bigger values
 * make playback happen faster. Audio will only play back if the speed is 
 * 1.0. Note that if the player is not playing when calling this function,
 * it will not automatically start.
 **/
void
swfdec_fb_player_set_speed (SwfdecFbPlayer *player, double speed)
{
  g_return_if_fail (SWFDEC_IS_FB_PLAYER (player));
  g_return_if_fail (speed > 0.0);

  player->priv->speed = speed;
  swfdec_fb_player_update_audio (player);
  if (player->priv->source)
    swfdec_iterate_source_set_speed (player->priv->source, player->priv->speed);
  g_object_notify (G_OBJECT (player), "speed");
}

/**
 * swfdec_fb_player_get_speed:
 * @player: a #SwfdecFbPlayer
 *
 * Queries the current playback speed. If the player is currently paused, it
 * will report the speed that it would use if playing.
 *
 * Returns: the current playback speed.
 **/
double
swfdec_fb_player_get_speed (SwfdecFbPlayer *player)
{
  g_return_val_if_fail (SWFDEC_IS_FB_PLAYER (player), FALSE);

  return player->priv->speed;
}

/**
 * swfdec_fb_player_set_missing_plugins_window:
 * @player: a #SwfdecFbPlayer
 * @window: the window to use for popping up codec install dialogs or %NULL
 *
 * Sets a given #GdkWindow to be used as the reference window when popping up
 * automatic codec install dialogs. Automatic codec install happens when Swfdec
 * cannot find a GStreamer plugin to play back a given media file. The player
 * will automaticcaly pause when this happens to allow the plugin install to 
 * finish and will resume playback after the codec install has completed.
 * You can use %NULL to disable this feature. It is disable by default.
 * Note that this function takes a #GdkWindow, not a #FbWindow, as its 
 * argument. This makes it slightly more inconvenient to use, as you need to 
 * call fb_widget_show() on your #FbWindow before having access to its 
 * #GdkWindow, but it allows automatic plugin installatio even when your 
 * application does not have a window. You can use gdk_get_default_root_window()
 * to obtain a default window in that case.
 * For details about automatic codec install, see the GStreamer documentation,
 * in particular the function gst_install_plugins_async(). If Swfdec was 
 * compiled without GStreamer support, this function will have no effect.
 **/
void
swfdec_fb_player_set_missing_plugins_window (SwfdecFbPlayer *player,
    GObject *window)
{
  SwfdecFbPlayerPrivate *priv;

  g_return_if_fail (SWFDEC_IS_FB_PLAYER (player));
  g_return_if_fail (window == NULL);

  priv = player->priv;
  g_object_notify (G_OBJECT (player), "missing-plugins-window");
}

/**
 * swfdec_fb_player_get_missing_plugins_window:
 * @player: a #SwfdecPlayer
 *
 * Gets the window used for automatic codec install. See 
 * swfdec_fb_player_set_missing_plugins_window() for details.
 *
 * Returns: the #GdkWindow used as parent for showing missing plugin dialogs or
 *          %NULL if automatic codec install is disabled.
 **/
GObject *
swfdec_fb_player_get_missing_plugins_window (SwfdecFbPlayer *player)
{
  g_return_val_if_fail (SWFDEC_IS_FB_PLAYER (player), NULL);

  return NULL;
}
