#import "Basic";

// Build the C library first (only .so — Jai links dynamically, which pulls in libsystemd automatically):
//   cc -fPIC -shared -o linux/libmpris.so mpris.c -lsystemd
libmpris :: #library "linux/libmpris";

/* ── Types ──────────────────────────────────────────────────────────────── */

Mpris_Player :: *void;

// Callbacks must be #c_call. Pass your context pointer as userdata.
Mpris_Callback             :: #type (userdata: *void)                                         -> void #c_call;
Mpris_Seek_Callback        :: #type (offset_us: s64,  userdata: *void)                       -> void #c_call;
Mpris_Set_Position_Callback :: #type (track_id: *u8, position_us: s64, userdata: *void)      -> void #c_call;
Mpris_Volume_Callback      :: #type (volume: float64, userdata: *void)                       -> void #c_call;

/* ── Raw C bindings ─────────────────────────────────────────────────────── */

_mpris_player_create       :: (player_name: *u8, identity: *u8) -> Mpris_Player                                             #foreign libmpris "mpris_player_create";
_mpris_player_destroy      :: (p: Mpris_Player)                                                                              #foreign libmpris "mpris_player_destroy";
_mpris_set_playback_status :: (p: Mpris_Player, status: *u8)                                                                 #foreign libmpris "mpris_set_playback_status";
_mpris_set_metadata        :: (p: Mpris_Player, track_id: *u8, title: *u8, artist: *u8, album: *u8, length_us: s64)          #foreign libmpris "mpris_set_metadata";
_mpris_set_position        :: (p: Mpris_Player, position_us: s64)                                                            #foreign libmpris "mpris_set_position";
_mpris_set_volume          :: (p: Mpris_Player, volume: float64)                                                             #foreign libmpris "mpris_set_volume";
_mpris_set_can_go_next     :: (p: Mpris_Player, value: s32)                                                                  #foreign libmpris "mpris_set_can_go_next";
_mpris_set_can_go_previous :: (p: Mpris_Player, value: s32)                                                                  #foreign libmpris "mpris_set_can_go_previous";
_mpris_set_can_play        :: (p: Mpris_Player, value: s32)                                                                  #foreign libmpris "mpris_set_can_play";
_mpris_set_can_pause       :: (p: Mpris_Player, value: s32)                                                                  #foreign libmpris "mpris_set_can_pause";
_mpris_set_can_seek        :: (p: Mpris_Player, value: s32)                                                                  #foreign libmpris "mpris_set_can_seek";
_mpris_emit_seeked         :: (p: Mpris_Player, position_us: s64)                                                            #foreign libmpris "mpris_emit_seeked";
_mpris_on_play             :: (p: Mpris_Player, cb: Mpris_Callback,              userdata: *void)                            #foreign libmpris "mpris_on_play";
_mpris_on_pause            :: (p: Mpris_Player, cb: Mpris_Callback,              userdata: *void)                            #foreign libmpris "mpris_on_pause";
_mpris_on_play_pause       :: (p: Mpris_Player, cb: Mpris_Callback,              userdata: *void)                            #foreign libmpris "mpris_on_play_pause";
_mpris_on_stop             :: (p: Mpris_Player, cb: Mpris_Callback,              userdata: *void)                            #foreign libmpris "mpris_on_stop";
_mpris_on_next             :: (p: Mpris_Player, cb: Mpris_Callback,              userdata: *void)                            #foreign libmpris "mpris_on_next";
_mpris_on_previous         :: (p: Mpris_Player, cb: Mpris_Callback,              userdata: *void)                            #foreign libmpris "mpris_on_previous";
_mpris_on_seek             :: (p: Mpris_Player, cb: Mpris_Seek_Callback,         userdata: *void)                            #foreign libmpris "mpris_on_seek";
_mpris_on_set_position     :: (p: Mpris_Player, cb: Mpris_Set_Position_Callback, userdata: *void)                            #foreign libmpris "mpris_on_set_position";
_mpris_on_volume           :: (p: Mpris_Player, cb: Mpris_Volume_Callback,       userdata: *void)                            #foreign libmpris "mpris_on_volume";
_mpris_process             :: (p: Mpris_Player) -> s32                                                                       #foreign libmpris "mpris_process";

/* ── Jai-friendly wrappers (handle string conversion) ───────────────────── */

// player_name: D-Bus name component, no spaces (e.g. "MyPlayer")
// identity:    Human-readable name shown in media menus (e.g. "My Music Player")
mpris_player_create :: (player_name: string, identity: string) -> Mpris_Player {
    n := temp_c_string(player_name);
    i := temp_c_string(identity);
    return _mpris_player_create(n, i);
}

mpris_player_destroy :: (p: Mpris_Player) {
    _mpris_player_destroy(p);
}

// status: "Playing" | "Paused" | "Stopped"
mpris_set_playback_status :: (p: Mpris_Player, status: string) {
    _mpris_set_playback_status(p, temp_c_string(status));
}

Mpris_Metadata :: struct {
    track_id  : string;  // D-Bus object path, e.g. "/myapp/track/42". Leave empty for auto.
    title     : string;
    artist    : string;
    album     : string;
    length_us : s64;     // Duration in microseconds. 0 = unknown.
}

mpris_set_metadata :: (p: Mpris_Player, meta: Mpris_Metadata) {
    tid := ifx meta.track_id then temp_c_string(meta.track_id) else null;
    t   := ifx meta.title    then temp_c_string(meta.title)    else null;
    a   := ifx meta.artist   then temp_c_string(meta.artist)   else null;
    al  := ifx meta.album    then temp_c_string(meta.album)    else null;
    _mpris_set_metadata(p, tid, t, a, al, meta.length_us);
}

mpris_set_position :: (p: Mpris_Player, position_us: s64) {
    _mpris_set_position(p, position_us);
}

mpris_set_volume :: (p: Mpris_Player, volume: float64) {
    _mpris_set_volume(p, volume);
}

mpris_set_can_go_next     :: (p: Mpris_Player, v: bool) { _mpris_set_can_go_next(p,     cast(s32) ifx v then 1 else 0); }
mpris_set_can_go_previous :: (p: Mpris_Player, v: bool) { _mpris_set_can_go_previous(p, cast(s32) ifx v then 1 else 0); }
mpris_set_can_play        :: (p: Mpris_Player, v: bool) { _mpris_set_can_play(p,        cast(s32) ifx v then 1 else 0); }
mpris_set_can_pause       :: (p: Mpris_Player, v: bool) { _mpris_set_can_pause(p,       cast(s32) ifx v then 1 else 0); }
mpris_set_can_seek        :: (p: Mpris_Player, v: bool) { _mpris_set_can_seek(p,        cast(s32) ifx v then 1 else 0); }

mpris_emit_seeked :: (p: Mpris_Player, position_us: s64) {
    _mpris_emit_seeked(p, position_us);
}

mpris_on_play         :: (p: Mpris_Player, cb: Mpris_Callback,              ud: *void) { _mpris_on_play(p, cb, ud); }
mpris_on_pause        :: (p: Mpris_Player, cb: Mpris_Callback,              ud: *void) { _mpris_on_pause(p, cb, ud); }
mpris_on_play_pause   :: (p: Mpris_Player, cb: Mpris_Callback,              ud: *void) { _mpris_on_play_pause(p, cb, ud); }
mpris_on_stop         :: (p: Mpris_Player, cb: Mpris_Callback,              ud: *void) { _mpris_on_stop(p, cb, ud); }
mpris_on_next         :: (p: Mpris_Player, cb: Mpris_Callback,              ud: *void) { _mpris_on_next(p, cb, ud); }
mpris_on_previous     :: (p: Mpris_Player, cb: Mpris_Callback,              ud: *void) { _mpris_on_previous(p, cb, ud); }
mpris_on_seek         :: (p: Mpris_Player, cb: Mpris_Seek_Callback,         ud: *void) { _mpris_on_seek(p, cb, ud); }
mpris_on_set_position :: (p: Mpris_Player, cb: Mpris_Set_Position_Callback, ud: *void) { _mpris_on_set_position(p, cb, ud); }
mpris_on_volume       :: (p: Mpris_Player, cb: Mpris_Volume_Callback,       ud: *void) { _mpris_on_volume(p, cb, ud); }

// Returns true if messages were processed, false if idle, exits on error.
mpris_process :: (p: Mpris_Player) -> bool {
    r := _mpris_process(p);
    if r < 0 { log_error("mpris_process failed: %", r); return false; }
    return r > 0;
}
