The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "fcntl.h"

#undef list

#include "xmms/xmmsctrl.h"

typedef gint Xmms__Remote;

#define PUSHgint(g) PUSHs(sv_2mortal(newSViv(g)))

#define CURRENT_POS \
xmms_remote_get_playlist_pos(session)

static gint xmms_session = 0;

#define xmms_remote_prefs_win_toggle(session, show) \
xmms_remote_show_prefs_box(session)

#ifndef HAS_ADD_URL
#define xmms_remote_playlist_add_url_string(session, string) \
        croak("playlist_add_url not available")
#endif

#define xmms_remote_playlist_add_url \
        xmms_remote_playlist_add_url_string

#ifndef HAS_DELETE
#define xmms_remote_playlist_delete(session, pos) \
        croak("playlist_delete not available")
#endif

static AV *svrv_2av(SV *avrv)
{
    if (!(SvROK(avrv) && SvTYPE(SvRV(avrv)) == SVt_PVAV)) {
	croak("not an ARRAY reference");
    }
    return (AV*)SvRV(avrv);
}

static GList *avrv_2glist(SV *avrv)
{
    AV *av = svrv_2av(avrv);
    STRLEN n_a;
    I32 i;
    GList *list = 0;

    for (i=0; i<=AvFILL(av); i++) {
	list = g_list_append(list, SvPV(*av_fetch(av, i, FALSE), n_a));
    }

    return list;
}

static gchar **avrv_2gchar_list(SV *avrv, gint *num)
{
    AV *av = svrv_2av(avrv);
    STRLEN n_a;
    I32 i;
    gchar **list;

    *num = (gint)AvFILL(av)+1;
    list = (gchar **)g_malloc0(*num * sizeof(gchar *));

    for (i=0; i<=AvFILL(av); i++) {
	list[i] = (gchar *)SvPV(*av_fetch(av, i, FALSE), n_a);
    }
    
    return list;
}

typedef gchar * (*playlist_do_func)(gint, gint);

static AV *playlist_do(gint session, playlist_do_func func)
{
    gint i;
    AV *av = newAV();

    for (i=0; i < xmms_remote_get_playlist_length(session); i++) {
	gchar *title = (*func)(session, i);
	av_push(av, newSVpv(title, 0));
        g_free(title);
    }

    return av;
}

static AV *bands_2av(gfloat *bands)
{
    AV *av = newAV();
    int i;
    for (i=0; i<10; i++) {
        av_push(av, newSVnv(bands[i]));
    }
    return av;
}

static gfloat *av_2bands(SV *avrv)
{
    AV *av = svrv_2av(avrv);
    gfloat *bands = (gfloat *)safemalloc(sizeof(gfloat) * 10);
    I32 i;
    if (AvFILL(av)+1 != 10) {
        croak("wrong number of bands %d, should be 10", (int)AvFILL(av)+1);
    }
    for (i=0; i<10; i++) {
        SV *sv = *av_fetch(av, i, FALSE);
        bands[i] = (gfloat)SvNV(sv);
    }
    return bands;
}

static SV *size_string(size_t size)
{
    SV *sv = newSVpv("    -", 5);
    if (size == (size_t)-1) {
	/**/
    }
    else if (!size) {
	sv_setpv(sv, "   0k");
    }
    else if (size < 1024) {
	sv_setpv(sv, "   1k");
    }
    else if (size < 1048576) {
	sv_setpvf(sv, "%4dk", (size + 512) / 1024);
    }
    else if (size < 103809024) {
	sv_setpvf(sv, "%4.1fM", size / 1048576.0);
    }
    else {
	sv_setpvf(sv, "%4dM", (size + 524288) / 1048576);
    }

    return sv;
}

static int waitfor_audio(gint usec)
{
    for (;;) {
	int fd;
	if ((fd = open("/dev/dsp", O_WRONLY)) > 0) {
	    close(fd);
	    return 1;
	}
	xmms_usleep(usec);
    }
    return 0;
}

MODULE = Xmms::Remote   PACKAGE = Xmms::Remote   PREFIX = xmms_remote_

PROTOTYPES: disable

Xmms::Remote
new(classname, session=xmms_session)
    char *classname
    gint session

    CODE:
    ST(0) = sv_newmortal();
    sv_setiv(newSVrv(ST(0),classname), session);

void
xmms_remote_play(session)
    Xmms::Remote session

void
xmms_remote_pause(session)
    Xmms::Remote session

void
xmms_remote_stop(session)
    Xmms::Remote session

void
xmms_remote_quit(session)
    Xmms::Remote session

void
xmms_remote_playlist(session, list, enqueue=0)
    Xmms::Remote session
    GList *list
    gboolean enqueue

    CODE:
    if (!enqueue) {
        xmms_remote_playlist_clear(session);
    }

    xmms_remote_playlist_add(session, list);

    if (!enqueue) {
        xmms_remote_play(session);
    }

    CLEANUP:
    g_list_free(list);

gint
xmms_remote_get_version(session)
    Xmms::Remote session

void 
xmms_remote_playlist_add(session, list)
    Xmms::Remote session
    GList *list

    CLEANUP:
    g_list_free(list);

void 
xmms_remote_playlist_delete(session, pos)
    Xmms::Remote session
    gint pos

void
xmms_remote_playlist_add_url(session, string)
    Xmms::Remote session
    gchar *string

gboolean
xmms_remote_is_playing(session)
    Xmms::Remote session

gboolean
xmms_remote_is_paused(session)
    Xmms::Remote session

gint
xmms_remote_get_playlist_pos(session)
    Xmms::Remote session

void
xmms_remote_set_playlist_pos(session, pos)
    Xmms::Remote session
    gint pos

gint
xmms_remote_get_playlist_length(session)
    Xmms::Remote session

void 
xmms_remote_playlist_clear(session)
    Xmms::Remote session

gint 
xmms_remote_get_output_time(session)
    Xmms::Remote session

SV *
xmms_remote_get_output_timestr(session)
    Xmms::Remote session

    PREINIT:
    gint otime, ptime;

    CODE:
    otime = xmms_remote_get_output_time(session)/1000;
    ptime = xmms_remote_get_playlist_time(session,CURRENT_POS)/1000;
    RETVAL = newSV(0);
    if (ptime) {
	sv_setpvf(RETVAL, "%d:%-2.2d/%d:%-2.2d (%d%%)",
		  otime/60, otime%60, ptime/60, ptime%60, 
		  (otime != 0) ? ((otime*100)/ptime) : 0);
    }
    else {
	sv_setpv(RETVAL, "?");
    }

    OUTPUT:
    RETVAL

void 
xmms_remote_jump_to_time(session, pos)
    Xmms::Remote session
    gint pos

void 
xmms_remote_jump_to_timestr(session, str)
    Xmms::Remote session
    char *str

    PREINIT:
    gint mm, ss;

    CODE:
    if (sscanf(str, "%d:%d", &mm, &ss) == 2) {
	xmms_remote_jump_to_time(session, (mm * 60000) + (ss * 1000));
    }

void 
xmms_remote_get_volume(session)
    Xmms::Remote session

    PREINIT:
    gint vl, vr;

    PPCODE:
    xmms_remote_get_volume(session, &vl, &vr);
    EXTEND(sp, 2);
    PUSHgint(vl);
    PUSHgint(vr);

gint 
xmms_remote_get_main_volume(session)
    Xmms::Remote session

gint 
xmms_remote_get_balance(session)
    Xmms::Remote session

SV *
xmms_remote_get_balancestr(session)
    Xmms::Remote session

    PREINIT:
    gint bal;

    CODE:
    RETVAL = newSV(0);
    if ((bal = xmms_remote_get_balance(session)) == 0) {
	sv_setpv(RETVAL, "center");
    }
    else {
	sv_setpvf(RETVAL, "%d%% %s", 
		  abs(bal), bal > 0 ? "right" : "left");
    }

    OUTPUT:
    RETVAL
    
void 
xmms_remote_set_volume(session, vl, vr=vl)
    Xmms::Remote session
    gint vl
    gint vr

void 
xmms_remote_set_main_volume(session, v)
    Xmms::Remote session
    gint v

void 
xmms_remote_set_balance(session, b)
    Xmms::Remote session
    gint b

gchar *
xmms_remote_get_skin(session)
    Xmms::Remote session

    CLEANUP:
    g_free(RETVAL);

void 
xmms_remote_set_skin(session, skinfile)
    Xmms::Remote session
    gchar *skinfile

gchar *
xmms_remote_get_playlist_file(session, pos=CURRENT_POS)
    Xmms::Remote session
    gint pos

    CLEANUP:
    g_free(RETVAL);

AV *
xmms_remote_get_playlist_files(session)
    Xmms::Remote session

    CODE:
    RETVAL = playlist_do(session, xmms_remote_get_playlist_file);

    OUTPUT:
    RETVAL

gchar *
xmms_remote_get_playlist_title(session, pos=CURRENT_POS)
    Xmms::Remote session
    gint pos

    CLEANUP:
    g_free(RETVAL);

AV *
xmms_remote_get_playlist_titles(session)
    Xmms::Remote session

    CODE:
    RETVAL = playlist_do(session, xmms_remote_get_playlist_title);

    OUTPUT:
    RETVAL

gint 
xmms_remote_get_playlist_time(session, pos=CURRENT_POS)
    Xmms::Remote session
    gint pos

SV *
xmms_remote_get_playlist_timestr(session, pos=CURRENT_POS)
    Xmms::Remote session
    gint pos

    PREINIT:
    gint length;

    CODE:
    length = xmms_remote_get_playlist_time(session, pos);
    RETVAL = newSV(5);
    sv_setpvf(RETVAL, "%d:%-2.2d", length/60000, (length/1000) % 60);

    OUTPUT:
    RETVAL

void 
xmms_remote_get_info(session)
    Xmms::Remote session

    PREINIT:
    gint rate, freq, nch;

    PPCODE:
    xmms_remote_get_info(session, &rate, &freq, &nch);
    EXTEND(sp, 3);
    PUSHgint(rate);
    PUSHgint(freq);
    PUSHgint(nch);

void 
xmms_remote_main_win_toggle(session, show)
    Xmms::Remote session
    gboolean show

void 
xmms_remote_pl_win_toggle(session, show)
    Xmms::Remote session
    gboolean show

void 
xmms_remote_eq_win_toggle(session, show)
    Xmms::Remote session
    gboolean show

void 
xmms_remote_prefs_win_toggle(session, show)
    Xmms::Remote session
    gboolean show

void 
xmms_remote_show_prefs_box(session)
    Xmms::Remote session

void 
xmms_remote_toggle_aot(session, ontop)
    Xmms::Remote session
    gboolean ontop

gboolean
xmms_remote_is_main_win(session)
    Xmms::Remote session

gboolean
xmms_remote_is_pl_win(session)
    Xmms::Remote session

gboolean
xmms_remote_is_eq_win(session)
    Xmms::Remote session

void 
xmms_remote_eject(session)
    Xmms::Remote session

void 
xmms_remote_playlist_prev(session)
    Xmms::Remote session

void 
xmms_remote_playlist_next(session)
    Xmms::Remote session

gboolean 
xmms_remote_is_running(session)
    Xmms::Remote session

void 
xmms_remote_toggle_repeat(session)
    Xmms::Remote session

void 
xmms_remote_toggle_shuffle(session)
    Xmms::Remote session

gint
xmms_remote_is_repeat(session)
    Xmms::Remote session

gint
xmms_remote_is_shuffle(session)
    Xmms::Remote session

void
xmms_remote_get_eq(session)
    Xmms::Remote session

    PREINIT:
    gfloat preamp;
    gfloat *bands;

    PPCODE:
    xmms_remote_get_eq(session, &preamp, &bands);
    if (GIMME_V == G_ARRAY) {
        XPUSHs(sv_2mortal(newSVnv(preamp)));
    }
    XPUSHs(sv_2mortal(newRV_noinc((SV*)bands_2av(bands))));
    g_free(bands);

void
xmms_remote_set_eq(session, preamp, bandsav)
    Xmms::Remote session
    gfloat preamp
    SV *bandsav

    PREINIT:
    gfloat *bands;

    CODE:
    bands = av_2bands(bandsav);
    xmms_remote_set_eq(session, preamp, bands);

    CLEANUP:
    g_free(bands);

gfloat
xmms_remote_get_eq_preamp(session)
    Xmms::Remote session

gfloat
xmms_remote_get_eq_band(session, band)
    Xmms::Remote session
    gint band

void
xmms_remote_set_eq_preamp(session, preamp)
    Xmms::Remote session
    gfloat preamp

void
xmms_remote_set_eq_band(session, band, value)
    Xmms::Remote session
    gint band
    gfloat value

MODULE = Xmms::Remote   PACKAGE = Xmms    PREFIX = xmms_

SV *
size_string(size)
    size_t size

void
xmms_usleep(usec)
    gint usec

void
waitfor_audio(usec=350)
    gint usec