char *
rb_make_elapsed_time_string (guint elapsed, guint duration, gboolean show_remaining)
{
	int seconds = 0, minutes = 0, hours = 0;
	int seconds2 = 0, minutes2 = 0, hours2 = 0;

	if (duration == 0)
		return rb_make_duration_string (elapsed);

	if (duration > 0) {
		hours2 = duration / (60 * 60);
		minutes2 = (duration - (hours2 * 60 * 60)) / 60;
		seconds2 = duration % 60;
	}

	if (elapsed > 0) {
		hours = elapsed / (60 * 60);
		minutes = (elapsed - (hours * 60 * 60)) / 60;
		seconds = elapsed % 60;
	}

	if (show_remaining) {
		int remaining = duration - elapsed;
		int remaining_hours = remaining / (60 * 60);
		int remaining_minutes = (remaining - (remaining_hours * 60 * 60)) / 60;
		/* remaining could conceivably be negative. This would
		 * be a bug, but the elapsed time will display right
		 * with the abs(). */
		int remaining_seconds = abs (remaining % 60);
		if (hours2 == 0)
			return g_strdup_printf (_("%d:%02d of %d:%02d remaining"),
						remaining_minutes, remaining_seconds,
						minutes2, seconds2);
		else
			return g_strdup_printf (_("%d:%02d:%02d of %d:%02d:%02d remaining"),
						remaining_hours, remaining_minutes, remaining_seconds,
						hours2, minutes2, seconds2);
	} else {
		if (hours == 0 && hours2 == 0)
			return g_strdup_printf (_("%d:%02d of %d:%02d"),
						minutes, seconds,
						minutes2, seconds2);
		else
			return g_strdup_printf (_("%d:%02d:%02d of %d:%02d:%02d"),
						hours, minutes, seconds,
						hours2, minutes2, seconds2);
	}
}
/*
 * Parse a filename pattern and replace markers with values from a TrackDetails
 * structure.
 *
 * Valid markers so far are:
 * %at -- album title
 * %aa -- album artist
 * %aA -- album artist (lowercase)
 * %as -- album artist sortname
 * %aS -- album artist sortname (lowercase)
 * %ay -- album year
 * %ag -- album genre
 * %aG -- album genre (lowercase)
 * %an -- album disc number
 * %aN -- album disc number, zero padded
 * %tn -- track number (i.e 8)
 * %tN -- track number, zero padded (i.e 08)
 * %tt -- track title
 * %ta -- track artist
 * %tA -- track artist (lowercase)
 * %ts -- track artist sortname
 * %tS -- track artist sortname (lowercase)
 * %td -- track duration
 * %te -- track elapsed time
 * %tb -- track bitrate
 * %st -- stream title
 */
static char *
parse_pattern (const char *pattern, GHashTable *properties, guint elapsed)
{
	/* p is the pattern iterator, i is a general purpose iterator */
	const char *p;
	char *temp;
	GString *s;

	if (pattern == NULL || pattern[0] == 0)
		return g_strdup (" ");

	s = g_string_new (NULL);

	p = pattern;
	while (*p) {
		char *string = NULL;
		const GValue *value = NULL;

		/* If not a % marker, copy and continue */
		if (*p != '%') {
			g_string_append_c (s, *p++);
			/* Explicit increment as we continue past the increment */
			continue;
		}

		/* Is a % marker, go to next and see what to do */
		switch (*++p) {
		case '%':
			/*
			 * Literal %
			 */
			g_string_append_c (s, '%');
			break;
		case 'a':
			/*
			 * Album tag
			 */
			switch (*++p) {
			case 't':
				value = g_hash_table_lookup (properties, "album");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 'T':
				value = g_hash_table_lookup (properties, "album-folded");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 'a':
				value = g_hash_table_lookup (properties, "artist");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 'A':
				value = g_hash_table_lookup (properties, "artist-folded");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 's':
				value = g_hash_table_lookup (properties, "mb-artistsortname");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 'S':
				value = g_hash_table_lookup (properties, "mb-artistsortname");
				if (value)
					string = g_utf8_strdown (g_value_get_string (value), -1);
				break;
			case 'y':
				/* Release year */
				value = g_hash_table_lookup (properties, "year");
				if (value)
					string = g_strdup_printf ("%u", g_value_get_uint (value));
				break;
				/* Disc number */
			case 'n':
				value = g_hash_table_lookup (properties, "disc-number");
				if (value)
					string = g_strdup_printf ("%u", g_value_get_uint (value));
				break;
			case 'N':
				value = g_hash_table_lookup (properties, "disc-number");
				if (value)
					string = g_strdup_printf ("%02u", g_value_get_uint (value));
				break;
				/* genre */
			case 'g':
				value = g_hash_table_lookup (properties, "genre");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 'G':
				value = g_hash_table_lookup (properties, "genre-folded");
				if (value)
					string = g_value_dup_string (value);
				break;
			default:
				string = g_strdup_printf ("%%a%c", *p);
			}

			break;

		case 't':
			/*
			 * Track tag
			 */
			switch (*++p) {
			case 't':
				value = g_hash_table_lookup (properties, "title");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 'T':
				value = g_hash_table_lookup (properties, "title-folded");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 'a':
				value = g_hash_table_lookup (properties, "artist");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 'A':
				value = g_hash_table_lookup (properties, "artist-folded");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 's':
				value = g_hash_table_lookup (properties, "mb-artistsortname");
				if (value)
					string = g_value_dup_string (value);
				break;
			case 'S':
				value = g_hash_table_lookup (properties, "mb-artistsortname");
				if (value)
					string = g_utf8_strdown (g_value_get_string (value), -1);
				break;
			case 'n':
				/* Track number */
				value = g_hash_table_lookup (properties, "track-number");
				if (value)
					string = g_strdup_printf ("%u", g_value_get_uint (value));
				break;
			case 'N':
				/* Track number, zero-padded */
				value = g_hash_table_lookup (properties, "track-number");
				if (value)
					string = g_strdup_printf ("%02u", g_value_get_uint (value));
				break;
			case 'd':
				/* Track duration */
				value = g_hash_table_lookup (properties, "duration");
				if (value)
					string = rb_make_duration_string (g_value_get_uint (value));
				break;
			case 'e':
				/* Track elapsed time */
				string = rb_make_duration_string (elapsed);
				break;
			case 'b':
				/* Track bitrate */
				value = g_hash_table_lookup (properties, "bitrate");
				if (value)
					string = g_strdup_printf ("%u", g_value_get_uint (value));
				break;

			default:
				string = g_strdup_printf ("%%t%c", *p);
 			}

			break;

		case 's':
			/*
			 * Stream tag
			 */
			switch (*++p) {
			case 't':
				value = g_hash_table_lookup (properties, "rb:stream-song-title");
				if (value)
					string = g_value_dup_string (value);
				break;
			default:
				string = g_strdup_printf ("%%s%c", *p);
 			}

			break;


		default:
			string = g_strdup_printf ("%%%c", *p);
		}

		if (string)
			g_string_append (s, string);
		g_free (string);

		++p;
	}

	temp = s->str;
	g_string_free (s, FALSE);
	return temp;
}
/*
 * Parse a filename pattern and replace markers with values from a TrackDetails
 * structure.
 *
 * Valid markers so far are:
 * %at -- album title
 * %aa -- album artist
 * %aA -- album artist (lowercase)
 * %as -- album artist sortname
 * %aS -- album artist sortname (lowercase)
 * %ay -- album year
 * %ag -- album genre
 * %aG -- album genre (lowercase)
 * %an -- album disc number
 * %aN -- album disc number, zero padded
 * %tn -- track number (i.e 8)
 * %tN -- track number, zero padded (i.e 08)
 * %tt -- track title
 * %ta -- track artist
 * %tA -- track artist (lowercase)
 * %ts -- track artist sortname
 * %tS -- track artist sortname (lowercase)
 * %td -- track duration
 * %te -- track elapsed time
 * %tb -- track bitrate
 * %st -- stream title
 */
static char *
parse_pattern (const char *pattern, GHashTable *properties, gint64 elapsed)
{
	/* p is the pattern iterator, i is a general purpose iterator */
	const char *p;
	char *temp;
	GString *s;

	if (pattern == NULL || pattern[0] == 0)
		return g_strdup (" ");

	s = g_string_new (NULL);

	p = pattern;
	while (*p) {
		char *string = NULL;
		const char **strv = NULL;
		GVariant *value = NULL;

		/* If not a % marker, copy and continue */
		if (*p != '%') {
			g_string_append_c (s, *p++);
			/* Explicit increment as we continue past the increment */
			continue;
		}

		/* Is a % marker, go to next and see what to do */
		switch (*++p) {
		case '%':
			/*
			 * Literal %
			 */
			g_string_append_c (s, '%');
			break;
		case 'a':
			/*
			 * Album tag
			 */
			switch (*++p) {
			case 't':
				value = g_hash_table_lookup (properties, "xesam:album");
				if (value)
					string = g_variant_dup_string (value, NULL);
				break;
			case 'T':
				value = g_hash_table_lookup (properties, "xesam:album");
				if (value)
					string = g_utf8_strdown (g_variant_get_string (value, NULL), -1);
				break;
			case 'a':
				value = g_hash_table_lookup (properties, "xesam:artist");
				if (value) {
					strv = g_variant_get_strv (value, NULL);
					string = g_strdup (strv[0]);
				}
				break;
			case 'A':
				value = g_hash_table_lookup (properties, "xesam:artist");
				if (value) {
					strv = g_variant_get_strv (value, NULL);
					string = g_utf8_strdown (strv[0], -1);
				}
				break;
			case 's':
				value = g_hash_table_lookup (properties, "rhythmbox:albumArtistSortname");
				if (value)
					string = g_variant_dup_string (value, NULL);
				break;
			case 'S':
				value = g_hash_table_lookup (properties, "rhythmbox:albumArtistSortname");
				if (value)
					string = g_utf8_strdown (g_variant_get_string (value, NULL), -1);
				break;
			case 'y':
				/* Release year */
				value = g_hash_table_lookup (properties, "xesam:contentCreated");
				if (value) {
					const char *iso8601;
					GTimeVal tv;

					iso8601 = g_variant_get_string (value, NULL);
					if (g_time_val_from_iso8601 (iso8601, &tv)) {
						GDate d;
						g_date_set_time_val (&d, &tv);

						string = g_strdup_printf ("%u", g_date_get_year (&d));
					}
				}
				break;
				/* Disc number */
			case 'n':
				value = g_hash_table_lookup (properties, "xesam:discNumber");
				if (value)
					string = g_strdup_printf ("%u", g_variant_get_int32 (value));
				break;
			case 'N':
				value = g_hash_table_lookup (properties, "xesam:discNumber");
				if (value)
					string = g_strdup_printf ("%02u", g_variant_get_int32 (value));
				break;
				/* genre */
			case 'g':
				value = g_hash_table_lookup (properties, "xesam:genre");
				if (value) {
					strv = g_variant_get_strv (value, NULL);
					string = g_strdup (strv[0]);
				}
				break;
			case 'G':
				value = g_hash_table_lookup (properties, "xesam:genre");
				if (value) {
					strv = g_variant_get_strv (value, NULL);
					string = g_utf8_strdown (strv[0], -1);
				}
				break;
			default:
				string = g_strdup_printf ("%%a%c", *p);
			}

			break;

		case 't':
			/*
			 * Track tag
			 */
			switch (*++p) {
			case 't':
				value = g_hash_table_lookup (properties, "rhythmbox:streamTitle");
				if (value == NULL)
					value = g_hash_table_lookup (properties, "xesam:title");
				if (value)
					string = g_variant_dup_string (value, NULL);
				break;
			case 'T':
				value = g_hash_table_lookup (properties, "rhythmbox:streamTitle");
				if (value == NULL)
					value = g_hash_table_lookup (properties, "xesam:title");
				if (value)
					string = g_utf8_strdown (g_variant_get_string (value, NULL), -1);
				break;
			case 'a':
				value = g_hash_table_lookup (properties, "xesam:artist");
				if (value) {
					strv = g_variant_get_strv (value, NULL);
					string = g_strdup (strv[0]);
				}
				break;
			case 'A':
				value = g_hash_table_lookup (properties, "xesam:artist");
				if (value) {
					strv = g_variant_get_strv (value, NULL);
					string = g_utf8_strdown (strv[0], -1);
				}
				break;
			case 's':
				value = g_hash_table_lookup (properties, "rhythmbox:artistSortname");
				if (value)
					string = g_variant_dup_string (value, NULL);
				break;
			case 'S':
				value = g_hash_table_lookup (properties, "rhythmbox:artistSortname");
				if (value)
					string = g_utf8_strdown (g_variant_get_string (value, NULL), -1);
				break;
			case 'n':
				/* Track number */
				value = g_hash_table_lookup (properties, "xesam:trackNumber");
				if (value)
					string = g_strdup_printf ("%u", g_variant_get_int32 (value));
				break;
			case 'N':
				/* Track number, zero-padded */
				value = g_hash_table_lookup (properties, "xesam:trackNumber");
				if (value)
					string = g_strdup_printf ("%02u", g_variant_get_int32 (value));
				break;
			case 'd':
				/* Track duration */
				value = g_hash_table_lookup (properties, "mpris:length");
				if (value)
					string = rb_make_duration_string (g_variant_get_int64 (value));
				break;
			case 'e':
				/* Track elapsed time */
				string = rb_make_duration_string (elapsed);
				break;
			case 'b':
				/* Track bitrate */
				value = g_hash_table_lookup (properties, "xesam:audioBitrate");
				if (value)
					string = g_strdup_printf ("%u", g_variant_get_int32 (value) / 1024);
				break;

			default:
				string = g_strdup_printf ("%%t%c", *p);
 			}

			break;

		case 's':
			/*
			 * Stream tag
			 */
			switch (*++p) {
			case 't':
				value = g_hash_table_lookup (properties, "rhythmbox:streamTitle");
				if (value) {
					value = g_hash_table_lookup (properties, "xesam:title");
					if (value) {
						string = g_variant_dup_string (value, NULL);
					}
				}
				break;
			default:
				string = g_strdup_printf ("%%s%c", *p);
 			}
			break;

		default:
			string = g_strdup_printf ("%%%c", *p);
		}

		if (string)
			g_string_append (s, string);
		g_free (string);

		++p;
	}

	temp = s->str;
	g_string_free (s, FALSE);
	return temp;
}