//plugin.c:

/*
 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2009-2014
 *
 *  This file is part of RoarAudio PlayList Daemon,
 *  a playlist management daemon for RoarAudio.
 *  See README for details.
 *
 *  This file is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 3
 *  as published by the Free Software Foundation.
 *
 *  RoarAudio 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this software; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 */

#include "rpld.h"

#ifdef SUPPORT_PLUGIN
#include "rpldplugin.h"

static struct roar_plugincontainer * g_plugin_container = NULL;


static ssize_t _config_get_limit(rpldph_config_key_t key) {
 switch (key) {
  // invalid keys:
  case RPLDPH_CONFIG_KEY_DEFAULT_BACKEND:
  case RPLDPH_CONFIG_KEY_APPNAME:
    roar_err_set(ROAR_ERROR_TYPEMM);
    return -1;
   break;
  // valid keys:
  case RPLDPH_CONFIG_KEY_MAX_CLIENTS: return RPLD_CLIENTS_MAX; break;
  case RPLDPH_CONFIG_KEY_MAX_PLAYLISTS: return MAX_PLAYLISTS; break;
  case RPLDPH_CONFIG_KEY_MAX_LISTENS: return RPLD_MAX_LISTEN; break;
  case RPLDPH_CONFIG_KEY_MAX_CODECBL: return PLAYBACK_MAX_CODECBL; break;
  case RPLDPH_CONFIG_KEY_MAX_POINTER: return POINTER_NUM; break;
  case RPLDPH_CONFIG_KEY_DEFAULT_PLF: return RPLD_FF_PLF_DEFAULT; break;
  case RPLDPH_CONFIG_KEY_APPVENDOR: return ROAR_VID_ROARAUDIO; break;
 }

 roar_err_set(ROAR_ERROR_NOENT);
 return -1;
}

static const char * _config_get_value(rpldph_config_key_t key) {
 switch (key) {
  // invalid keys:
  case RPLDPH_CONFIG_KEY_MAX_CLIENTS:
  case RPLDPH_CONFIG_KEY_MAX_PLAYLISTS:
  case RPLDPH_CONFIG_KEY_MAX_LISTENS:
  case RPLDPH_CONFIG_KEY_MAX_CODECBL:
  case RPLDPH_CONFIG_KEY_MAX_POINTER:
    roar_err_set(ROAR_ERROR_TYPEMM);
    return NULL;
   break;
  // valid keys:
  case RPLDPH_CONFIG_KEY_DEFAULT_BACKEND: return BACKEND_DEFAULT_NAME; break;
  case RPLDPH_CONFIG_KEY_APPNAME: return APPLICATION_NAME; break;
  case RPLDPH_CONFIG_KEY_APPVENDOR: return ROAR_VSTR_ROARAUDIO; break;
  case RPLDPH_CONFIG_KEY_DEFAULT_PLF: return "vclt"; break; // TODO: FIXME: use a global const here.
 }

 roar_err_set(ROAR_ERROR_NOENT);
 return NULL;
}

static void * _mm_calloc(size_t nmemb, size_t size) {
 return roar_mm_calloc(nmemb, size);
}
static void * _mm_malloc(size_t size) {
 return roar_mm_malloc(size);
}
static void * _mm_realloc(void *ptr, size_t size) {
 return roar_mm_realloc(ptr, size);
}
static char * _mm_strdup(const char *s) {
 return roar_mm_strdup(s);
}

static int          _error_get(void) {
 return roar_error;
}

#define __ple_check(ret) if ( plent == NULL ) { roar_err_set(ROAR_ERROR_FAULT); return (ret); }
static int                         _ple_set_res     (struct rpld_playlist_entry * plent, const char * file, int codec, time_t length) {
 __ple_check(-1)

 if ( file == NULL )
  file = "";

 rpld_ple_push_filename(plent, file, RPLD_TYPE_UNKNOWN, 1);
 plent->codec = codec;
 plent->length = length;
 return 0;
}
static int                         _ple_set_meta    (struct rpld_playlist_entry * plent, struct roar_keyval * kv, ssize_t len) {
 struct roar_keyval * c;
 ssize_t i;

 if ( plent == NULL || kv == NULL ) {
  roar_err_set(ROAR_ERROR_FAULT);
  return -1;
 }

 for (i = 0; len == -1 ? (kv[i].key != NULL || kv[i].value != NULL) : i < len; i++) {
  c = &(kv[i]);
  if ( 0 ) {
#define _handle_meta_type(type) } else if ( !strcasecmp(c->key, #type) ) { \
                                   strncpy(plent->meta.type, c->value == NULL ? "" : c->value, sizeof(plent->meta.type));
#ifdef __RPLD__FIX_VIM_SYNTAX_HIGHLIGHT__
}
#endif
   _handle_meta_type(album)
   _handle_meta_type(title)
   _handle_meta_type(artist)
   _handle_meta_type(performer)
   _handle_meta_type(version)
  }
 }

 return 0;
}
static const char *                 _ple_get_meta    (struct rpld_playlist_entry * plent, const char * key) {
 const struct roar_keyval * kv;

 __ple_check(NULL)

 if ( !strcasecmp(key, "album") )
  return plent->meta.album;
 if ( !strcasecmp(key, "title") )
  return plent->meta.title;
 if ( !strcasecmp(key, "artist") )
  return plent->meta.artist;
 if ( !strcasecmp(key, "performer") )
  return plent->meta.performer;
 if ( !strcasecmp(key, "version") )
  return plent->meta.version;

 if ( plent->meta.extra.kv != NULL ) {
  kv = roar_keyval_lookup(plent->meta.extra.kv, key, plent->meta.extra.kvlen, 0);
  if ( kv != NULL )
   return kv->value;
 }

 roar_err_set(ROAR_ERROR_NOENT);
 return NULL;
}
static int              _ple_set_meta_int(struct rpld_playlist_entry * plent, const char * key, rpld_maxint_t val) {
 __ple_check(-1)

 if ( !strcasecmp(key, "length") ) {
  plent->length = val;
 } else if ( !strcasecmp(key, "codec") ) {
  plent->codec = val;
 } else if ( !strcasecmp(key, "genre") ) {
  plent->meta.genre = val;
 } else if ( !strcasecmp(key, "tracknum") || !strcasecmp(key, "tracknumber") ) {
  plent->meta.tracknum = val;
 } else if ( !strcasecmp(key, "discid") ) {
  plent->meta.discid = val;
 } else {
  roar_err_set(ROAR_ERROR_NOTSUP);
  return -1;
 }

 return 0;
}

static rpld_maxint_t    _ple_get_meta_int(struct rpld_playlist_entry * plent, const char * key) {
 __ple_check((rpld_maxint_t)-1)

 if ( !strcasecmp(key, "length") )
  return plent->length;
 if ( !strcasecmp(key, "codec") )
  return plent->codec;
 if ( !strcasecmp(key, "genre") )
  return plent->meta.genre;
 if ( !strcasecmp(key, "tracknum") || !strcasecmp(key, "tracknumber") )
  return plent->meta.tracknum;
 if ( !strcasecmp(key, "discid") )
  return (uint32_t)plent->meta.discid;

 roar_err_set(ROAR_ERROR_NOENT);
 return (rpld_maxint_t)-1;
}

static int                          _ple_get_codec   (struct rpld_playlist_entry * plent) {
 __ple_check(-1)
 return plent->codec;
}
static time_t                       _ple_get_length  (struct rpld_playlist_entry * plent) {
 __ple_check((time_t)-1)
 return plent->length;
}
static int                          _ple_set_uuid    (struct rpld_playlist_entry * plent, const char * uuid) {
 if ( plent == NULL || uuid == NULL ) {
  roar_err_set(ROAR_ERROR_FAULT);
  return -1;
 }

 if ( roar_str2uuid(plent->uuid, uuid) == 0 )
  return 0;

 roar_err_set(ROAR_ERROR_ILLSEQ);
 return -1;
}
static const char *                 _ple_get_uuid    (struct rpld_playlist_entry * plent, char * buf) {
 static char buffer[37];

 __ple_check(NULL)

 if ( buf == NULL )
  buf = buffer;

 roar_uuid2str(buf, plent->uuid, sizeof(buffer));

 return buf;
}

static int                          _ple_set_likeness(struct rpld_playlist_entry * plent, float likeness) {
 __ple_check(-1)
 plent->likeness = likeness;
 return 0;
}
static int                          _ple_inc_likeness(struct rpld_playlist_entry * plent, float likeness) {
 __ple_check(-1)
 plent->likeness += likeness;
 return 0;
}
static float                        _ple_get_likeness(struct rpld_playlist_entry * plent) {
 __ple_check(-1)
 return plent->likeness;
}
static struct rpld_playlist_entry * _ple_get_next    (struct rpld_playlist_entry * plent) {
 __ple_check(NULL)
 return plent->list.next;
}

static int     _ple_set_offset(struct rpld_playlist_entry * plent, int_least32_t   start, int_least32_t   end) {
 __ple_check(-1)

 plent->start = start;
 plent->end   = end;

 return 0;
}
static int     _ple_get_offset(struct rpld_playlist_entry * plent, int_least32_t * start, int_least32_t * end) {
 __ple_check(-1)
 if ( start != NULL )
  *start = plent->start;
 if ( end != NULL )
  *end = plent->end;

 return 0;
}

#define _begin(ret) \
 struct rpld_playlist * playlist = rpld_pl_get_by_id(pl); \
 int err; \
 if ( playlist == NULL ) \
  return (ret);

#define _return      \
 err = roar_error;   \
 rpld_pl_unref(playlist);  \
 roar_err_set(err);  \
 return ret;

static rpld_pli_t   _playlist_new  (const char * name, rpld_pli_t parent) {
 struct rpld_playlist * playlist;
 rpld_pli_t ret;

 if ( name == NULL ) {
  roar_err_set(ROAR_ERROR_FAULT);
  return RPLD_PLI_INVALID;
 }

 playlist = rpld_pl_new();
 if ( playlist == NULL )
  return RPLD_PLI_INVALID;

 rpld_pl_set_name(playlist, name);
 if ( parent != RPLD_PLI_INVALID )
  rpld_pl_set_parent(playlist, parent);

 rpld_pl_register(playlist);

 ret = rpld_pl_get_id(playlist);
 rpld_pl_unref(playlist);

 return ret;
}

static void         _playlist_free (rpld_pli_t pl) {
 struct rpld_playlist * playlist = rpld_pl_get_by_id(pl);

 if ( playlist == NULL )
  return;

 rpld_pl_set_name(playlist, NULL);
 rpld_pl_unref(playlist);
}

static int          _playlist_flush(rpld_pli_t pl) {
 const int ret = 0;
 _begin(-1)

 rpld_pl_flush(playlist);

 _return
}

static int          _playlist_merge(rpld_pli_t pl, rpld_pli_t tm) {
 struct rpld_playlist * playlist_tm;
 int ret;
 _begin(-1)

 playlist_tm = rpld_pl_get_by_id(tm);
 if ( playlist_tm == NULL ) {
  ret = -1;
  _return
 }

 ret = rpld_pl_merge(playlist, playlist_tm);

 err = roar_error;
 rpld_pl_unref(playlist_tm);
 roar_err_set(err);

 _return
}

static int          _playlist_set_name(rpld_pli_t pl, const char * name) {
 int ret;
 _begin(-1)

 ret = rpld_pl_set_name(playlist, name);

 _return
}
static const char * _playlist_get_name(rpld_pli_t pl) {
 const char * ret;
 _begin(NULL)

 ret = rpld_pl_get_name(playlist);

 _return
}
static int          _playlist_set_parent(rpld_pli_t pl, rpld_pli_t parent) {
 int ret;
 _begin(-1)

 ret = rpld_pl_set_parent(playlist, parent);

 _return
}
static rpld_pli_t   _playlist_get_parent(rpld_pli_t pl) {
 rpld_pli_t ret;
 _begin(RPLD_PLI_INVALID)

 ret = rpld_pl_get_parent(playlist);

 _return
}

static rpld_pli_t   _playlist_get_by_name(const char * name) {
 struct rpld_playlist * playlist = rpld_pl_get_by_name(name);
 int err;
 rpld_pli_t ret;

 if ( playlist == NULL )
  return RPLD_PLI_INVALID;

 ret = rpld_pl_get_id(playlist);

 _return
}

static ssize_t      _playlist_get_num(rpld_pli_t pl) {
 ssize_t ret;
 _begin(-1)

 ret = rpld_pl_num(playlist);

 _return
}

static int                          _playlist_push     (rpld_pli_t pl, struct rpld_playlist_entry * plent) {
 const int ret = 0;
 _begin(-1)

 rpld_pl_push(playlist, plent);

 _return
}


static struct rpld_playlist_entry * _playlist_pop      (rpld_pli_t pl) {
 struct rpld_playlist_entry * ret;
 _begin(NULL)

 ret = rpld_pl_pop(playlist);

 _return
}

static struct rpld_playlist_entry * _playlist_shift    (rpld_pli_t pl) {
 struct rpld_playlist_entry * ret;
 _begin(NULL)

 ret = rpld_pl_shift(playlist);

 _return
}

static int                          _playlist_add      (rpld_pli_t pl, struct rpld_playlist_entry * plent, ssize_t pos) {
 const int ret = 0;
 _begin(-1)

 rpld_pl_add(playlist, plent, pos);

 _return
}

static int                          _playlist_unshift  (rpld_pli_t pl, struct rpld_playlist_entry * plent) {
 return _playlist_add(pl, plent, -1);
}

static struct rpld_playlist_entry * _playlist_get_first(rpld_pli_t pl) {
 struct rpld_playlist_entry * ret;
 _begin(NULL)

 ret = rpld_pl_get_first(playlist);

 _return
}


static int     _history_set_size(rpld_pli_t history, ssize_t size) {
 struct rpld_playlist * pl = rpld_pl_get_by_id(history);
 int ret;
 int err;

 if ( pl == NULL )
  return -1;

 ret = rpld_pl_set_histsize(pl, size);
 err = roar_error;

 rpld_pl_unref(pl);
 roar_err_set(err);
 return ret;
}

static ssize_t _history_get_size(rpld_pli_t history) {
 struct rpld_playlist * pl = rpld_pl_get_by_id(history);
 ssize_t ret;
 int err;

 if ( pl == NULL )
  return -1;

 ret = rpld_pl_get_histsize(pl);
 err = roar_error;

 rpld_pl_unref(pl);
 roar_err_set(err);
 return ret;
}

static struct rpld_playlist_pointer * _plp_new     (rpld_pli_t playlist, const char * type, const void * argp) {
 struct rpld_playlist_search pls;
 union {
  const void * vp;
  const size_t * num;
  const float * likeness;
  const char * uuid;
 } arg = {.vp = argp};
 struct rpld_playlist * pl;
 struct rpld_playlist_pointer * ret;
 int err;

 if ( type == NULL ) {
  roar_err_set(ROAR_ERROR_FAULT);
  return NULL;
 }

 memset(&pls, 0, sizeof(pls));

 if ( !strcasecmp(type, "num") ) {
  pls.type = RPLD_PL_ST_NUM;
 } else if ( !strcasecmp(type, "uuid") ) {
  pls.type = RPLD_PL_ST_UUID;
 } else if ( !strcasecmp(type, "likeness") ) {
  pls.type = RPLD_PL_ST_LIKENESS;
 } else if ( !strcasecmp(type, "random") ) {
  pls.type = RPLD_PL_ST_RANDOM;
 } else if ( !strcasecmp(type, "randomlike") ) {
  pls.type = RPLD_PL_ST_RANDOM_LIKENESS;
 } else {
  roar_err_set(ROAR_ERROR_NOTSUP);
  return NULL;
 }

 switch (pls.type) {
  case RPLD_PL_ST_UUID:
  case RPLD_PL_ST_NUM:
  case RPLD_PL_ST_LIKENESS:
    if ( argp == NULL ) {
     roar_err_set(ROAR_ERROR_FAULT);
     return NULL;
    }
   break;
  case RPLD_PL_ST_RANDOM:
  case RPLD_PL_ST_RANDOM_LIKENESS:
    if ( playlist == RPLD_PLI_INVALID ) {
     roar_err_set(ROAR_ERROR_INVAL);
     return NULL;
    }
   break;
 }

 switch (pls.type) {
  case RPLD_PL_ST_UUID:
    if ( roar_str2uuid(pls.subject.uuid, arg.uuid) != 0 ) {
     roar_err_set(ROAR_ERROR_ILLSEQ);
     return NULL;
    }
   break;
  case RPLD_PL_ST_NUM:
    pls.subject.num = *arg.num;
   break;
  case RPLD_PL_ST_LIKENESS:
    pls.subject.likeness = *arg.likeness;
   break;
  case RPLD_PL_ST_RANDOM:
  case RPLD_PL_ST_RANDOM_LIKENESS:
    pls.subject.pl = playlist;
   break;
 }

 pl = rpld_pl_get_by_id(playlist); // we can ignore errors here as rpld_plp_init() can handle NULL.
 ret = rpld_plp_init(pl, &pls);
 err = roar_error;

 if ( pl != NULL )
  rpld_pl_unref(pl);

 roar_err_set(err);
 return ret;
}

static int _playback_stop(rpld_pli_t queue) {
 return playback_stop(queue, 0, 1);
}

static int _playback_pause(rpld_pli_t queue, rpldph_bool_t how) {
 switch (how) {
  case RPLDPH_FALSE:  return playback_pause(queue, PLAYBACK_PAUSE_FALSE);  break;
  case RPLDPH_TRUE:   return playback_pause(queue, PLAYBACK_PAUSE_TRUE);   break;
  case RPLDPH_TOGGLE: return playback_pause(queue, PLAYBACK_PAUSE_TOGGLE); break;
  case RPLDPH_ASK:
  case RPLDPH_ERROR:
    // don't do anything, run into the error case below.
   break;
 }

 roar_err_set(ROAR_ERROR_BADRQC);
 return -1;
}

static rpldph_bool_t  _playback_is_playing(rpld_pli_t queue) {
 switch (playback_is_playing(queue)) {
  case 0:  return RPLDPH_FALSE; break;
  case 1:  return RPLDPH_TRUE; break;
  default: return RPLDPH_ERROR; break;
 }
}
static rpldph_bool_t  _playback_is_paused (rpld_pli_t queue) {
 switch (playback_is_pause(queue)) {
  case PLAYBACK_PAUSE_FALSE: return RPLDPH_FALSE; break;
  case PLAYBACK_PAUSE_TRUE: return RPLDPH_TRUE; break;
  default: return RPLDPH_ERROR; break;
 }
}
static rpldph_bool_t  _playback_is_queue  (rpld_pli_t queue) {
 struct rpld_playlist * pl = rpld_pl_get_by_id(queue);
 rpldph_bool_t ret;

 if ( pl == NULL )
  return RPLDPH_ERROR;

 if ( pl->queue == NULL ) {
  ret = RPLDPH_FALSE;
 } else {
  ret = RPLDPH_TRUE;
 }

 rpld_pl_unref(pl);
 return ret;
}

static float _playback_get_volume(rpld_pli_t queue) {
 float ret = playback_get_volume_mu16(queue);

 return ret / 65535.;
}

static int   _playback_set_volume(rpld_pli_t queue, float volume) {
 if ( volume < 0. || volume > 65535. ) {
  roar_err_set(ROAR_ERROR_RANGE);
  return -1;
 }

 volume *= 65535.;

 return playback_set_volume_mu16(queue, (uint16_t)volume);
}

struct roar_connection * _backend_get_con(struct rpld_backend * backend) {
 if ( backend == NULL ) {
  roar_err_set(ROAR_ERROR_FAULT);
  return NULL;
 }

 if ( !(backend->flags & BACKEND_FLAG_CONNECTED) ) {
  roar_err_set(ROAR_ERROR_NOTCONN);
  return NULL;
 }

 if ( roar_connectionref(&(backend->con)) == -1 )
  return NULL;

 return &(backend->con);
}

static int            _fformat_playlist_export(rpld_fformat_t handle,
                                    struct roar_vio_calls * vio,
                                    rpld_pli_t pl) {
 struct rpld_playlist * realpl;
 int ret;

 realpl = rpld_pl_get_by_id(pl);
 if ( realpl == NULL )
  return -1;

 ret = fformat_playlist_export((struct fformat_handle *)handle, vio, realpl);

 rpld_pl_unref(realpl);

 return ret;
}

static int            _fformat_playlist_import(rpld_fformat_t handle,
                                    struct roar_vio_calls * vio,
                                    rpld_pli_t pl) {
 struct rpld_playlist * realpl;
 int ret;

 realpl = rpld_pl_get_by_id(pl);
 if ( realpl == NULL )
  return -1;

 ret = fformat_playlist_import((struct fformat_handle *)handle, vio, realpl);

 rpld_pl_unref(realpl);

 return ret;
}

static inline void _yield_ay(enum yield_auto cmd) {
 yield_auto(cmd, RPLD_YIELD_TASK_NONE, RPLD_YIELD_TASK_ALL, RPLD_YIELD_NONBLOCK);
}
static void _yield_ay_trigger(void) {
 _yield_ay(RPLD_YIELD_TRIGGER);
}
static void _yield_ay_pause(void) {
 _yield_ay(RPLD_YIELD_PAUSE);
}
static void _yield_ay_unpause(void) {
 _yield_ay(RPLD_YIELD_UNPAUSE);
}

static struct __rpldph_binargv __binargv = {
 .functions = {
  .config = {
   ._get_limit = _config_get_limit,
   ._get_value = _config_get_value,
  },
  .memory = {
   ._calloc  = _mm_calloc,
   ._malloc  = _mm_malloc,
   ._free    = roar_mm_free_retvoid,
   ._realloc = _mm_realloc,
   ._strdup  = _mm_strdup
  },
  .error = {
   ._get = _error_get,
   ._set = roar_err_set,
   ._error2str = roar_error2str,
  },
  .control = {
   ._exit = rpld_exit,
   ._serverinfo = rpld_serverinfo_get
  },
  .ple = {
   ._new = rpld_ple_new,
   ._free = rpld_ple_free,
   ._copy = rpld_ple_copy,
   ._set_res = _ple_set_res,
   ._set_meta = _ple_set_meta,
   ._get_meta = _ple_get_meta,
   ._set_meta_int = _ple_set_meta_int,
   ._get_meta_int = _ple_get_meta_int,
   ._get_codec = _ple_get_codec,
   ._get_length = _ple_get_length,
   ._set_uuid = _ple_set_uuid,
   ._get_uuid = _ple_get_uuid,
   ._set_likeness = _ple_set_likeness,
   ._inc_likeness = _ple_inc_likeness,
   ._get_likeness = _ple_get_likeness,
   ._get_next = _ple_get_next,
   ._set_offset = _ple_set_offset,
   ._get_offset = _ple_get_offset
  },
  .playlist = {
   ._new = _playlist_new,
   ._free = _playlist_free,
   ._flush = _playlist_flush,
   ._merge = _playlist_merge,
   ._set_name = _playlist_set_name,
   ._get_name = _playlist_get_name,
   ._set_parent = _playlist_set_parent,
   ._get_parent = _playlist_get_parent,
   ._get_by_name = _playlist_get_by_name,
   ._get_num = _playlist_get_num,
   ._push = _playlist_push,
   ._unshift = _playlist_unshift,
   ._pop = _playlist_pop,
   ._shift = _playlist_shift,
   ._add = _playlist_add,
   ._get_first = _playlist_get_first
  },
  .history = {
   ._set_size = _history_set_size,
   ._get_size = _history_get_size
  },
  .plp = {
   ._new    = _plp_new,
   ._ref    = rpld_plp_ref,
   ._unref  = rpld_plp_unref,
   ._copy   = rpld_plp_copy,
   ._search = rpld_plp_search
  },
  .playback = {
   ._play  = playback_play,
   ._next  = playback_next,
   ._prev  = playback_prev,
   ._stop  = _playback_stop,
   ._pause = _playback_pause,

   ._is_playing = _playback_is_playing,
   ._is_paused  = _playback_is_paused,
   ._is_queue   = _playback_is_queue,

   ._get_volume = _playback_get_volume,
   ._set_volume = _playback_set_volume,

   ._get_ple = playback_get_ple,
   ._get_mduc = playback_get_mduc,
   ._get_stream = playback_get_stream
  },
  .store = {
   ._save = store,
   ._load = restore
  },
  .backend = {
   ._get_connection = backend_get_connection,

   ._ref = backend_ref,
   ._unref = backend_unref,

   ._get_name = backend_get_name,
   ._get_con = _backend_get_con,

   ._blacklist_codec = backend_blacklist_codec,
   ._blacklist_codec_global = backend_blacklist_codec_global,
   ._blacklist_check_codec = backend_blacklist_check_codec
  },
  .client = {
   ._set_name = client_set_name,
   ._get_name = client_get_name,
   ._set_playlist = client_set_playlist,
   ._get_playlist = client_get_playlist,
   ._set_queue = client_set_queue,
   ._get_queue = client_get_queue,

   ._str2proto = client_str2proto,
   ._proto2str = client_proto2str
  },
  .fformat = {
   ._str2plf = fformat_str2plf,
   ._plf2str = fformat_plf2str,

   ._handle_new = (rpld_fformat_t (*)(int format))fformat_handle_new,
   ._handle_close = (void (*)(rpld_fformat_t handle))fformat_handle_close,

   ._playlist_export = _fformat_playlist_export,
   ._playlist_import = _fformat_playlist_import
  },
  .pointer = {
   ._get_name = rpld_pointer_get_name,
   ._parse_name = rpld_pointer_parse_name,
   ._is_set = rpld_pointer_is_set,
   ._get = rpld_pointer_get,
   ._get_by_name = rpld_pointer_get_by_name,
   ._set = rpld_pointer_set,
   ._set_by_name = rpld_pointer_set_by_name,
   ._search = rpld_pointer_search,
  },
  .yield = {
   ._ay_trigger = _yield_ay_trigger,
   ._ay_pause   = _yield_ay_pause,
   ._ay_unpause = _yield_ay_unpause,
   ._watchdog_trigger = yield_watchdog_trigger
  }
 }
};

int rpld_plugin_init(void) {
 struct roar_dl_librarypara * plugin_para;
 int err;

 if ( g_plugin_container != NULL ) {
  roar_err_set(ROAR_ERROR_ALREADY);
  return -1;
 }

 plugin_para = roar_dl_para_new(NULL, &__binargv, RPLDPH_APPNAME, RPLDPH_ABIVERSION);

 g_plugin_container = roar_plugincontainer_new(plugin_para);
 err = roar_error;

 roar_dl_para_unref(plugin_para);

 if ( g_plugin_container == NULL ) {
  roar_err_set(err);
  return -1;
 } else {
  return 0;
 }
}

int rpld_plugin_free(void) {
 if ( g_plugin_container == NULL ) {
  roar_err_set(ROAR_ERROR_FAULT);
  return -1;
 }

 rpld_plugin_appsched(ROAR_DL_APPSCHED_FREE);
 roar_plugincontainer_unref(g_plugin_container);

 g_plugin_container = NULL;

 return 0;
}

int rpld_plugin_load(const char * plugin, const char * args) {
 struct roar_dl_librarypara * plugin_para = NULL;
 int ret;

 if ( args != NULL )
  plugin_para = roar_dl_para_new(args, &__binargv, RPLDPH_APPNAME, RPLDPH_ABIVERSION);

 ret = roar_plugincontainer_load(g_plugin_container, plugin, plugin_para);

 if ( plugin_para != NULL )
  roar_dl_para_unref(plugin_para);

 return ret;
}

int rpld_plugin_appsched(enum roar_dl_appsched_trigger trigger) {
 return roar_plugincontainer_appsched_trigger(g_plugin_container, trigger);
}

#endif

//ll
