<commit_before>/*  XMMS2 - X Music Multiplexer System
 *  Copyright (C) 2003	<NAME>, <NAME>, <NAME>
 * 
 *  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
 *  Controls playlist
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <math.h>

#include "xmms/playlist.h"
#include "xmms/dbus.h"
#include "xmms/playlist_entry.h"
#include "xmms/util.h"
#include "xmms/signal_xmms.h"
#include "xmms/mediainfo.h"


/** @defgroup PlaylistClientMethods PlaylistClientMethods
  * @ingroup Playlist
  * @brief the playlist methods that could be used by the client
  */

/** @defgroup Playlist Playlist
  * @ingroup XMMSServer
  * @brief This is the playlist control.
  *
  * A playlist is a central thing in the XMMS server, it
  * tells us what to do after we played the following entry
  * @{
  */

/* Internal macro to emit XMMS_SIGNAL_PLAYLIST_CHANGED */
#define XMMS_PLAYLIST_CHANGED_MSG(ttype,iid,argument) do { \
	xmms_playlist_changed_msg_t *chmsg; \
	chmsg = g_new0 (xmms_playlist_changed_msg_t, 1);\
	chmsg->type = ttype; chmsg->id=iid; chmsg->arg=argument; \
	xmms_object_emit_f (XMMS_OBJECT (playlist), XMMS_SIGNAL_PLAYLIST_CHANGED, XMMS_OBJECT_METHOD_ARG_PLCH, chmsg);\
	g_free (chmsg); \
} while (0)


/** Playlist structure */
struct xmms_playlist_St {
	xmms_object_t object;
	/** The current list first node. */
	GList *list;
	/** Next song that will be retured by xmms_playlist_get_next */
	GList *nextentry;

	GHashTable *id_table;
	guint nextid;

	GMutex *mutex;
	GCond *cond;
	gboolean is_waiting;

	xmms_mediainfo_thread_t *mediainfothr;

};

static void xmms_playlist_sort (xmms_playlist_t *playlist, gchar *property, xmms_error_t *err);
static void xmms_playlist_shuffle (xmms_playlist_t *playlist, xmms_error_t *err);
static void xmms_playlist_clear (xmms_playlist_t *playlist, xmms_error_t *err);
static gboolean xmms_playlist_id_move (xmms_playlist_t *playlist, guint id, gint steps, xmms_error_t *err);
static void xmms_playlist_destroy (xmms_object_t *object);
static void xmms_playlist_set_next (xmms_playlist_t *playlist, guint32 type, gint32 moment, xmms_error_t *error);


/*
 * Public functions
 */

/** Lock the playlist for changes and queries */
#define XMMS_PLAYLIST_LOCK(a) g_mutex_lock (a->mutex)
/** Unlock the playlist for changes and queries */
#define XMMS_PLAYLIST_UNLOCK(a) g_mutex_unlock (a->mutex)

/** Gererate statistics for this playlist */
GList *
xmms_playlist_stats (xmms_playlist_t *playlist, GList *list)
{
	gchar *tmp;

	XMMS_PLAYLIST_LOCK (playlist);
	tmp = g_strdup_printf ("playlist.entries=%u", xmms_playlist_entries_total (playlist));
	list = g_list_append (list, tmp);
	XMMS_PLAYLIST_UNLOCK (playlist);

	return list;

}


/** Total number of entries in the playlist. */

guint
xmms_playlist_entries_total (xmms_playlist_t *playlist)
{
	guint ret;
	ret = g_hash_table_size (playlist->id_table);
	return ret;
}

/** Number of entries *after* the nextsong pointer. */

guint
xmms_playlist_entries_left (xmms_playlist_t *playlist)
{
	guint ret;
	ret = g_list_length (playlist->nextentry);
	return ret;
}

/** Shuffles the playlist
  * @ingroup PlaylistClientMethods
  */
XMMS_METHOD_DEFINE (shuffle, xmms_playlist_shuffle, xmms_playlist_t *, NONE, NONE, NONE);

/** shuffles playlist */
static void
xmms_playlist_shuffle (xmms_playlist_t *playlist, xmms_error_t *err)
{
        gint len;
        gint i, j;
        GList *node, **ptrs;

	g_return_if_fail (playlist);

	XMMS_PLAYLIST_LOCK (playlist);

	len = xmms_playlist_entries_total (playlist);
                                                                                  
        if (!len) {
		XMMS_PLAYLIST_UNLOCK (playlist);
                return;
	}

        ptrs = g_new (GList *, len);
                                                                                  
        for (node = playlist->list, i = 0; i < len; node = g_list_next (node), i++)
                ptrs[i] = node;
                                                                                  
        j = random() % len;
        playlist->list = ptrs[j];
        ptrs[j]->next = NULL;
        ptrs[j] = ptrs[0];

        for (i = 1; i < len; i++)
        {
                j = random() % (len - i);
                playlist->list->prev = ptrs[i + j];
                ptrs[i + j]->next = playlist->list;
                playlist->list = ptrs[i + j];
                ptrs[i + j] = ptrs[i];
        }
        playlist->list->prev = NULL;
                                                                                  
        g_free(ptrs);

	XMMS_PLAYLIST_CHANGED_MSG (XMMS_PLAYLIST_CHANGED_SHUFFLE, 0, 0);

	g_cond_signal (playlist->cond);

	XMMS_PLAYLIST_UNLOCK (playlist);
}

/** Removes the id from the playlist
  * @ingroup PlaylistClientMethods
  */

XMMS_METHOD_DEFINE (remove, xmms_playlist_id_remove, xmms_playlist_t *, NONE, UINT32, NONE);

/** removes entry from playlist */
gboolean 
xmms_playlist_id_remove (xmms_playlist_t *playlist, guint id, xmms_error_t *err)
{
	GList *node;

	g_return_val_if_fail (playlist, FALSE);
	g_return_val_if_fail (id, FALSE);

	XMMS_PLAYLIST_LOCK (playlist);
	node = g_hash_table_lookup (playlist->id_table, GUINT_TO_POINTER (id));
	if (!node) {
		XMMS_PLAYLIST_UNLOCK (playlist);
		xmms_error_set (err, XMMS_ERROR_NOENT, "Trying to remove nonexistant playlist entry");<commit_msg>Formatting fixes (and a tiny bugfix).<commit_after>/*  XMMS2 - X Music Multiplexer System
 *  Copyright (C) 2003	<NAME>, <NAME>, <NAME>
 * 
 *  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
 *  Controls playlist
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <math.h>

#include "xmms/playlist.h"
#include "xmms/dbus.h"
#include "xmms/playlist_entry.h"
#include "xmms/util.h"
#include "xmms/signal_xmms.h"
#include "xmms/mediainfo.h"


/** @defgroup PlaylistClientMethods PlaylistClientMethods
  * @ingroup Playlist
  * @brief the playlist methods that could be used by the client
  */

/** @defgroup Playlist Playlist
  * @ingroup XMMSServer
  * @brief This is the playlist control.
  *
  * A playlist is a central thing in the XMMS server, it
  * tells us what to do after we played the following entry
  * @{
  */

/* Internal macro to emit XMMS_SIGNAL_PLAYLIST_CHANGED */
#define XMMS_PLAYLIST_CHANGED_MSG(ttype,iid,argument) do { \
	xmms_playlist_changed_msg_t *chmsg; \
	chmsg = g_new0 (xmms_playlist_changed_msg_t, 1);\
	chmsg->type = ttype; chmsg->id=iid; chmsg->arg=argument; \
	xmms_object_emit_f (XMMS_OBJECT (playlist), XMMS_SIGNAL_PLAYLIST_CHANGED, XMMS_OBJECT_METHOD_ARG_PLCH, chmsg);\
	g_free (chmsg); \
} while (0)


/** Playlist structure */
struct xmms_playlist_St {
	xmms_object_t object;
	/** The current list first node. */
	GList *list;
	/** Next song that will be retured by xmms_playlist_get_next */
	GList *nextentry;

	GHashTable *id_table;
	guint nextid;

	GMutex *mutex;
	GCond *cond;
	gboolean is_waiting;

	xmms_mediainfo_thread_t *mediainfothr;

};

static void xmms_playlist_sort (xmms_playlist_t *playlist, gchar *property, xmms_error_t *err);
static void xmms_playlist_shuffle (xmms_playlist_t *playlist, xmms_error_t *err);
static void xmms_playlist_clear (xmms_playlist_t *playlist, xmms_error_t *err);
static gboolean xmms_playlist_id_move (xmms_playlist_t *playlist, guint id, gint steps, xmms_error_t *err);
static void xmms_playlist_destroy (xmms_object_t *object);
static void xmms_playlist_set_next (xmms_playlist_t *playlist, guint32 type, gint32 moment, xmms_error_t *error);


/*
 * Public functions
 */

/** Lock the playlist for changes and queries */
#define XMMS_PLAYLIST_LOCK(a) g_mutex_lock (a->mutex)
/** Unlock the playlist for changes and queries */
#define XMMS_PLAYLIST_UNLOCK(a) g_mutex_unlock (a->mutex)

/** Gererate statistics for this playlist */
GList *
xmms_playlist_stats (xmms_playlist_t *playlist, GList *list)
{
	gchar *tmp;

	XMMS_PLAYLIST_LOCK (playlist);
	tmp = g_strdup_printf ("playlist.entries=%u", xmms_playlist_entries_total (playlist));
	list = g_list_append (list, tmp);
	XMMS_PLAYLIST_UNLOCK (playlist);

	return list;

}


/** Total number of entries in the playlist. */

guint
xmms_playlist_entries_total (xmms_playlist_t *playlist)
{
	guint ret;
	ret = g_hash_table_size (playlist->id_table);
	return ret;
}

/** Number of entries *after* the nextsong pointer. */

guint
xmms_playlist_entries_left (xmms_playlist_t *playlist)
{
	guint ret;
	ret = g_list_length (playlist->nextentry);
	return ret;
}

/** Shuffles the playlist
  * @ingroup PlaylistClientMethods
  */
XMMS_METHOD_DEFINE (shuffle, xmms_playlist_shuffle, xmms_playlist_t *, NONE, NONE, NONE);

/** shuffles playlist */
static void
xmms_playlist_shuffle (xmms_playlist_t *playlist, xmms_error_t *err)
{
	gint len, i, j;
	GList *node, **ptrs;

	g_return_if_fail (playlist);

	XMMS_PLAYLIST_LOCK (playlist);

	len = xmms_playlist_entries_total (playlist);
	if (!len) {
		XMMS_PLAYLIST_UNLOCK (playlist);
		return;
	}

	ptrs = g_new (GList *, len);

	for (node = playlist->list, i = 0;
	     i < len && node;
	     node = g_list_next (node), i++)
		ptrs[i] = node;

	j = random() % len;
	playlist->list = ptrs[j];
	ptrs[j]->next = NULL;
	ptrs[j] = ptrs[0];

	for (i = 1; i < len; i++) {
		j = random() % (len - i);
		playlist->list->prev = ptrs[i + j];
		ptrs[i + j]->next = playlist->list;
		playlist->list = ptrs[i + j];
		ptrs[i + j] = ptrs[i];
	}

	playlist->list->prev = NULL;

	g_free (ptrs);

	XMMS_PLAYLIST_CHANGED_MSG (XMMS_PLAYLIST_CHANGED_SHUFFLE, 0, 0);

	g_cond_signal (playlist->cond);

	XMMS_PLAYLIST_UNLOCK (playlist);
}

/** Removes the id from the playlist
  * @ingroup PlaylistClientMethods
  */

XMMS_METHOD_DEFINE (remove, xmms_playlist_id_remove, xmms_playlist_t *, NONE, UINT32, NONE);

/** removes entry from playlist */
gboolean 
xmms_playlist_id_remove (xmms_playlist_t *playlist, guint id, xmms_error_t *err)
{
	GList *node;

	g_return_val_if_fail (playlist, FALSE);
	g_return_val_if_fail (id, FALSE);

	XMMS_PLAYLIST_LOCK (playlist);
	node = g_hash_table_lookup (playlist->id_table, GUINT_TO_POINTER (id));
	if (!node) {
		XMMS_PLAYLIST_UNLOCK (playlist);
		xmms_error_set (err, XMMS_ERROR_NOENT, "Trying to remove nonexistant playlist entry");
{'fraction_non_alphanumeric': 0.10689503732981993, 'fraction_numerical': 0.003689064558629776, 'mean_word_length': 3.643556280587276, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7178892', 'n_tokens_mistral': 4365, 'n_tokens_neox': 4205, 'n_words': 1071}