
#include <Elementary.h>

/* no mpris support on Windows */
#ifndef _WIN32
#include "main.h"
#include "win.h"
#include "browser.h"
#include "video.h"
#include "winvid.h"
#include "winlist.h"
#include "config.h"
#include "mpris.h"

static Evas_Object *mainwin = NULL;
static Eldbus_Connection *conn = NULL;
static Eldbus_Service_Interface *iface = NULL;
static Eldbus_Service_Interface *iface_player = NULL;

#define SERVICE "org.mpris.MediaPlayer2.rage"
#define PATH    "/org/mpris/MediaPlayer2"

static void
_cb_name_request(void *data EINA_UNUSED, const Eldbus_Message *msg EINA_UNUSED,
                 Eldbus_Pending *pending EINA_UNUSED)
{
   return;
/*
   unsigned int flag;

   if (eldbus_message_error_get(msg, NULL, NULL))
     {
        fprintf(stderr, "Could not request bus name");
        return;
     }
   if (!eldbus_message_arguments_get(msg, "u", &flag))
     {
        fprintf(stderr, "Could not get arguments on on_name_request");
        return;
     }
   if (!(flag & ELDBUS_NAME_REQUEST_REPLY_PRIMARY_OWNER))
     fprintf(stderr, "Name already in use\n");
 */
}

/* Implementing almost all of:
 * 
https://specifications.freedesktop.org/mpris-spec/latest/Player_Interface.html
 * 
 * Not implemented:
 * Tracklist objects
 * SetPosition (requires Tracklist objects)
 * 
 * In rage generally and here:
 * 
 * Loop playlist vs just loop current
 * Shuffle play
 * Playback rate
 */

#define API(fn) \
static Eldbus_Message * \
_cb_api_##fn(const Eldbus_Service_Interface *iface EINA_UNUSED, \
              const Eldbus_Message *msg)
#define GETTER(fn) \
static Eina_Bool \
_cb_prop_##fn##_get(const Eldbus_Service_Interface *iface EINA_UNUSED, \
                        const char *propname EINA_UNUSED, \
                        Eldbus_Message_Iter *iter, \
                        const Eldbus_Message *request_msg EINA_UNUSED, \
                        Eldbus_Message **error EINA_UNUSED)

#define SETTER(fn) \
static Eldbus_Message * \
_cb_prop_##fn##_set(const Eldbus_Service_Interface *iface EINA_UNUSED, \
                        const char *propname EINA_UNUSED, \
                        Eldbus_Message_Iter *iter, \
                        const Eldbus_Message *msg)
#define PROP_RO(str, type, prop) \
   { str, type, _cb_prop_##prop##_get, NULL, 0 }
#define PROP_RW(str, type, prop) \
   { str, type, _cb_prop_##prop##_get, _cb_prop_##prop##_set, 0 }
#define METHOD(str, type, fn) \
   { str,  NULL, type, _cb_api_##fn, 0 }





/////////////////////////////////////////////////////////////////////////////

GETTER(can_quit)
{
   eldbus_message_iter_arguments_append(iter, "b", EINA_TRUE);
   return EINA_TRUE;
}

GETTER(fullscreen)
{
   Eina_Bool fs = elm_win_fullscreen_get(mainwin);
   eldbus_message_iter_arguments_append(iter, "b", fs);
   return EINA_TRUE;
}

SETTER(fullscreen)
{
   Eina_Bool fs = EINA_FALSE;
   eldbus_message_iter_arguments_get(iter, "d", &fs);
   elm_win_fullscreen_set(mainwin, fs);
   return eldbus_message_method_return_new(msg);
}

GETTER(can_set_fullscreen)
{
   eldbus_message_iter_arguments_append(iter, "b", EINA_TRUE);
   return EINA_TRUE;
}

GETTER(can_raise)
{
   eldbus_message_iter_arguments_append(iter, "b", EINA_TRUE);
   return EINA_TRUE;
}

GETTER(has_track_list)
{
   // XXX: Implement Tracklist
   eldbus_message_iter_arguments_append(iter, "b", EINA_FALSE);
   return EINA_TRUE;
}

GETTER(identity)
{
   char buf[] = "Rage";
   eldbus_message_iter_arguments_append(iter, "s", buf);
   return EINA_TRUE;
}

GETTER(desktop_entry)
{
   char buf[] = "rage";
   eldbus_message_iter_arguments_append(iter, "s", buf);
   return EINA_TRUE;
}

GETTER(supported_mime_types)
{
   Eldbus_Message_Iter *array = NULL;

   eldbus_message_iter_arguments_append(iter, "as", &array);
#define A(s) eldbus_message_iter_arguments_append(array, "s", s)
   A("application/ogg");

   A("audio/1d-interleaved-parityfec");
   A("audio/32kadpcm");
   A("audio/3gpp");
   A("audio/3gpp2");
   A("audio/AMR");
   A("audio/AMR-WB");
   A("audio/ATRAC-ADVANCED-LOSSLESS");
   A("audio/ATRAC-X");
   A("audio/ATRAC3");
   A("audio/BV16");
   A("audio/BV32");
   A("audio/CN");
   A("audio/DAT12");
   A("audio/DV");
   A("audio/DVI4");
   A("audio/EVRC");
   A("audio/EVRC-QCP");
   A("audio/EVRC0");
   A("audio/EVRC1");
   A("audio/EVRCB");
   A("audio/EVRCB0");
   A("audio/EVRCB1");
   A("audio/EVRCWB");
   A("audio/EVRCWB0");
   A("audio/EVRCWB1");
   A("audio/G719");
   A("audio/G722");
   A("audio/G7221");
   A("audio/G723");
   A("audio/G726-16");
   A("audio/G726-24");
   A("audio/G726-32");
   A("audio/G726-40");
   A("audio/G728");
   A("audio/G729");
   A("audio/G7291");
   A("audio/G729D");
   A("audio/G729E");
   A("audio/GSM");
   A("audio/GSM-EFR");
   A("audio/GSM-HR-08");
   A("audio/L16");
   A("audio/L20");
   A("audio/L24");
   A("audio/L8");
   A("audio/LPC");
   A("audio/MP4A-LATM");
   A("audio/MPA");
   A("audio/PCMA");
   A("audio/PCMA-WB");
   A("audio/PCMU");
   A("audio/PCMU-WB");
   A("audio/QCELP");
   A("audio/RED");
   A("audio/SMV");
   A("audio/SMV-QCP");
   A("audio/SMV0");
   A("audio/UEMCLIP");
   A("audio/VDVI");
   A("audio/VMR-WB");
   A("audio/aac");
   A("audio/ac3");
   A("audio/adpcm");
   A("audio/amr");
   A("audio/amr-wb");
   A("audio/amr-wb+");
   A("audio/annodex");
   A("audio/asc");
   A("audio/basic");
   A("audio/bv16");
   A("audio/bv32");
   A("audio/clearmode");
   A("audio/cn");
   A("audio/csound");
   A("audio/dat12");
   A("audio/dls");
   A("audio/dsr-es201108");
   A("audio/dsr-es202050");
   A("audio/dsr-es202211");
   A("audio/dsr-es202212");
   A("audio/dvi4");
   A("audio/eac3");
   A("audio/evrc");
   A("audio/evrc-qcp");
   A("audio/evrc0");
   A("audio/evrc1");
   A("audio/evrcb");
   A("audio/evrcb0");
   A("audio/evrcb1");
   A("audio/evrcwb");
   A("audio/evrcwb0");
   A("audio/evrcwb1");
   A("audio/example");
   A("audio/flac");
   A("audio/fwdred");
   A("audio/g.722.1");
   A("audio/g719");
   A("audio/g722");
   A("audio/g7221");
   A("audio/g723");
   A("audio/g726-16");
   A("audio/g726-24");
   A("audio/g726-32");
   A("audio/g726-40");
   A("audio/g728");
   A("audio/g729");
   A("audio/g7291");
   A("audio/g729d");
   A("audio/g729e");
   A("audio/gsm");
   A("audio/gsm-efr");
   A("audio/iLBC");
   A("audio/ilbc");
   A("audio/ip-mr_v2.5");
   A("audio/l16");
   A("audio/l20");
   A("audio/l24");
   A("audio/l8");
   A("audio/lpc");
   A("audio/midi");
   A("audio/mobile-xmf");
   A("audio/mp2");
   A("audio/mp4");
   A("audio/mp4a-latm");
   A("audio/mpa");
   A("audio/mpa-robust");
   A("audio/mpeg");
   A("audio/mpeg4-generic");
   A("audio/mpegurl");
   A("audio/ogg");
   A("audio/parityfec");
   A("audio/pcma");
   A("audio/pcma-wb");
   A("audio/pcmu");
   A("audio/pcmu-wb");
   A("audio/prs.sid");
   A("audio/qcelp");
   A("audio/raptorfec");
   A("audio/red");
   A("audio/rtp-enc-aescm128");
   A("audio/rtp-midi");
   A("audio/rtx");
   A("audio/s3m");
   A("audio/silk");
   A("audio/smv");
   A("audio/smv-qcp");
   A("audio/smv0");
   A("audio/sp-midi");
   A("audio/speex");
   A("audio/t140c");
   A("audio/t38");
   A("audio/telephone-event");
   A("audio/tone");
   A("audio/ulpfec");
   A("audio/vdvi");
   A("audio/vmr-wb");
   A("audio/vnd.3gpp.iufp");
   A("audio/vnd.4SB");
   A("audio/vnd.4sb");
   A("audio/vnd.CELP");
   A("audio/vnd.audiokoz");
   A("audio/vnd.celp");
   A("audio/vnd.cisco.nse");
   A("audio/vnd.cmles.radio-events");
   A("audio/vnd.cns.anp1");
   A("audio/vnd.cns.inf1");
   A("audio/vnd.dece.audio");
   A("audio/vnd.digital-winds");
   A("audio/vnd.dlna.adts");
   A("audio/vnd.dolby.heaac.1");
   A("audio/vnd.dolby.heaac.2");
   A("audio/vnd.dolby.mlp");
   A("audio/vnd.dolby.mps");
   A("audio/vnd.dolby.pl2");
   A("audio/vnd.dolby.pl2x");
   A("audio/vnd.dolby.pl2z");
   A("audio/vnd.dolby.pulse.1");
   A("audio/vnd.dra");
   A("audio/vnd.dts");
   A("audio/vnd.dts.hd");
   A("audio/vnd.dvb.file");
   A("audio/vnd.everad.plj");
   A("audio/vnd.hns.audio");
   A("audio/vnd.lucent.voice");
   A("audio/vnd.rn-realaudio");
   A("audio/vnd.ms-playready.media.pya");
   A("audio/vnd.nokia.mobile-xmf");
   A("audio/vnd.nortel.vbk");
   A("audio/vnd.nuera.ecelp4800");
   A("audio/vnd.nuera.ecelp7470");
   A("audio/vnd.nuera.ecelp9600");
   A("audio/vnd.octel.sbc");
   A("audio/vnd.qcelp");
   A("audio/vnd.rhetorex.32kadpcm");
   A("audio/vnd.rip");
   A("audio/vnd.sealedmedia.softseal.mpeg");
   A("audio/vnd.vmx.cvsd");
   A("audio/vorbis");
   A("audio/vorbis-config");
   A("audio/webm");
   A("audio/x-aac");
   A("audio/x-adpcm");
   A("audio/x-aifc");
   A("audio/x-aiff");
   A("audio/x-amzxml");
   A("audio/x-ape");
   A("audio/x-caf");
   A("audio/x-flac");
   A("audio/x-flac+ogg");
   A("audio/x-gsm");
   A("audio/x-iriver-pla");
   A("audio/x-it");
   A("audio/x-m4b");
   A("audio/x-matroska");
   A("audio/x-minipsf");
   A("audio/x-mo3");
   A("audio/x-mod");
   A("audio/x-mpegurl");
   A("audio/x-ms-asx");
   A("audio/x-ms-wax");
   A("audio/x-ms-wma");
   A("audio/x-musepack");
   A("audio/x-opus+ogg");
   A("audio/x-pn-audibleaudio");
   A("audio/x-pn-realaudio");
   A("audio/x-pn-realaudio-plugin");
   A("audio/x-psf");
   A("audio/x-psflib");
   A("audio/x-realaudio");
   A("audio/x-riff");
   A("audio/x-s3m");
   A("audio/x-scpls");
   A("audio/x-sd2");
   A("audio/x-speex");
   A("audio/x-speex+ogg");
   A("audio/x-stm");
   A("audio/x-tta");
   A("audio/x-voc");
   A("audio/x-vorbis+ogg");
   A("audio/x-wav");
   A("audio/x-wavpack");
   A("audio/x-wavpack-correction");
   A("audio/x-xi");
   A("audio/x-xm");
   A("audio/x-xmf");
   A("audio/xm");

   A("video/1d-interleaved-parityfec");
   A("video/3gpp");
   A("video/3gpp-tt");
   A("video/3gpp2");
   A("video/BMPEG");
   A("video/BT656");
   A("video/CelB");
   A("video/DV");
   A("video/H261");
   A("video/H263");
   A("video/H263-1998");
   A("video/H263-2000");
   A("video/H264");
   A("video/H264-RCDO");
   A("video/H264-SVC");
   A("video/JPEG");
   A("video/MJ2");
   A("video/MP1S");
   A("video/MP2P");
   A("video/MP2T");
   A("video/MP4V-ES");
   A("video/MPV");
   A("video/SMPTE292M");
   A("video/annodex");
   A("video/bmpeg");
   A("video/bt656");
   A("video/celb");
   A("video/dl");
   A("video/dv");
   A("video/example");
   A("video/fli");
   A("video/gl");
   A("video/h261");
   A("video/h263");
   A("video/h263-1998");
   A("video/h263-2000");
   A("video/h264");
   A("video/isivideo");
   A("video/jpeg");
   A("video/jpeg2000");
   A("video/jpm");
   A("video/mj2");
   A("video/mp1s");
   A("video/mp2p");
   A("video/mp2t");
   A("video/mp4");
   A("video/mp4v-es");
   A("video/mpeg");
   A("video/mpeg4-generic");
   A("video/mpv");
   A("video/nv");
   A("video/ogg");
   A("video/parityfec");
   A("video/pointer");
   A("video/quicktime");
   A("video/raptorfec");
   A("video/raw");
   A("video/rtp-enc-aescm128");
   A("video/rtx");
   A("video/smpte292m");
   A("video/ulpfec");
   A("video/vc1");
   A("video/vnd.CCTV");
   A("video/vnd.cctv");
   A("video/vnd.dece.hd");
   A("video/vnd.dece.mobile");
   A("video/vnd.dece.mp4");
   A("video/vnd.dece.pd");
   A("video/vnd.dece.sd");
   A("video/vnd.dece.video");
   A("video/vnd.directv.mpeg");
   A("video/vnd.directv.mpeg-tts");
   A("video/vnd.dlna.mpeg-tts");
   A("video/vnd.dvb.file");
   A("video/vnd.fvt");
   A("video/vnd.hns.video");
   A("video/vnd.iptvforum.1dparityfec-1010");
   A("video/vnd.iptvforum.1dparityfec-2005");
   A("video/vnd.iptvforum.2dparityfec-1010");
   A("video/vnd.iptvforum.2dparityfec-2005");
   A("video/vnd.iptvforum.ttsavc");
   A("video/vnd.iptvforum.ttsmpeg2");
   A("video/vnd.motorola.video");
   A("video/vnd.motorola.videop");
   A("video/vnd.mpegurl");
   A("video/vnd.ms-playready.media.pyv");
   A("video/vnd.mts");
   A("video/vnd.nokia.interleaved-multimedia");
   A("video/vnd.nokia.videovoip");
   A("video/vnd.objectvideo");
   A("video/vnd.rn-realvideo");
   A("video/vnd.sealed.mpeg1");
   A("video/vnd.sealed.mpeg4");
   A("video/vnd.sealed.swf");
   A("video/vnd.sealedmedia.softseal.mov");
   A("video/vnd.uvvu.mp4");
   A("video/vnd.vivo");
   A("video/wavelet");
   A("video/webm");
   A("video/x-anim");
   A("video/x-f4v");
   A("video/x-fli");
   A("video/x-flic");
   A("video/x-flv");
   A("video/x-javafx");
   A("video/x-la-asf");
   A("video/x-m4v");
   A("video/x-matroska");
   A("video/x-matroska-3d");
   A("video/x-mng");
   A("video/x-ms-asf");
   A("video/x-ms-vob");
   A("video/x-ms-wm");
   A("video/x-ms-wmp");
   A("video/x-ms-wmv");
   A("video/x-ms-wmx");
   A("video/x-ms-wvx");
   A("video/x-msvideo");
   A("video/x-nsv");
   A("video/x-ogm+ogg");
   A("video/x-sgi-movie");
   A("video/x-smv");
   A("video/x-theora+ogg");

   // hope the other end understands globs?
   A("video/*");
   A("audio/*");
   eldbus_message_iter_container_close(iter, array);
   return EINA_TRUE;
}

GETTER(supported_uri_schemes)
{
   Eldbus_Message_Iter *array = NULL;

   eldbus_message_iter_arguments_append(iter, "as", &array);
   A("file");
   A("http");
   A("https");
   A("rtsp");
   eldbus_message_iter_container_close(iter, array);
   return EINA_TRUE;
}

static const Eldbus_Property properties[] =
{
   PROP_RO("CanQuit",             "b",  can_quit),
   PROP_RW("Fullscreen",          "b",  fullscreen),
   PROP_RO("CanSetFullscreen",    "b",  can_set_fullscreen),
   PROP_RO("CanRaise",            "b",  can_raise),
   PROP_RO("HasTrackList",        "b",  has_track_list),
   PROP_RO("Identity",            "s",  identity),
   PROP_RO("DesktopEntry",        "s",  desktop_entry),
   PROP_RO("SupportedMimeTypes",  "as", supported_mime_types),
   PROP_RO("SupportedUriSchemes", "as", supported_uri_schemes),
   { NULL, NULL, NULL, NULL, 0 }
};

API(quit)
{
   evas_object_del(mainwin);
   return eldbus_message_method_return_new(msg);
}

API(raize)
{
   elm_win_raise(mainwin);
   return eldbus_message_method_return_new(msg);
}

static const Eldbus_Method methods[] =
{
   METHOD("Quit",  NULL, quit),
   METHOD("Raise", NULL, raize),
   { NULL, NULL, NULL, NULL, 0 }
};

static const Eldbus_Service_Interface_Desc desc = {
   "org.mpris.MediaPlayer2", methods, NULL, properties, NULL, NULL
};





/////////////////////////////////////////////////////////////////////////////

GETTER(playback_status)
{
   Inf *inf = evas_object_data_get(mainwin, "inf");
   const char *buf = "Stopped";

   if (!inf) goto end;
   if (browser_visible()) goto end;
   if (video_play_get(inf->vid)) buf = "Playing";
   else buf = "Paused";
end:
   eldbus_message_iter_arguments_append(iter, "s", buf);
   return EINA_TRUE;
}

GETTER(loop_status)
{
   Inf *inf = evas_object_data_get(mainwin, "inf");
   const char *buf = "None";

   if (!inf) goto end;
   if (browser_visible()) goto end;
   if (video_loop_get(inf->vid)) buf = "Track";
   else buf = "None";
//   "Playlist";
end:
   eldbus_message_iter_arguments_append(iter, "s", buf);
   return EINA_TRUE;
}

SETTER(loop_status)
{
   Inf *inf = evas_object_data_get(mainwin, "inf");
   const char *buf = NULL;

   if (!inf) goto end;
   if (browser_visible()) goto end;
   eldbus_message_iter_arguments_get(iter, "s", &buf);
   if (buf)
     {
        if (!strcmp(buf, "None"))
          {
             video_loop_set(inf->vid, EINA_FALSE);
             mpris_loop_status_change();
          }
        else if (!strcmp(buf, "Track"))
          {
             video_loop_set(inf->vid, EINA_TRUE);
             mpris_loop_status_change();
          }
        else if (!strcmp(buf, "Playlist"))
          {
             fprintf(stderr, "Cannot do playlist loop yet.\n");
          }
        if (video_loop_get(inf->vid))
          elm_layout_signal_emit(inf->lay, "action,loop", "rage");
        else
          elm_layout_signal_emit(inf->lay, "action,sequential", "rage");
     }
end:
   return eldbus_message_method_return_new(msg);
}

GETTER(rate)
{
   double rate = 1.0;
   // XXX: Implement playback rate
   eldbus_message_iter_arguments_append(iter, "d", rate);
   return EINA_TRUE;
}

SETTER(rate)
{
   double rate = 1.0;
   // XXX: Implement playback rate
   eldbus_message_iter_arguments_get(iter, "d", &rate);
   return eldbus_message_method_return_new(msg);
}

GETTER(shuffle)
{
   Eina_Bool shuffle = EINA_FALSE;
   // XXX: Implement shuffle mode
   eldbus_message_iter_arguments_append(iter, "b", shuffle);
   return EINA_TRUE;
}

SETTER(shuffle)
{
   Eina_Bool shuffle = EINA_FALSE;
   // XXX: Implement shuffle mode
   fprintf(stderr, "Cannot set shuffle mode yet.\n");
   eldbus_message_iter_arguments_get(iter, "b", &shuffle);
   return eldbus_message_method_return_new(msg);
}

GETTER(metadata)
{
   Inf *inf = evas_object_data_get(mainwin, "inf");
   Eldbus_Message_Iter *array = NULL, *entry = NULL, *var, *var2;
   uint64_t len = 0;
   char *buf = NULL, *tmp = NULL, *p;
   const char *s, *file;

   if (!inf) return EINA_FALSE;
   // XXX: TODO:
   // mpris:trackid

   eldbus_message_iter_arguments_append(iter, "a{sv}", &array);

   s = video_file_get(inf->vid);
   if (s)
     {
        if (s[0] == '/')
          {
             buf = alloca(strlen(s) + sizeof("file://") + 1);
             sprintf(buf, "file://%s", s);
          }
        else if (strstr(s, "://"))
          {
             buf = alloca(strlen(s) + 1);
             strcpy(buf, s);
          }
        else
          {
             char cwd[PATH_MAX];

             if (getcwd(cwd, sizeof(cwd) - 1))
               {
                  cwd[sizeof(cwd) - 1] = 0;
                  buf = alloca(strlen(cwd) + 1 + strlen(s) + sizeof("file://") + 1);
                  sprintf(buf, "file://%s/%s", cwd, s);
               }
          }
     }
   if (buf)
     {
        eldbus_message_iter_arguments_append(array, "{sv}", &entry);
        eldbus_message_iter_basic_append(entry, 's', "xesam:url");
        var = eldbus_message_iter_container_new(entry, 'v', "s");
        eldbus_message_iter_basic_append(var, 's', buf);
        eldbus_message_iter_container_close(entry, var);
        eldbus_message_iter_container_close(array, entry);
     }

   s = video_artfile_get(inf->vid);
   if (s)
     {
        buf = alloca(strlen(s) + sizeof("file://") + 1);
        sprintf(buf, "file://%s", s);
        eldbus_message_iter_arguments_append(array, "{sv}", &entry);
        eldbus_message_iter_basic_append(entry, 's', "mpris:artUrl");
        var = eldbus_message_iter_container_new(entry, 'v', "s");
        eldbus_message_iter_basic_append(var, 's', buf);
        eldbus_message_iter_container_close(entry, var);
        eldbus_message_iter_container_close(array, entry);
     }

   s = video_title_get(inf->vid);
   if (!s) s = video_meta_title_get(inf->vid);
   if ((!s) && (inf->file_cur))
     {
        Winvid_Entry *vid = inf->file_cur->data;
        if (vid)
          {
             file = vid->file;
             s = ecore_file_file_get(vid->uri ? vid->uri->path : file);
             if (s)
               {
                  tmp = alloca(strlen(s) + 1);
                  strcpy(tmp, s);
                  for (p = tmp; *p; p++)
                    {
                       // nuke stupid characters from the label that may be in filename
                       if ((*p == '_') || (*p == '#') || (*p == '$') || (*p == '%') ||
                           (*p == '*') || (*p == '+') || (*p == '[') || (*p == ']') ||
                           (*p == ';') || (*p == '<') || (*p == '=') || (*p == '>') ||
                           (*p == '^') || (*p == '`') || (*p == '{') || (*p == '}') ||
                           (*p == '|') || (*p == '~') || (*p == 127) ||
                           (*p == '\'') || (*p == '\\'))
                         {
                            *p = ' ';
                         }
                       else if (*p == '.')
                         {
                            *p = 0;
                            break;
                         }
                    }
                  s = tmp;
               }
          }
     }
   if (s)
     {
        eldbus_message_iter_arguments_append(array, "{sv}", &entry);
        eldbus_message_iter_basic_append(entry, 's', "xesam:title");
        var = eldbus_message_iter_container_new(entry, 'v', "s");
        eldbus_message_iter_basic_append(var, 's', s);
        eldbus_message_iter_container_close(entry, var);
        eldbus_message_iter_container_close(array, entry);
     }

   s = video_meta_album_get(inf->vid);
   if (s)
     {
        eldbus_message_iter_arguments_append(array, "{sv}", &entry);
        eldbus_message_iter_basic_append(entry, 's', "xesam:album");
        var = eldbus_message_iter_container_new(entry, 'v', "s");
        eldbus_message_iter_basic_append(var, 's', buf);
        eldbus_message_iter_container_close(entry, var);
        eldbus_message_iter_container_close(array, entry);
     }

   s = video_meta_artist_get(inf->vid);
   if (s)
     {
        eldbus_message_iter_arguments_append(array, "{sv}", &entry);
        eldbus_message_iter_basic_append(entry, 's', "xesam:artist");
        var = eldbus_message_iter_container_new(entry, 'v', "as");
        eldbus_message_iter_arguments_append(var, "as", &var2);
        eldbus_message_iter_basic_append(var2, 's', s);
        eldbus_message_iter_container_close(var, var2);
        eldbus_message_iter_container_close(entry, var);
        eldbus_message_iter_container_close(array, entry);
     }

   s = video_meta_comment_get(inf->vid);
   if (s)
     {
        eldbus_message_iter_arguments_append(array, "{sv}", &entry);
        eldbus_message_iter_basic_append(entry, 's', "xesam:comment");
        var = eldbus_message_iter_container_new(entry, 'v', "as");
        eldbus_message_iter_arguments_append(var, "as", &var2);
        eldbus_message_iter_basic_append(var2, 's', s);
        eldbus_message_iter_container_close(var, var2);
        eldbus_message_iter_container_close(entry, var);
        eldbus_message_iter_container_close(array, entry);
     }

   s = video_meta_genre_get(inf->vid);
   if (s)
     {
        eldbus_message_iter_arguments_append(array, "{sv}", &entry);
        eldbus_message_iter_basic_append(entry, 's', "xesam:genre");
        var = eldbus_message_iter_container_new(entry, 'v', "as");
        eldbus_message_iter_arguments_append(var, "as", &var2);
        eldbus_message_iter_basic_append(var2, 's', s);
        eldbus_message_iter_container_close(var, var2);
        eldbus_message_iter_container_close(entry, var);
        eldbus_message_iter_container_close(array, entry);
     }

   len = video_length_get(inf->vid) * 1000000.0;
   eldbus_message_iter_arguments_append(array, "{sv}", &entry);
   eldbus_message_iter_basic_append(entry, 's', "mpris:length");
   var = eldbus_message_iter_container_new(entry, 'v', "x");
   eldbus_message_iter_basic_append(var, 'x', len);
   eldbus_message_iter_container_close(entry, var);
   eldbus_message_iter_container_close(array, entry);

   eldbus_message_iter_container_close(iter, array);
   return EINA_TRUE;
}

GETTER(volume)
{
   Inf *inf = evas_object_data_get(mainwin, "inf");
   double vol = 1.0;

   if (!inf) goto end;
   if (browser_visible()) goto end;
   vol = video_volume_get(inf->vid);
end:
   eldbus_message_iter_arguments_append(iter, "d", vol);
   return EINA_TRUE;
}

SETTER(volume)
{
   Inf *inf = evas_object_data_get(mainwin, "inf");
   double vol = 1.0; // max

   if (!inf) goto end;
   if (browser_visible()) goto end;
   eldbus_message_iter_arguments_get(iter, "d", &vol);
   if (vol < 0.0) vol = 0.0;
   if (vol > 1.0) vol = 1.0;
   video_volume_set(inf->vid, vol);
   mpris_volume_change();
end:
   return eldbus_message_method_return_new(msg);
}

GETTER(position)
{
   Inf *inf = evas_object_data_get(mainwin, "inf");
   uint64_t pos = 0;

   if (!inf) goto end;
   if (browser_visible()) goto end;
   pos = video_position_get(inf->vid) * 1000000.0;
end:
   eldbus_message_iter_arguments_append(iter, "x", pos);
   return EINA_TRUE;
}

GETTER(minimum_rate)
{
   eldbus_message_iter_arguments_append(iter, "d", 1.0);
   return EINA_TRUE;
}

GETTER(maximum_rate)
{
   eldbus_message_iter_arguments_append(iter, "d", 1.0);
   return EINA_TRUE;
}

GETTER(can_go_next)
{
   eldbus_message_iter_arguments_append(iter, "b", EINA_TRUE);
   return EINA_TRUE;
}

GETTER(can_go_previous)
{
   eldbus_message_iter_arguments_append(iter, "b", EINA_TRUE);
   return EINA_TRUE;
}

GETTER(can_play)
{
   eldbus_message_iter_arguments_append(iter, "b", EINA_TRUE);
   return EINA_TRUE;
}

GETTER(can_pause)
{
   eldbus_message_iter_arguments_append(iter, "b", EINA_TRUE);
   return EINA_TRUE;
}

GETTER(can_seek)
{
   eldbus_message_iter_arguments_append(iter, "b", EINA_TRUE);
   return EINA_TRUE;
}

GETTER(can_control)
{
   eldbus_message_iter_arguments_append(iter, "b", EINA_TRUE);
   return EINA_TRUE;
}

static const Eldbus_Property properties_player[] =
{
   PROP_RO("PlaybackStatus", "s", playback_status),
   PROP_RW("LoopStatus",     "s", loop_status),
   PROP_RW("Rate",           "d", rate),
   PROP_RW("Shuffle",        "b", shuffle),
   PROP_RO("Metadata",       "a{sv}", metadata),
   PROP_RW("Volume",         "d", volume),
   PROP_RO("Position",       "x", position),
   PROP_RO("MinimumRate",    "d", minimum_rate),
   PROP_RO("MaximumRate",    "d", maximum_rate),
   PROP_RO("CanGoNext",      "b", can_go_next),
   PROP_RO("CanGoPrevious",  "b", can_go_previous),
   PROP_RO("CanPlay",        "b", can_play),
   PROP_RO("CanPause",       "b", can_pause),
   PROP_RO("CanSeek",        "b", can_seek),
   PROP_RO("CanControl",     "b", can_control),
   { NULL, NULL, NULL, NULL, 0 }
};

API(next)
{
   if (browser_visible()) goto end;
   win_do_next(mainwin);
end:
   return eldbus_message_method_return_new(msg);
}

API(previous)
{
   if (browser_visible()) goto end;
   win_do_prev(mainwin);
end:
   return eldbus_message_method_return_new(msg);
}

API(pause)
{
   if (browser_visible()) goto end;
   win_do_pause(mainwin);
end:
   return eldbus_message_method_return_new(msg);
}

API(play_pause)
{
   if (browser_visible()) goto end;
   win_do_play_pause(mainwin);
end:
   return eldbus_message_method_return_new(msg);
}

API(stop)
{
   Inf *inf = evas_object_data_get(mainwin, "inf");

   if (!inf) goto end;
   if (browser_visible()) goto end;
   video_stop(inf->vid);
   elm_layout_signal_emit(inf->lay, "action,stop", "rage");
   elm_layout_signal_emit(inf->lay, "state,default", "rage");
   if (inf->browse_mode) browser_show(mainwin);
   else evas_object_del(mainwin);
end:
   return eldbus_message_method_return_new(msg);
}

API(play)
{
   if (browser_visible()) goto end;
   win_do_play(mainwin);
end:
   return eldbus_message_method_return_new(msg);
}

API(seek)
{
   Inf *inf = evas_object_data_get(mainwin, "inf");
   uint64_t pos = 0;

   if (!inf) goto end;
   if (browser_visible()) goto end;
   if (!eldbus_message_arguments_get(msg, "x", &pos))
     return eldbus_message_error_new(msg, "Invalid arguments", "Error getting position");
   video_position_set(inf->vid, (double)pos / 1000000.0);
end:
   return eldbus_message_method_return_new(msg);
}

/*
API(set_position)
{
   fprintf(stderr, "Cannot do set_position yet.\n");
   // XXX: get track + position
   return eldbus_message_method_return_new(msg);
}
*/

API(open_uri)
{
   char *uri = NULL;

   if (!eldbus_message_arguments_get(msg, "s", &uri))
     return eldbus_message_error_new(msg, "Invalid arguments", "Error getting URI string");
   win_video_insert(mainwin, uri);
   win_video_next(mainwin);
   win_list_content_update(mainwin);
   browser_hide(mainwin);
   return eldbus_message_method_return_new(msg);
}

static const Eldbus_Method methods_player[] =
{
   METHOD("Next", NULL, next),
   METHOD("Previous", NULL, previous),
   METHOD("Pause", NULL, pause),
   METHOD("PlayPause", NULL, play_pause),
   METHOD("Stop", NULL, stop),
   METHOD("Play", NULL, play),
   METHOD("Seek", ELDBUS_ARGS({"x", "Offset"}), seek),
//   METHOD("SetPosition", ELDBUS_ARGS({ "ox", "Path,Position"}), set_position),
   METHOD("OpenUri", ELDBUS_ARGS({"s", "Uri"}), open_uri),
   { NULL, NULL, NULL, NULL, 0 }
};

static const Eldbus_Signal signals_player[] =
{
   [ 0 ] = { "Seeked", ELDBUS_ARGS({ "x", "Position" }), 0 },
   { NULL, NULL, 0 }
};

static const Eldbus_Service_Interface_Desc desc_player = {
   "org.mpris.MediaPlayer2.Player", methods_player, signals_player, properties_player, NULL, NULL
};

void
mpris_fullscreen_change(void)
{
   if (!iface) return;
   eldbus_service_property_changed(iface, "Fullscreen");
}

void
mpris_volume_change(void)
{
   if (!iface_player) return;
   eldbus_service_property_changed(iface_player, "Volume");
}

void
mpris_loop_status_change(void)
{
   if (!iface_player) return;
   eldbus_service_property_changed(iface_player, "LoopStatus");
}

void
mpris_playback_status_change(void)
{
   if (!iface_player) return;
   eldbus_service_property_changed(iface_player, "PlaybackStatus");
}

void
mpris_position_change(double pos)
{
   uint64_t p = pos * 1000000.0;

   if (!iface_player) return;
   eldbus_service_signal_emit(iface_player, 0, p);
   eldbus_service_property_changed(iface_player, "Position");
}

void
mpris_metadata_change(void)
{
   if (!iface_player) return;
   eldbus_service_property_changed(iface_player, "Metadata");
}

void
mpris_init(Evas_Object *win)
{
   elm_need_eldbus();
   conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION);
   if (!conn) return;
   mainwin = win;
   eldbus_name_request(conn, SERVICE, 0, _cb_name_request, NULL);
   iface = eldbus_service_interface_register(conn, PATH, &desc);
   iface_player = eldbus_service_interface_register(conn, PATH, &desc_player);
}

void
mpris_shutdown(void)
{
   if (!conn) return;
   mainwin = NULL;
//   if (iface_player) eldbus_service_object_unregister(iface_player);
   iface_player = NULL;
//   if (iface) eldbus_service_object_unregister(iface);
   iface = NULL;
//   eldbus_name_release(conn, SERVICE, NULL, NULL);
//   eldbus_connection_unref(conn);
   conn = NULL;
}

#else

void
mpris_fullscreen_change(void)
{
}

void
mpris_volume_change(void)
{
}

void
mpris_loop_status_change(void)
{
}

void
mpris_playback_status_change(void)
{
}

void
mpris_position_change(double pos EINA_UNUSED)
{
}

void
mpris_metadata_change(void)
{
}

void
mpris_init(Evas_Object *win EINA_UNUSED)
{
}

void
mpris_shutdown(void)
{
}

#endif
