/* 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 <libsoup/soup.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "swfdec_fb_loader2.h"
#include "ipcchannel.h"

/*** FB-DOC ***/

/**
 * SECTION:FbExtensions
 * @title: Fb extensions
 * @short_description: extension objects for @SwfdecFbPlayer
 *
 * Swfdec-Fb provides some objects that implement the various extensions
 * provided by the @SwfdecPlayer object. They are used by default in the 
 * #SwfdecFbPlayer object, but you can use them outside of it, too. 
 * However, they require certain Fb functionality, such as a running glib
 * main loop.
 */

/**
 * SwfdecFbLoader2:
 *
 * #SwfdecFbLoader2 is a #SwfdecLoader2 that is intended as an easy way to be 
 * access ressources that are not stored in files, such as HTTP. It is a 
 * private object.
 */

struct _SwfdecFbLoader2
{
  SwfdecLoader		loader;

  SoupMessage *		message;	/* the message we're sending */
  gboolean		opened;		/* set after first bytes of data have arrived */
  gboolean		running;
  GThread *		thread;
  gulong		loaded;
  gulong		prefetch;  
};

struct _SwfdecFbLoader2Class {
  SwfdecLoaderClass	loader_class;

  SoupSession *		session;	/* the session used by the loader2 */
  GMainContext *	async_context;
  GMainLoop *		loop;
};

/*** SwfdecFbLoader2 ***/

G_DEFINE_TYPE (SwfdecFbLoader2, swfdec_fb_loader2, SWFDEC_TYPE_FILE_LOADER)

static void
swfdec_fb_loader2_ensure_open (SwfdecFbLoader2 *fb)
{
  char *real_uri;

  if (fb->opened)
    return;

  real_uri = soup_uri_to_string (soup_message_get_uri (fb->message), FALSE);
  swfdec_loader_set_url (SWFDEC_LOADER (fb), real_uri);
  g_free (real_uri);
  if (soup_message_headers_get_encoding (fb->message->response_headers) == SOUP_ENCODING_CONTENT_LENGTH) {
    swfdec_loader_set_size (SWFDEC_LOADER (fb), 
	soup_message_headers_get_content_length (fb->message->response_headers));
  }
  swfdec_stream_open (SWFDEC_STREAM (fb));
  fb->opened = TRUE;
}

static void
swfdec_fb_loader2_push (SoupMessage *msg, SoupBuffer *chunk, gpointer loader)
{
  SwfdecFbLoader2 *fb = SWFDEC_FB_LOADER2 (loader);
  SwfdecFbLoader2Class *klass = SWFDEC_FB_LOADER2_GET_CLASS (fb);
  int ret = 0;

  g_printerr ("Pushing stream: loader2, size : %d, total = %lu, prefetch = %lu\n", 
  	chunk->length, fb->loaded, fb->prefetch);

  // don't save the http data when redirecting ...
  if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
  	g_printerr("SOUP_STATUS_IS_REDIRECTION\n");
	return;
  }
  
  if (!fb->running)
  {
    soup_session_cancel_message (klass->session, fb->message, SOUP_STATUS_CANCELLED);
	return;
  }
  swfdec_fb_loader2_ensure_open (fb);
  ret = ipcc_write((char *)chunk->data, chunk->length);
  fb->loaded += ret;
  if (fb->loaded - fb->prefetch >= 1024 * 256)
  {
	fb->prefetch += 1024 * 256;  
  	usleep(100);
  }
}
  
static void
swfdec_fb_loader2_finished (SoupMessage *msg, gpointer loader)
{
  g_printerr("loader2 : %u %s\n", msg->status_code, msg->reason_phrase);
  if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
    swfdec_fb_loader2_ensure_open (loader);
    swfdec_stream_close (loader);
	ipcc_seteof();
  } else {
    if (!SWFDEC_FB_LOADER2 (loader)->opened) {
      char *real_uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
      swfdec_loader_set_url (loader, real_uri);
      g_free (real_uri);
    }
    swfdec_stream_error (loader, "%u %s", msg->status_code, msg->reason_phrase);
	ipcc_seterror();
  }
}

static gpointer
loader_thread (gpointer data)
{
  SwfdecFbLoader2 *fb = data;
  SwfdecFbLoader2Class *klass = SWFDEC_FB_LOADER2_GET_CLASS (fb);

  GMainLoop *loop = klass->loop;
  g_object_ref (fb->message);
  soup_session_queue_message (klass->session, fb->message, NULL, NULL);
  g_main_loop_run(loop);
  if (!swfdec_stream_is_complete (SWFDEC_STREAM(fb)))
  {
    soup_session_cancel_message (klass->session, fb->message, SOUP_STATUS_CANCELLED);
    g_object_unref (fb->message);
    fb->message = NULL;
  }
  fb->running = FALSE;
  return NULL;
}

static void
swfdec_fb_loader2_dispose (GObject *object)
{
  SwfdecFbLoader2 *fb = SWFDEC_FB_LOADER2 (object);
  SwfdecFbLoader2Class *klass = SWFDEC_FB_LOADER2_GET_CLASS (fb);
  int ret = 0;

  fb->running = FALSE;
  ipcc_seterror();
  if (fb->thread != NULL)
  {
	if (g_main_loop_is_running (klass->loop))
		g_main_loop_quit (klass->loop);
	g_thread_join (fb->thread);
	fb->thread = NULL;
  }
  if (fb->message) {
    g_signal_handlers_disconnect_by_func (fb->message, swfdec_fb_loader2_push, fb);
    g_signal_handlers_disconnect_by_func (fb->message, swfdec_fb_loader2_finished, fb);
    g_object_unref (fb->message);
    fb->message = NULL;
  }
  if (klass->loop != NULL)
  {
  	g_main_loop_unref (klass->loop);
	klass->loop = NULL;
  }
  ret = ipcc_close(IPC_MODE_CLIENT);
  G_OBJECT_CLASS (swfdec_fb_loader2_parent_class)->dispose (object);
}

static void
swfdec_fb_loader2_load (SwfdecLoader *loader, SwfdecPlayer *player, 
    const char *url_string, SwfdecBuffer *buffer, guint n_headers,
    const char **header_names, const char **header_values)
{
  SwfdecURL *url;
  guint i;
  
  g_printerr ("Loading stream: loader2, %s\n", url_string);

  // test
  // return;

  if (swfdec_url_path_is_relative (url_string)) {
    url = swfdec_url_new_relative (swfdec_player_get_base_url (player), url_string);
  } else {
    url = swfdec_url_new (url_string);
  }

  if (url == NULL) {
    swfdec_stream_error (SWFDEC_STREAM (loader), "invalid URL %s", url_string);
    return;
  };
  if (!swfdec_url_has_protocol (url, "http") &&
      !swfdec_url_has_protocol (url, "https")) {
    SWFDEC_LOADER_CLASS (swfdec_fb_loader2_parent_class)->load (loader, player,
	url_string, buffer, n_headers, header_names, header_values);
  } else {
    SwfdecFbLoader2 *fb = SWFDEC_FB_LOADER2 (loader);
	GError *err = NULL;

	fb->loaded = 0;
	fb->prefetch = 0;
    fb->message = soup_message_new (buffer != NULL ? "POST" : "GET",
	swfdec_url_get_url (url));
    soup_message_set_flags (fb->message, SOUP_MESSAGE_OVERWRITE_CHUNKS);

    for (i = 0; i < n_headers; i++) {
      soup_message_headers_append (fb->message->request_headers,
	  header_names[i], header_values[i]);
    }

    g_signal_connect (fb->message, "got-chunk", G_CALLBACK (swfdec_fb_loader2_push), fb);
    g_signal_connect (fb->message, "finished", G_CALLBACK (swfdec_fb_loader2_finished), fb);
    if (buffer)
      soup_message_set_request (fb->message, "appliation/x-www-form-urlencoded",
	  SOUP_MEMORY_COPY, (char *) buffer->data, buffer->length);
	fb->running = TRUE;
	fb->thread = g_thread_create_full (loader_thread, fb, 0, TRUE, FALSE, G_THREAD_PRIORITY_LOW, &err);
  }
  swfdec_url_free (url);
}

static gulong
swfdec_fb_loader2_get_loaded (SwfdecLoader * loader)
{
  SwfdecFbLoader2 *fb = SWFDEC_FB_LOADER2 (loader);
  return fb->loaded;
}

static void
swfdec_fb_loader2_close (SwfdecStream *stream)
{
  SwfdecFbLoader2 *fb = SWFDEC_FB_LOADER2 (stream);
  SwfdecFbLoader2Class *klass = SWFDEC_FB_LOADER2_GET_CLASS (fb);

  fb->running = FALSE;
  ipcc_seterror();
  if (fb->thread != NULL)
  {
	if (g_main_loop_is_running (klass->loop))
		g_main_loop_quit (klass->loop);
	g_thread_join (fb->thread);
	fb->thread = NULL;
  }
}

static void
swfdec_fb_loader2_class_init (SwfdecFbLoader2Class *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  SwfdecStreamClass *stream_class = SWFDEC_STREAM_CLASS (klass);
  SwfdecLoaderClass *loader_class = SWFDEC_LOADER_CLASS (klass);

  object_class->dispose = swfdec_fb_loader2_dispose;

  stream_class->close = swfdec_fb_loader2_close;

  loader_class->load = swfdec_fb_loader2_load;
  loader_class->get_loaded = swfdec_fb_loader2_get_loaded;
  
  klass->async_context = g_main_context_new();
  klass->session = soup_session_async_new_with_options (SOUP_SESSION_ASYNC_CONTEXT, 
										   klass->async_context,
										   NULL);
	{ // output http header.
		const char* str = getenv("VA_FLASH_DEBUG");
		if(str && strcmp(str, "false") != 0) {
			g_printerr("enable libsoup log\n");
			SoupLogger* logger = soup_logger_new(SOUP_LOGGER_LOG_HEADERS, -1);
			soup_session_add_feature(klass->session, logger);
		}
  	}

  	{ // set user agent
		const char* str = getenv("SWFDEC_USER_AGENT");
		if(str) {
			g_printerr("set libsoup User-Agent = %s\n", str);
   			g_object_set (G_OBJECT (klass->session), SOUP_SESSION_USER_AGENT, str, NULL);
		}
	}
	
	g_main_context_unref (klass->async_context);
	
	{  // set http_proxy
		const char *proxy = getenv("http_proxy");
		if (proxy) {
			SoupURI *proxy_uri = NULL;
			char http_proxy[128] = "http://";
			strcat(http_proxy, proxy);
			g_printerr("proxy = %s\n", http_proxy);
			proxy_uri = soup_uri_new (http_proxy);
			if (proxy_uri) {
				g_printerr("proxy ok\n");
				g_object_set (G_OBJECT (klass->session), SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
				soup_uri_free (proxy_uri);
			}
		}
	}
}

static void
swfdec_fb_loader2_init (SwfdecFbLoader2 *fb_loader2)
{
  SwfdecFbLoader2Class *klass = SWFDEC_FB_LOADER2_GET_CLASS (fb_loader2);
  int ret = 0;
  ret = ipcc_open(IPC_MODE_CLIENT);
  fb_loader2->running = FALSE;
  fb_loader2->thread = NULL;
  klass->loop = g_main_loop_new (klass->async_context, FALSE);
}

