/* Swfdec
 * Copyright (C) 2007-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 <math.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "swfdec_net_stream.h"
#include "swfdec_access.h"
#include "swfdec_amf.h"
#include "swfdec_as_frame_internal.h"
#include "swfdec_as_internal.h"
#include "swfdec_as_strings.h"
#include "swfdec_cached_video.h"
#include "swfdec_debug.h"
#include "swfdec_loader_internal.h"
#include "swfdec_player_internal.h"
#include "swfdec_renderer_internal.h"
#include "swfdec_resource.h"
#include "swfdec_sandbox.h"
#include "swfdec_stream_target.h"
#include "swfdec_video_provider.h"
#include "swfdec_nmp.h"

/* NB: code and level must be rooted gc-strings */
static void
swfdec_net_stream_onstatus (SwfdecNetStream *stream, const char *code, const char *level)
{
  SwfdecAsValue val;
  SwfdecAsObject *object;
  SwfdecAsContext *cx;

  cx = swfdec_gc_object_get_context (stream);
  swfdec_sandbox_use (stream->sandbox);
  object = swfdec_as_object_new (cx, SWFDEC_AS_STR_Object, NULL);
  SWFDEC_INFO ("emitting onStatus for %s %s", level, code);
  g_printerr("emitting onStatus for %s %s\n", level, code);
  SWFDEC_AS_VALUE_SET_STRING (&val, code);
  swfdec_as_object_set_variable (object, SWFDEC_AS_STR_code, &val);
  SWFDEC_AS_VALUE_SET_STRING (&val, level);
  swfdec_as_object_set_variable (object, SWFDEC_AS_STR_level, &val);

  SWFDEC_AS_VALUE_SET_OBJECT (&val, object);
  if (!swfdec_as_relay_call (SWFDEC_AS_RELAY (stream),
        SWFDEC_AS_STR_onStatus, 1, &val, NULL)) {
    // if it's an error message and the stream object didn't have onStatus
    // handler, call System.onStatus
    if (level == SWFDEC_AS_STR_error) {
      SwfdecAsValue system;

      swfdec_as_object_get_variable (cx->global,
          SWFDEC_AS_STR_System, &system);
      if (SWFDEC_AS_VALUE_IS_COMPOSITE (system) &&
	  (object = SWFDEC_AS_VALUE_GET_COMPOSITE (system)) != NULL) {
        swfdec_as_object_call (object, SWFDEC_AS_STR_onStatus, 1, &val, NULL);
      }
    }
  }
  swfdec_sandbox_unuse (stream->sandbox);
}

static void
swfdec_net_stream_decode_video (SwfdecVideoDecoder *decoder, SwfdecBuffer *buffer)
{
}

static void swfdec_net_stream_update_playing (SwfdecNetStream *stream);
static void
swfdec_net_stream_video_goto (SwfdecNetStream *stream, guint timestamp)
{
  SwfdecBuffer *buffer;
  guint format;
  cairo_surface_t *old;
  gboolean process_events;
  guint process_events_from;

  // nmp
  return ;

  SWFDEC_LOG ("goto %ums", timestamp);
  process_events = timestamp == stream->next_time;
  process_events_from = MIN (stream->next_time, stream->current_time + 1);
  old = stream->surface;
  if (stream->surface) {
    cairo_surface_destroy (stream->surface);
    stream->surface = NULL;
  }
  if (stream->flvdecoder == NULL)
    return;
  if (stream->flvdecoder->video) {
    buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, timestamp,
	FALSE, &format, &stream->current_time, &stream->next_time);
  } else {
    buffer = NULL;
  }
  if (buffer == NULL) {
    SWFDEC_ERROR ("got no buffer - no video available?");
  } else {
    guint next;

    if (stream->decoder != NULL &&
	stream->decoder_time >= stream->current_time) {
      buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
	  stream->current_time, TRUE, &format, &stream->decoder_time,
	  &next);
    }

    if (stream->decoder == NULL) {
      buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
	  stream->current_time, TRUE, &format, &stream->decoder_time,
	  &next);
      stream->decoder = swfdec_video_decoder_new (format);
    } else {
      swfdec_flv_decoder_get_video (stream->flvdecoder, 
	  stream->decoder_time, FALSE, NULL, NULL, &next);
      if (next != stream->current_time) {
	guint key_time, key_next;
	buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
	    stream->current_time, TRUE, &format, &key_time, &key_next);
	if (key_time > stream->decoder_time) {
	  stream->decoder_time = key_time;
	  next = key_next;
	} else {
	  buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
	      next, FALSE, &format, &stream->decoder_time,
	      &next);
	}
      } else {
	buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
	    next, FALSE, &format, &stream->decoder_time,
	    &next);
      }
    }

    /* the following things hold:
     * buffer: next buffer to decode
     * format: format of that buffer
     * stream->decoder_time: timestamp of buffer to decode
     * stream->decoder: non-null, using stream->format
     */
    for (;;) {
      if (format != swfdec_video_decoder_get_codec (stream->decoder)) {
	g_object_unref (stream->decoder);
	stream->decoder = swfdec_video_decoder_new (format);
      }
      swfdec_net_stream_decode_video (stream->decoder, buffer);
      if (stream->decoder_time >= stream->current_time)
	break;

      buffer = swfdec_flv_decoder_get_video (stream->flvdecoder,
	  next, FALSE, &format, &stream->decoder_time, &next);
    }

    swfdec_video_provider_new_image (SWFDEC_VIDEO_PROVIDER (stream));
  }
  if (stream->next_time <= stream->current_time) {
    if (swfdec_flv_decoder_is_eof (stream->flvdecoder)) {
      swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Play_Stop, SWFDEC_AS_STR_status);
    } else {
      stream->buffering = TRUE;
      swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Empty,
	  SWFDEC_AS_STR_status);
    }
    swfdec_net_stream_update_playing (stream);
  }
  if (process_events) {
    while (stream->flvdecoder && process_events_from <= stream->current_time) {
      SwfdecAsValue name, value;
      SwfdecBits bits;
      SwfdecBuffer *event = swfdec_flv_decoder_get_data (stream->flvdecoder, process_events_from, &process_events_from);
      if (!event)
	break;
      SWFDEC_LOG ("processing event from timestamp %u", process_events_from);
      process_events_from++; /* increase so we get the next event next time */
      swfdec_bits_init (&bits, event);
      swfdec_sandbox_use (stream->sandbox);
      if (swfdec_amf_parse (swfdec_gc_object_get_context (stream), &bits, 2, 
	    SWFDEC_AMF_STRING, &name, SWFDEC_AMF_MIXED_ARRAY, &value) != 2) {
	SWFDEC_ERROR ("could not parse data tag");
      } else {
	swfdec_as_relay_call (SWFDEC_AS_RELAY (stream), 
	    SWFDEC_AS_VALUE_GET_STRING (name), 1, &value, NULL);
      }
      swfdec_sandbox_unuse (stream->sandbox);
    }
  }
}

static void
swfdec_net_stream_exectag(SwfdecNetStream *stream, char *buf, int size)
{
  SwfdecAsValue name, value;
  SwfdecBits bits;
  SwfdecBuffer *buffer = NULL;
  buffer = swfdec_buffer_new (size);
  if (buffer == NULL)
	return;
  memcpy (buffer->data, buf, size);
  swfdec_bits_init (&bits, buffer);
  swfdec_sandbox_use (stream->sandbox);
  if (swfdec_amf_parse (swfdec_gc_object_get_context (stream), &bits, 2,
  		SWFDEC_AMF_STRING, &name, SWFDEC_AMF_MIXED_ARRAY, &value) != 2) {
  	SWFDEC_ERROR ("could not parse data tag");
  } else {
  	swfdec_as_relay_call (SWFDEC_AS_RELAY (stream),
  	SWFDEC_AS_VALUE_GET_STRING (name), 1, &value, NULL);
  }
  swfdec_sandbox_unuse (stream->sandbox);
  swfdec_buffer_unref (buffer);
}

static void
swfdec_net_stream_timeout (SwfdecTimeout *timeout)
{
  SwfdecNetStream *stream = SWFDEC_NET_STREAM ((void *) ((guchar *) timeout - G_STRUCT_OFFSET (SwfdecNetStream, timeout)));
  SwfdecTick timestamp;

  SWFDEC_LOG ("timeout fired");
  if (stream->failflag)
  {
	swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Full,
		SWFDEC_AS_STR_status);
	swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Play_Stop,
		SWFDEC_AS_STR_status);
	swfdec_video_provider_new_image (SWFDEC_VIDEO_PROVIDER (stream));
	return;
  }
  else if (stream->flag == 0)
  {
	swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Play_Start,
		SWFDEC_AS_STR_status);
	swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Empty,
		SWFDEC_AS_STR_status);
	swfdec_video_provider_new_image (SWFDEC_VIDEO_PROVIDER (stream));
	stream->flag = 1;
  }
  else if (stream->flag == 1)
  {
	if (swfdec_nmp_get_status(stream->_mp) >= MP_OPENED)
	{
	  char *buf = NULL;
	  int size = 0;
	  if (swfdec_nmp_get_metadata(stream->_mp, &buf, &size) == 0
		&& buf != NULL && size > 0)
	  {
		swfdec_nmp_get_starttime(stream->_mp, buf, size, &stream->start_time);
	  	swfdec_net_stream_exectag(stream, buf, size);
	  }
	  stream->flag = 2;
	}
  }
  else if (stream->flag == 2)
  {
	MPSTATUS status;
	status = swfdec_nmp_get_status(stream->_mp);
	switch (status)
	{
	case MP_PB_PLAYING:
	  swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Full,
		SWFDEC_AS_STR_status);
	  stream->flag = 3;
	  break;
	case MP_PB_FINISHED:
	  swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Full,
		SWFDEC_AS_STR_status);
	  swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Play_Stop,
		SWFDEC_AS_STR_status);
	  swfdec_net_stream_set_playing(stream, FALSE);
	  return;
	default:
		break;
	}
  }
  else if (stream->flag == 3)
  {
	MPSTATUS status;
	status = swfdec_nmp_get_status(stream->_mp);
	switch (status)
	{
	case MP_PB_BUFFERING:
	  swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Empty,
		SWFDEC_AS_STR_status);
	  stream->flag = 2;
	  break;
	case MP_PB_FINISHED:
	  swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Full,
		SWFDEC_AS_STR_status);
	  swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Play_Stop,
		SWFDEC_AS_STR_status);
	  swfdec_net_stream_set_playing(stream, FALSE);
	  return;
	default:
		break;
	}
  }
  if (stream->flag > 1 
		&& stream->current_time - stream->prev_time >= 500)
  {
	int current_time = stream->current_time;
	char *buf = NULL;
	int size = 0;
	if (swfdec_nmp_set_starttime(stream->_mp, &buf, &size, current_time) == 0
		&& buf != NULL && size > 0)
	{
	  swfdec_net_stream_exectag(stream, buf, size);
	  free(buf);
	  stream->prev_time = current_time;
	}
  }
  timestamp = stream->timeout.timestamp;
  if (stream->timeout.timestamp == timestamp &&
      stream->timeout.callback) {
    SWFDEC_LOG ("readding timeout");
    stream->timeout.timestamp += SWFDEC_MSECS_TO_TICKS (100);
    swfdec_player_add_timeout (SWFDEC_PLAYER (swfdec_gc_object_get_context (stream)), &stream->timeout);
  } 
}

static void
swfdec_net_stream_update_playing (SwfdecNetStream *stream)
{
  SwfdecPlayer *player = SWFDEC_PLAYER (swfdec_gc_object_get_context (stream));
  gboolean should_play;
   
#if 0    
  should_play = stream->playing; /* checks user-set play/pause */
  should_play &= !stream->buffering; /* checks enough data is available */
  should_play &= stream->flvdecoder != NULL; /* checks there even is something to play */
  should_play &= stream->next_time > stream->current_time; /* checks if EOF */
#endif
  should_play = stream->playing;  
  if (should_play && stream->timeout.callback == NULL) {
    SWFDEC_DEBUG ("starting playback");
    stream->timeout.callback = swfdec_net_stream_timeout;
    stream->timeout.timestamp = player->priv->time + SWFDEC_MSECS_TO_TICKS (stream->next_time - stream->current_time);
    swfdec_player_add_timeout (player, &stream->timeout);
  } else if (!should_play && stream->timeout.callback != NULL) {
    /* FIXME: timeout might or might not be be added here if
     * timestamp == player->priv->time, but we'll just remove to be sure */
    swfdec_player_remove_timeout (player, &stream->timeout);
    stream->timeout.callback = NULL;
    SWFDEC_DEBUG ("stopping playback");
  }
}

/*** SWFDEC_STREAM_TARGET interface ***/

static SwfdecPlayer *
swfdec_net_stream_stream_target_get_player (SwfdecStreamTarget *target)
{
  return SWFDEC_PLAYER (swfdec_gc_object_get_context (target));
}

static void
swfdec_net_stream_stream_target_error (SwfdecStreamTarget *target, 
    SwfdecStream *stream)
{
  SwfdecNetStream *ns = SWFDEC_NET_STREAM (target);

#if 0
  if (ns->flvdecoder == NULL)
    swfdec_net_stream_onstatus (ns, SWFDEC_AS_STR_NetStream_Play_StreamNotFound,
	SWFDEC_AS_STR_error);
#else
  glong loaded = swfdec_loader_get_loaded (ns->loader);
  if (loaded == 0 && ns->flag < 2)
    swfdec_net_stream_onstatus (ns, SWFDEC_AS_STR_NetStream_Play_StreamNotFound,
	SWFDEC_AS_STR_error);
#endif
}

#if 0
static void
swfdec_net_stream_stream_target_recheck (SwfdecNetStream *stream)
{
  if (stream->buffering) {
    guint first, last;
    if (swfdec_flv_decoder_get_video_info (stream->flvdecoder, &first, &last)) {
      guint current = MAX (first, stream->current_time);
      if (current + stream->buffer_time <= last) {
	swfdec_net_stream_video_goto (stream, current);
	stream->buffering = FALSE;
	swfdec_net_stream_onstatus (stream, SWFDEC_AS_STR_NetStream_Buffer_Full,
	    SWFDEC_AS_STR_status);
      }
    } else {
      SWFDEC_ERROR ("no video stream, how do we update buffering?");
    }
  }
  swfdec_net_stream_update_playing (stream);
}
#endif

static gboolean
swfdec_net_stream_stream_target_parse (SwfdecStreamTarget *target, 
    SwfdecStream *stream)
{
#if 0
  SwfdecNetStream *ns = SWFDEC_NET_STREAM (target);
  SwfdecBufferQueue *queue;
  SwfdecStatus status;
  
  if (ns->flvdecoder == NULL) {
    /* FIXME: add mp3 support */
    ns->flvdecoder = g_object_new (SWFDEC_TYPE_FLV_DECODER, NULL);
    g_signal_connect_swapped (ns->flvdecoder, "missing-plugin", 
	G_CALLBACK (swfdec_player_add_missing_plugin), swfdec_gc_object_get_context (ns));
    swfdec_net_stream_onstatus (ns, SWFDEC_AS_STR_NetStream_Play_Start,
	SWFDEC_AS_STR_status);
    swfdec_loader_set_data_type (SWFDEC_LOADER (stream), SWFDEC_LOADER_DATA_FLV);
  }

  status = SWFDEC_STATUS_OK;
  queue = swfdec_stream_get_queue (stream);
  do {
    SwfdecBuffer *buffer = swfdec_buffer_queue_pull_buffer (queue);
    if (buffer == NULL)
      break;
    status &= ~SWFDEC_STATUS_NEEDBITS;
    status |= swfdec_decoder_parse (SWFDEC_DECODER (ns->flvdecoder), buffer);	
	swfdec_buffer_unref(buffer);
  } while ((status & (SWFDEC_STATUS_ERROR | SWFDEC_STATUS_EOF | SWFDEC_STATUS_INIT)) == 0);

  if (status & SWFDEC_STATUS_INIT)
    return TRUE;

  if (status & SWFDEC_STATUS_IMAGE)
    swfdec_net_stream_stream_target_recheck (ns);
  return FALSE;
#else
  return FALSE;
#endif
}

static void
swfdec_net_stream_stream_target_close (SwfdecStreamTarget *target, 
    SwfdecStream *stream)
{
#if 0
  SwfdecNetStream *ns = SWFDEC_NET_STREAM (target);
  guint first, last;

  swfdec_net_stream_onstatus (ns, SWFDEC_AS_STR_NetStream_Buffer_Flush,
      SWFDEC_AS_STR_status);
  swfdec_net_stream_video_goto (ns, ns->current_time);
  ns->buffering = FALSE;
  if (swfdec_flv_decoder_get_video_info (ns->flvdecoder, &first, &last) &&
      ns->current_time + ns->buffer_time <= last) {
    swfdec_net_stream_onstatus (ns, SWFDEC_AS_STR_NetStream_Buffer_Full,
	SWFDEC_AS_STR_status);
  }
  swfdec_net_stream_stream_target_recheck (ns);
#endif
}

static void
swfdec_net_stream_stream_target_init (SwfdecStreamTargetInterface *iface)
{
  iface->get_player = swfdec_net_stream_stream_target_get_player;
  iface->parse = swfdec_net_stream_stream_target_parse;
  iface->close = swfdec_net_stream_stream_target_close;
  iface->error = swfdec_net_stream_stream_target_error;
}

/*** SWFDEC VIDEO PROVIDER ***/

static cairo_surface_t *
swfdec_net_stream_video_provider_get_image (SwfdecVideoProvider *provider,
    SwfdecRenderer *renderer, guint *width, guint *height)
{
  SwfdecNetStream *stream = SWFDEC_NET_STREAM (provider);
  SwfdecCachedVideo *cached;
  cairo_surface_t *surface;

  cached = SWFDEC_CACHED_VIDEO (swfdec_renderer_get_cache (renderer, stream, NULL, NULL));
  if (cached != NULL && swfdec_cached_video_get_frame (cached) == stream->current_time) {
    swfdec_cached_use (SWFDEC_CACHED (cached));
    swfdec_cached_video_get_size (cached, width, height);
    return swfdec_cached_video_get_surface (cached);
  }

  if (stream->decoder == NULL)
    return NULL;

  surface = swfdec_video_decoder_get_image (stream->decoder, renderer);
  if (surface == NULL)
    return NULL;
  *width = swfdec_video_decoder_get_width (stream->decoder);
  *height = swfdec_video_decoder_get_height (stream->decoder);
  cached = swfdec_cached_video_new (surface, *width * *height * 4);
  swfdec_cached_video_set_frame (cached, stream->decoder_time);
  swfdec_cached_video_set_size (cached, *width, *height);
  swfdec_renderer_add_cache (renderer, TRUE, stream, SWFDEC_CACHED (cached));
  g_object_unref (cached);

  return surface;
}

static void
swfdec_net_stream_video_provider_get_size (SwfdecVideoProvider *provider,
    guint *width, guint *height)
{
  SwfdecNetStream *stream = SWFDEC_NET_STREAM (provider);

  if (stream->decoder) {
    *width = swfdec_video_decoder_get_width (stream->decoder);
    *height = swfdec_video_decoder_get_height (stream->decoder);
  } else {
    *width = 0;
    *height = 0;
  }
}

static void
swfdec_net_stream_video_provider_set_position (SwfdecVideoProvider *provider,
    guint x, guint y, guint w, guint h)
{
  SwfdecNetStream *stream = SWFDEC_NET_STREAM (provider);
  if (stream->_x == (int)x && stream->_y == (int)y
  		 && stream->_w == (int)w && stream->_h == (int)h)
  	return;
  stream->_x = x;
  stream->_y = y;
  stream->_w = w;
  stream->_h = h;
  swfdec_nmp_resize(stream->_mp, x, y, w, h); 
}

static void
swfdec_net_stream_video_provider_get_failflag (SwfdecVideoProvider *provider,
    guint *failflag)
{
  SwfdecNetStream *stream = SWFDEC_NET_STREAM (provider);

  if (stream->_mp) {
	*failflag = stream->failflag;
  } else {
	*failflag = 0;
  }
}

static void
swfdec_net_stream_video_provider_init (SwfdecVideoProviderInterface *iface)
{
  iface->get_image = swfdec_net_stream_video_provider_get_image;
  iface->get_size = swfdec_net_stream_video_provider_get_size;
  iface->set_position = swfdec_net_stream_video_provider_set_position;
  iface->get_failflag = swfdec_net_stream_video_provider_get_failflag;
}

/*** SWFDEC_NET_STREAM ***/

G_DEFINE_TYPE_WITH_CODE (SwfdecNetStream, swfdec_net_stream, SWFDEC_TYPE_AS_RELAY,
    G_IMPLEMENT_INTERFACE (SWFDEC_TYPE_STREAM_TARGET, swfdec_net_stream_stream_target_init)
    G_IMPLEMENT_INTERFACE (SWFDEC_TYPE_VIDEO_PROVIDER, swfdec_net_stream_video_provider_init))

static void
swfdec_net_stream_dispose (GObject *object)
{
  SwfdecNetStream *stream = SWFDEC_NET_STREAM (object);

  if (stream->_thread != NULL)
  {
	stream->_exit = 1;
	g_thread_join (stream->_thread);
	stream->_thread = NULL;
  }
  swfdec_nmp_destructor(&stream->_mp);
  swfdec_net_stream_set_playing (stream, FALSE);
  if (stream->surface) {
    cairo_surface_destroy (stream->surface);
    stream->surface = NULL;
  }
  if (stream->decoder) {
    g_object_unref (stream->decoder);
    stream->decoder = NULL;
  }
  swfdec_net_stream_set_loader (stream, NULL);
  g_assert (stream->movies == NULL);
  g_free (stream->requested_url);
  stream->requested_url = NULL;
  
  if (stream->timeout.callback != NULL)
  {
	SwfdecPlayer *player = SWFDEC_PLAYER (swfdec_gc_object_get_context (stream));
	swfdec_player_remove_timeout (player, &stream->timeout);
	stream->timeout.callback = NULL;
  }

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

static void
swfdec_net_stream_mark (SwfdecGcObject *object)
{
  SwfdecNetStream *stream = SWFDEC_NET_STREAM (object);

  if (stream->conn)
    swfdec_gc_object_mark (stream->conn);
  if (stream->sandbox)
    swfdec_gc_object_mark (stream->sandbox);

  SWFDEC_GC_OBJECT_CLASS (swfdec_net_stream_parent_class)->mark (object);
}

static void
swfdec_net_stream_class_init (SwfdecNetStreamClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  SwfdecGcObjectClass *gc_class = SWFDEC_GC_OBJECT_CLASS (klass);

  object_class->dispose = swfdec_net_stream_dispose;

  gc_class->mark = swfdec_net_stream_mark;
}

static void
swfdec_net_stream_init (SwfdecNetStream *stream)
{
  stream->buffer_time = 100; /* msecs */
  stream->on_off = FALSE;
  stream->flag = 0;
  stream->failflag = 0;
  /* NMP */
  swfdec_nmp_constructor(&stream->_mp);
  stream->_x = stream->_y = stream->_w = stream->_h = 0;
  stream->_exit = 1;
  stream->_thread = NULL;
}

static gpointer
player_thread (gpointer data)
{
  SwfdecNetStream *stream = data;
  int ret = 0;

  ret = swfdec_nmp_open(stream->_mp, &stream->_exit);
  if (ret != 0 && swfdec_nmp_is_fail(stream->_mp))
  {
	stream->failflag = 1;
  }
  else
  {
	stream->_byterate = swfdec_nmp_get_bitrate(stream->_mp) / 8;
  }
  g_printerr("_byterate = %lf\n", stream->_byterate);
  while (!stream->_exit && 
	stream->_x < 0 && stream->_y < 0 && stream->_w <= 0 && stream->_h <= 0)
	usleep(50000);
  if (!stream->_exit)
  {
  	ret = swfdec_nmp_play((void *)stream->_mp,
 		stream->_x, stream->_y, stream->_w, stream->_h, 
		stream->buffer_time, &stream->_exit);		
  }
  while (!stream->_exit)
  {
	stream->current_time = swfdec_nmp_get_pts(stream->_mp);
	//g_printerr("pts = %u\n", stream->current_time);
  	usleep(500000);
  }
  ret = swfdec_nmp_close((void *)stream->_mp);
  return NULL;
}

static void
swfdec_net_stream_load (SwfdecPlayer *player, gboolean allowed, gpointer streamp)
{
  SwfdecNetStream *stream = streamp;
  SwfdecLoader *loader;  
  GError *err = NULL;

  if (allowed) {
/*	swfdec_net_stream_set_loader (stream, NULL); */
    loader = swfdec_player_load2 (player, stream->requested_url, NULL);
    SWFDEC_FIXME ("-----------swfdec_net_stream_load %s",stream->requested_url);
    swfdec_net_stream_set_loader (stream, loader);
	stream->_exit = 0;
	stream->_thread = g_thread_create (player_thread, stream, TRUE, &err);
    g_object_unref (loader);
  } else {
    SWFDEC_WARNING ("SECURITY: no access to %s from NetStream",
	stream->requested_url);
    stream->sandbox = NULL;
  }
  g_free (stream->requested_url);
  stream->requested_url = NULL;
}

// When checkPolicyFile is true the YES values in last column must be changed
// to POLICY
#if 0
static const SwfdecAccessMatrix swfdec_net_stream_matrix = {
  { SWFDEC_ACCESS_NO,  SWFDEC_ACCESS_NO,  SWFDEC_ACCESS_NO },
  { SWFDEC_ACCESS_NO,  SWFDEC_ACCESS_YES, SWFDEC_ACCESS_YES },
  { SWFDEC_ACCESS_YES, SWFDEC_ACCESS_NO,  SWFDEC_ACCESS_NO },
  { SWFDEC_ACCESS_NO,  SWFDEC_ACCESS_NO,  SWFDEC_ACCESS_YES },
  { SWFDEC_ACCESS_YES, SWFDEC_ACCESS_NO,  SWFDEC_ACCESS_YES }
};
#else
static const SwfdecAccessMatrix swfdec_net_stream_matrix = {
  { SWFDEC_ACCESS_YES,  SWFDEC_ACCESS_YES,  SWFDEC_ACCESS_YES },
  { SWFDEC_ACCESS_YES,  SWFDEC_ACCESS_YES,  SWFDEC_ACCESS_YES },
  { SWFDEC_ACCESS_YES, 	SWFDEC_ACCESS_YES,  SWFDEC_ACCESS_YES },
  { SWFDEC_ACCESS_YES,  SWFDEC_ACCESS_YES,  SWFDEC_ACCESS_YES },
  { SWFDEC_ACCESS_YES, 	SWFDEC_ACCESS_YES,  SWFDEC_ACCESS_YES }
};
#endif

void
swfdec_net_stream_set_url (SwfdecNetStream *stream, const char *url_string)
{
  SwfdecPlayer *player;
  SwfdecAsContext *cx;

  g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));
  g_return_if_fail (url_string != NULL);

  cx = swfdec_gc_object_get_context (stream);
  player = SWFDEC_PLAYER (cx);
  /* close first */
  swfdec_net_stream_set_loader(stream, NULL);
  swfdec_net_stream_set_playing(stream, FALSE);
  stream->start_time = 0;
  stream->current_time = 0;
  stream->prev_time = 0;
  stream->on_off = FALSE;
  stream->flag = 0;
  stream->failflag = 0;
  stream->_byterate = 320000.0/8;

  if (stream->requested_url != NULL) {
    SWFDEC_FIXME ("can't load %s - already loading %s, what now?", 
	url_string, stream->requested_url);
    return;
  }
  stream->requested_url = g_strdup (url_string);
  stream->sandbox = swfdec_sandbox_get (player);
  swfdec_player_allow_by_matrix (player, stream->sandbox, url_string, 
    swfdec_net_stream_matrix, swfdec_net_stream_load, stream);
}

void
swfdec_net_stream_set_loader (SwfdecNetStream *stream, SwfdecLoader *loader)
{
  g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));
  g_return_if_fail (loader == NULL || SWFDEC_IS_SANDBOX (stream->sandbox));
  g_return_if_fail (loader == NULL || SWFDEC_IS_LOADER (loader));

  if (stream->loader) {
	if (stream->_thread != NULL)
	{
  	  stream->_exit = 1;
  	  g_thread_join (stream->_thread);
  	  stream->_thread = NULL;
	}
  	stream->failflag = 0;
    SwfdecStream *lstream = SWFDEC_STREAM (stream->loader);
    swfdec_stream_ensure_closed (lstream);
    swfdec_stream_set_target (lstream, NULL);
    g_object_unref (lstream);	
  }
  if (stream->flvdecoder) {
    g_signal_handlers_disconnect_by_func (stream->flvdecoder,
	  swfdec_player_add_missing_plugin, swfdec_gc_object_get_context (stream));
    g_object_unref (stream->flvdecoder);
    stream->flvdecoder = NULL;
  }
  stream->loader = loader;
  stream->buffering = TRUE;
  if (loader) {
    g_object_ref (loader);
    swfdec_stream_set_target (SWFDEC_STREAM (loader), SWFDEC_STREAM_TARGET (stream));
	swfdec_net_stream_set_playing (stream, TRUE);
  }
//swfdec_net_stream_set_playing (stream, TRUE);
}

void
swfdec_net_stream_set_playing (SwfdecNetStream *stream, gboolean playing)
{
  g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));

  stream->playing = playing;

  swfdec_net_stream_update_playing (stream);
}

gboolean
swfdec_net_stream_get_playing (SwfdecNetStream *stream)
{
  g_return_val_if_fail (SWFDEC_IS_NET_STREAM (stream), FALSE);

  return stream->playing;
}

void
swfdec_net_stream_set_buffer_time (SwfdecNetStream *stream, double secs)
{
  g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));

  /* FIXME: is this correct? */
  if (secs <= 0)
    return;

  stream->buffer_time = secs * 1000;
}

double
swfdec_net_stream_get_buffer_time (SwfdecNetStream *stream)
{
  g_return_val_if_fail (SWFDEC_IS_NET_STREAM (stream), 0.1);

  return (double) stream->buffer_time / 1000.0;
}

void
swfdec_net_stream_seek (SwfdecNetStream *stream, double secs)
{
  guint first, last, msecs;

  g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));

  if (stream->flvdecoder == NULL)
    return;
  if (!isfinite (secs) || secs < 0) {
    SWFDEC_ERROR ("seeking to %g doesn't work", secs);
    return;
  }
  if (!swfdec_flv_decoder_get_video_info (stream->flvdecoder, &first, &last)) {
    SWFDEC_ERROR ("FIXME: implement seeking in audio only NetStream");
    return;
  }
  msecs = secs * 1000;
  msecs += first;
  if (msecs > last)
    msecs = last;
  swfdec_flv_decoder_get_video (stream->flvdecoder, msecs, TRUE, NULL, &msecs, NULL);
  swfdec_net_stream_video_goto (stream, msecs);
}

void
swfdec_net_stream_nmp_pause (SwfdecNetStream *stream, gboolean on_off)
{
  g_return_if_fail (SWFDEC_IS_NET_STREAM (stream));
  if (on_off != stream->on_off 
		&& stream->_mp != NULL)
  {
	swfdec_nmp_pause(stream->_mp, on_off);
	stream->on_off = on_off;
  }
}

guint
swfdec_net_stream_nmp_buffer_length (SwfdecNetStream *stream)
{
  gulong loaded = swfdec_loader_get_loaded (stream->loader);
  guint len = loaded / stream->_byterate * 1000.0 - (stream->current_time - stream->start_time);
  g_printerr("ByteLoaded = %lu, bufferLength = %u\n", loaded, len);
  return len;
}

gulong
swfdec_net_stream_get_loaded (SwfdecNetStream *stream)
{
  glong loaded = swfdec_loader_get_loaded (stream->loader);
  glong loaded_bt = stream->_byterate * stream->buffer_time / 1000.0;
  loaded -= stream->_byterate * (stream->prev_time - stream->start_time) / 1000.0;
  if (loaded < 0)
  	return 0;
  if (loaded > loaded_bt)
  {
	loaded = loaded_bt;
  }
  //g_printerr("ByteLoaded(mod) = %ld,\n", loaded);
  return (gulong)loaded;
}

