/*  XMMS2 - X Music Multiplexer System
 *  Copyright (C) 2003-2007 XMMS2 Team
 *
 *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
 *
 *  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.
 */




/** @file
 * This file controls the mediainfo reader thread.
 *
 */

#include <stdlib.h>

#include "xmms/xmms_log.h"
#include "xmms/xmms_ipc.h"
#include "xmmspriv/xmms_mediainfo.h"
#include "xmmspriv/xmms_playlist.h"
#include "xmmspriv/xmms_medialib.h"
#include "xmmspriv/xmms_xform.h"


#include <glib.h>

/** @defgroup MediaInfoReader MediaInfoReader
  * @ingroup XMMSServer
  * @brief The mediainfo reader.
  *
  * When a item is added to the playlist the mediainfo reader will
  * start extracting the information from this entry and update it
  * if additional information is found.
  * @{
  */

struct xmms_mediainfo_reader_St {
	xmms_object_t object;

	GThread *thread;
	GMutex *mutex;
	GCond *cond;

	gboolean running;
	xmms_playlist_t *playlist;
};

static void xmms_mediainfo_reader_stop (xmms_object_t *o);
static gpointer xmms_mediainfo_reader_thread (gpointer data);
static void xmms_mediainfo_playlist_changed_cb (xmms_object_t *object, gconstpointer arg, gpointer userdata);


/**
  * Start a new mediainfo reader thread
  */

xmms_mediainfo_reader_t *
xmms_mediainfo_reader_start (xmms_playlist_t *playlist)
{
	xmms_mediainfo_reader_t *mrt;

	g_return_val_if_fail (playlist, NULL);

	mrt = xmms_object_new (xmms_mediainfo_reader_t,
	                       xmms_mediainfo_reader_stop);

	xmms_ipc_object_register (XMMS_IPC_OBJECT_MEDIAINFO_READER,
	                          XMMS_OBJECT (mrt));

	xmms_ipc_broadcast_register (XMMS_OBJECT (mrt),
	                             XMMS_IPC_SIGNAL_MEDIAINFO_READER_STATUS);
	xmms_ipc_signal_register (XMMS_OBJECT (mrt),
	                          XMMS_IPC_SIGNAL_MEDIAINFO_READER_UNINDEXED);

	mrt->mutex = g_mutex_new ();
	mrt->cond = g_cond_new ();
	mrt->playlist = playlist;
	mrt->running = TRUE;
	mrt->thread = g_thread_create (xmms_mediainfo_reader_thread, mrt, TRUE, NULL);

	xmms_object_connect (XMMS_OBJECT (playlist), XMMS_IPC_SIGNAL_PLAYLIST_CHANGED, xmms_mediainfo_playlist_changed_cb, mrt);

	return mrt;
}

/**
  * Kill the mediainfo reader thread
  */

static void
xmms_mediainfo_reader_stop (xmms_object_t *o)
{
	xmms_mediainfo_reader_t *mir = (xmms_mediainfo_reader_t *) o;

	g_mutex_lock (mir->mutex);
	mir->running = FALSE;
	g_cond_signal (mir->cond);
	g_mutex_unlock (mir->mutex);

	xmms_ipc_broadcast_unregister (XMMS_IPC_SIGNAL_MEDIAINFO_READER_STATUS);
	xmms_ipc_signal_unregister (XMMS_IPC_SIGNAL_MEDIAINFO_READER_UNINDEXED);
	xmms_ipc_object_unregister (XMMS_IPC_OBJECT_MEDIAINFO_READER);

	g_thread_join (mir->thread);

	g_cond_free (mir->cond);
	g_mutex_free (mir->mutex);
}

/**
 * Wake the reader thread and start process the entries.
 */

void
xmms_mediainfo_reader_wakeup (xmms_mediainfo_reader_t *mr)
{
	g_return_if_fail (mr);

	g_mutex_lock (mr->mutex);
	g_cond_signal (mr->cond);
	g_mutex_unlock (mr->mutex);
}

/** @} */

static void
xmms_mediainfo_playlist_changed_cb (xmms_object_t *object, gconstpointer arg, gpointer userdata)
{
	xmms_mediainfo_reader_t *mir = userdata;
	const xmms_object_cmd_arg_t *oarg = arg;
	GHashTable *chmsg = oarg->retval->value.dict;

	xmms_object_cmd_value_t *val = g_hash_table_lookup (chmsg, "type");

	if (!val)
		return;

	if (val->value.uint32 == XMMS_PLAYLIST_CHANGED_ADD ||
	    val->value.uint32 == XMMS_PLAYLIST_CHANGED_INSERT) {
		xmms_mediainfo_reader_wakeup (mir);
	}
}

static gpointer
xmms_mediainfo_reader_thread (gpointer data)
{
	GList *goal_format;
	GTimeVal timeval;
	xmms_stream_type_t *f;
	guint num = 0;

	xmms_mediainfo_reader_t *mrt = (xmms_mediainfo_reader_t *) data;

	xmms_object_emit_f (XMMS_OBJECT (mrt),
	                    XMMS_IPC_SIGNAL_MEDIAINFO_READER_STATUS,
	                    XMMS_OBJECT_CMD_ARG_INT32,
	                    XMMS_MEDIAINFO_READER_STATUS_RUNNING);


	f = _xmms_stream_type_new (NULL,
	                           XMMS_STREAM_TYPE_MIMETYPE,
	                           "audio/pcm",
	                           XMMS_STREAM_TYPE_END);
	goal_format = g_list_prepend (NULL, f);

	while (mrt->running) {
		xmms_medialib_session_t *session;
		xmmsc_medialib_entry_status_t prev_status;
		guint lmod = 0;
		xmms_medialib_entry_t entry;
		xmms_xform_t *xform;

		session = xmms_medialib_begin_write ();
		entry = xmms_medialib_entry_not_resolved_get (session);
		XMMS_DBG ("got %d as not resolved", entry);

		if (!entry) {
			xmms_medialib_end (session);

			xmms_object_emit_f (XMMS_OBJECT (mrt),
			                    XMMS_IPC_SIGNAL_MEDIAINFO_READER_STATUS,
			                    XMMS_OBJECT_CMD_ARG_INT32,
			                    XMMS_MEDIAINFO_READER_STATUS_IDLE);

			g_mutex_lock (mrt->mutex);
			g_cond_wait (mrt->cond, mrt->mutex);
			g_mutex_unlock (mrt->mutex);

			num = 0;

			xmms_object_emit_f (XMMS_OBJECT (mrt),
			                    XMMS_IPC_SIGNAL_MEDIAINFO_READER_STATUS,
			                    XMMS_OBJECT_CMD_ARG_INT32,
			                    XMMS_MEDIAINFO_READER_STATUS_RUNNING);
			continue;
		}

		prev_status = xmms_medialib_entry_property_get_int (session, entry, XMMS_MEDIALIB_ENTRY_PROPERTY_STATUS);
		xmms_medialib_entry_status_set (session, entry, XMMS_MEDIALIB_ENTRY_STATUS_RESOLVING);

		lmod = xmms_medialib_entry_property_get_int (session, entry, XMMS_MEDIALIB_ENTRY_PROPERTY_LMOD);

		if (num == 0) {
			xmms_object_emit_f (XMMS_OBJECT (mrt),
			                    XMMS_IPC_SIGNAL_MEDIAINFO_READER_UNINDEXED,
			                    XMMS_OBJECT_CMD_ARG_UINT32,
			                    xmms_medialib_num_not_resolved (session));
			num = 10;
		} else {
			num--;
		}

		xmms_medialib_end (session);
		xform = xmms_xform_chain_setup_without_effects (entry, goal_format);

		if (!xform) {
			if (prev_status == XMMS_MEDIALIB_ENTRY_STATUS_NEW) {
				xmms_medialib_entry_remove (entry);
			} else {
				session = xmms_medialib_begin_write ();
				xmms_medialib_entry_status_set (session, entry, XMMS_MEDIALIB_ENTRY_STATUS_NOT_AVAILABLE);
				xmms_medialib_end (session);
				xmms_medialib_entry_send_update (entry);
			}
			continue;
		}

		xmms_object_unref (xform);
		g_get_current_time (&timeval);

		session = xmms_medialib_begin_write ();
		xmms_medialib_entry_status_set (session, entry, XMMS_MEDIALIB_ENTRY_STATUS_OK);
		xmms_medialib_entry_property_set_int (session, entry,
		                                      XMMS_MEDIALIB_ENTRY_PROPERTY_ADDED,
		                                      timeval.tv_sec);
		xmms_medialib_end (session);
		xmms_medialib_entry_send_update (entry);

	}

	return NULL;
}
