/*
 * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <glib.h>
#include <assert.h>
#include <pthread.h>
#include "audiomanager_proxy.h"
#include "sm-helper.h"
#include "dbus/audio_manager_interface.h"

static AudiomanagerCommandinterface *am_cmd_bus;
static AudiomanagerRoutinginterface *am_route_bus;
static AudiomanagerRoutingSoundmanager *sm_adapter;
static AudiomanagerRoutingSoundmanagerIface* sm_itf;
static GDBusConnection* system_conn = NULL;

static am_event _am_event = {0};
static am_instruction _am_instruction = {0};
static void *dbus_event_loop_run(void *args);

/*
********** Callback Function invoked by Audio Manager Command Interface**********
*/
static void _on_new_main_connection(AudiomanagerCommandinterface* interface,
    GVariant* mainConnection)
{
    AFB_DEBUG("%s is called",__FUNCTION__);

    guint16 mcid, srcid, sinkid;
    gint16 delay, constate;
    g_variant_get(
        mainConnection,"(qqqnn)", &mcid, &srcid, &sinkid, &delay, &constate);

    _am_event.on_new_main_connection(mcid, srcid, sinkid, delay, constate);
}

static void _on_removed_main_connection(
    AudiomanagerCommandinterface* interface, guint16 mainConnectionID)
{
    AFB_DEBUG("%s is called",__FUNCTION__);

    _am_event.on_removed_main_connection(mainConnectionID);
}

static void _on_main_connection_state_changed(
    AudiomanagerCommandinterface* interface, guint16 connectionID, gint16 connectionState)
{
    AFB_DEBUG("%s is called",__FUNCTION__);
    _am_event.on_main_connection_state_changed(connectionID, connectionState);
}

static void _on_volume_changed(
            AudiomanagerCommandinterface* interface, guint16 sinkID, gint16 volume)
{
    AFB_DEBUG("%s is called",__FUNCTION__);
    _am_event.on_volume_changed(sinkID, volume);
}

static void _on_sink_mute_state_changed(
            AudiomanagerCommandinterface* interface, guint16 sinkID, gint16 mute)
{
    AFB_DEBUG("%s is called",__FUNCTION__);
    _am_event.on_sink_mute_state_changed(sinkID, mute);
}

/*
********** Callback Function invoked by Audio Manager Routing Interface**********
*/
static void _on_set_routing_ready(
            AudiomanagerRoutinginterface* interface)
{
    AFB_DEBUG("%s is called",__FUNCTION__);
    _am_event.on_set_routing_ready();
}

static void _on_set_routing_rundown(
            AudiomanagerRoutinginterface* interface)
{
    AFB_DEBUG("%s is called",__FUNCTION__);
    _am_event.on_set_routing_rundown();
}

static ErrorCode check_send_error(GError *err, guint16 result){
    if(err != NULL){
        return UNABLE_SEND;
    }
    return result;
}

static gboolean _on_async_abort(
    AudiomanagerRoutingSoundmanager *object,
    GDBusMethodInvocation *invocation,
    guint16 arg_handle)
{
    AFB_DEBUG( "%s called", __FUNCTION__);
    _am_instruction.on_async_abort((int)arg_handle);
    return TRUE;
}

static gboolean _on_async_connect(
    AudiomanagerRoutingSoundmanager *object,
    GDBusMethodInvocation *invocation,
    guint16 arg_handle,
    guint16 arg_connectionID,
    guint16 arg_sourceID,
    guint16 arg_sinkID,
    gint arg_connectionFormat)
{
    AFB_DEBUG( "%s called", __FUNCTION__);
    int handle = (int)arg_handle;
    int connection = (int)arg_connectionID;
    int source = (int)arg_sourceID;
    int sink   = (int)arg_sinkID;
    int connection_format = (int)arg_connectionFormat;
    _am_instruction.on_async_connect(handle, connection,
        source, sink, connection_format);
    int ack_ok = 0;
    ErrorCode ec = am_proxy_ack_connect(handle, connection, ack_ok);
    if(ec == UNABLE_SEND)
    {
        AFB_ERROR( "Can't send ackConnect", __FUNCTION__);
        return FALSE;
    }
    return TRUE;
}

static gboolean _on_async_disconnect(
    AudiomanagerRoutingSoundmanager *object,
    GDBusMethodInvocation *invocation,
    guint16 arg_handle,
    guint16 arg_connectionID)
{
    AFB_DEBUG( "%s called", __FUNCTION__);
    int handle = (int)arg_handle;
    int connection = (int)arg_connectionID;
    _am_instruction.on_async_disconnect(handle, connection);
    GError* err = NULL;
    int ack_ok = 0;

    ErrorCode ec = am_proxy_ack_disconnect(handle, connection, ack_ok);
    if(ec == UNABLE_SEND)
    {
        AFB_ERROR( "Can't send ack to sound manager adapter %s", __FUNCTION__);
        return FALSE;
    }
    return TRUE;
}

static gboolean _on_async_set_sink_volume(
    AudiomanagerRoutingSoundmanager *object,
    GDBusMethodInvocation *invocation,
    guint16 arg_handle,
    guint16 arg_sinkID,
    gint16 arg_volume,
    gint16 arg_ramp,
    guint16 arg_time)
{
    AFB_DEBUG( "%s called", __FUNCTION__);
    int handle = (int)arg_handle;
    int sink   = (int)arg_sinkID;
    int volume = (int)arg_volume;
    int ramp = (int)arg_ramp;
    int time = (int)arg_time;
    GError* err = NULL;
    int ack_ok = 0;
    ErrorCode ec = audiomanager_routinginterface_call_ack_set_sink_volume_sync(
        am_route_bus,
        arg_handle,
        arg_volume,
        ack_ok, NULL, &err);
    if(ec == UNABLE_SEND);{
        AFB_ERROR( "Can't send ack to sound manager adapter %s", __FUNCTION__);
        return FALSE;
    }
    return TRUE;
}

static gboolean _on_async_set_source_state(
    AudiomanagerRoutingSoundmanager *object,
    GDBusMethodInvocation *invocation,
    guint16 arg_handle,
    guint16 arg_sourceID,
    gint arg_sourceState)
{
    int handle = (int)arg_handle;
    int source = (int)arg_sourceID;
    int source_state   = (int)arg_sourceState;
    AFB_DEBUG( "%s called", __FUNCTION__);
    _am_instruction.on_async_set_source_state(handle, source, source_state);
    return TRUE;
}

/*
********** API which calls audio manager API **********
*/
ErrorCode am_proxy_connect(int source, int sink, int *main_connection_id){
    if(is_range_over_guint16(source) == OUT_RANGE || is_range_over_guint16(sink) == OUT_RANGE){
        return OUT_RANGE;
    }
    AFB_DEBUG("Call %s: source:%d sink:%d", __FUNCTION__, source, sink);

    guint16 connection_id = 0, ret = 0;
    ErrorCode ec;
    GError *err = NULL;

    assert(am_cmd_bus != NULL);

    audiomanager_commandinterface_call_connect_sync(
        am_cmd_bus,
        (guint16)source, (guint16)sink,
        &ret, &connection_id,
        NULL, &err);
    *main_connection_id = (int)connection_id;
    ec = check_send_error(err, ret);
    return ec;
}

ErrorCode am_proxy_disconnect(int main_connection_id){
    AFB_DEBUG("Call %s: mainConnectionID:%d", __FUNCTION__, main_connection_id);
    if(is_range_over_guint16(main_connection_id) == OUT_RANGE){
        return OUT_RANGE;
    }
    guint16 ret = 0;
    ErrorCode ec;
    GError *err = NULL;

    assert(am_cmd_bus != NULL);
    audiomanager_commandinterface_call_disconnect_sync(
        am_cmd_bus,
        (guint16)main_connection_id,
        &ret,
        NULL, &err);
    ec = check_send_error(err, ret);
    return ec;
}

ErrorCode am_proxy_set_volume(int sink, int volume){
    AFB_DEBUG("Call %s: sink:%d volume:%d", __FUNCTION__, sink, volume);
    if(is_range_over_guint16(sink) == OUT_RANGE && is_range_over_gint16(volume) == OUT_RANGE){
        return OUT_RANGE;
    }
    guint16 ret = 0;
    ErrorCode ec;
    GError *err = NULL;

    assert(am_cmd_bus != NULL);
    audiomanager_commandinterface_call_set_volume_sync(
        am_cmd_bus,
        (guint16)sink,
        (gint16)volume,
        &ret, NULL, &err);
    ec = check_send_error(err, ret);
    return ec;
}

ErrorCode am_proxy_volume_step(int sink, int volume){
    AFB_DEBUG("Call %s: sink:%d volume:%d", __FUNCTION__, sink, volume);
    if(is_range_over_guint16(sink) == OUT_RANGE && is_range_over_gint16(volume) == OUT_RANGE){
        return OUT_RANGE;
    }
    guint16 ret = 0;
    ErrorCode ec;
    GError *err = NULL;

    assert(am_cmd_bus != NULL);
    audiomanager_commandinterface_call_volume_step_sync(
        am_cmd_bus,
        (guint16)sink,
        (gint16)volume,
        &ret, NULL, &err);
    ec = check_send_error(err, ret);
    return ec;
}

ErrorCode am_proxy_set_sink_mute_state(int sink, int mute_state){
    AFB_DEBUG("Call %s: sink:%d mute_state:%d", __FUNCTION__, sink, mute_state);
    if(is_range_over_guint16(sink) == OUT_RANGE || is_range_over_gint16(mute_state) == OUT_RANGE){
        return OUT_RANGE;
    }
    guint16 ret = 0;
    ErrorCode ec;
    GError *err = NULL;

    assert(am_cmd_bus != NULL);
    audiomanager_commandinterface_call_set_sink_mute_state_sync(
        am_cmd_bus,
        (guint16)sink, (gint16)mute_state,
        &ret, NULL, &err);
    ec = check_send_error(err, ret);
    return ec;
}

ErrorCode am_proxy_get_list_main_connections(GVariant** connection_list){
    guint16 ret = 0;
    ErrorCode ec;
    GError *err = NULL;

    AFB_DEBUG("Call %s", __FUNCTION__);
    assert(am_cmd_bus != NULL);
    audiomanager_commandinterface_call_get_list_main_connections_sync(
        am_cmd_bus,
        &ret,
        connection_list,
        NULL,
        &err
    );
    ec = check_send_error(err, ret);
    return ec;
}

ErrorCode am_proxy_get_list_main_sources(GVariant** source_list){
    guint16 ret = 0;
    ErrorCode ec;
    GError *err = NULL;

    AFB_DEBUG("Call %s", __FUNCTION__);
    assert(am_cmd_bus != NULL);
    audiomanager_commandinterface_call_get_list_main_sources_sync(
        am_cmd_bus,
        &ret,
        source_list,
        NULL,
        &err
    );
    ec = check_send_error(err, ret);
    return ec;
}

ErrorCode am_proxy_get_list_main_sinks(GVariant** sink_list){
    guint16 ret = 0;
    ErrorCode ec;
    GError *err = NULL;

    AFB_DEBUG("Call %s", __FUNCTION__);
    assert(am_cmd_bus != NULL);
    audiomanager_commandinterface_call_get_list_main_sinks_sync(
        am_cmd_bus,
        &ret,
        sink_list,
        NULL,
        &err
    );
    ec = check_send_error(err, ret);
    return ec;
}

ErrorCode am_proxy_ack_connect(int handle, int connection_id, int usr_err){
    AFB_DEBUG("Call %s handle:%d connectionID:%d error:%d", __FUNCTION__, handle, connection_id, usr_err);
    if(is_range_over_guint16(handle) == OUT_RANGE ||
       is_range_over_guint16(connection_id) == OUT_RANGE ||
       is_range_over_guint16(usr_err) == OUT_RANGE){
        return OUT_RANGE;
    }

    assert(am_route_bus != NULL);
    GError *err = NULL;

    audiomanager_routinginterface_call_ack_connect_sync(
        am_route_bus,
        (guint16)handle,
        (guint16)connection_id,
        (guint16)usr_err, NULL, &err);
    return OK;
}

ErrorCode am_proxy_ack_disconnect(int handle, int connection_id, int usr_err){
    AFB_DEBUG("Call %s handle:%d connectionID:%d error:%d", __FUNCTION__, handle, connection_id, usr_err);
    if(is_range_over_guint16(handle) == OUT_RANGE ||
       is_range_over_guint16(connection_id) == OUT_RANGE ||
       is_range_over_guint16(usr_err) == OUT_RANGE){
        return OUT_RANGE;
    }
    assert(am_route_bus != NULL);
    GError *err = NULL;
    audiomanager_routinginterface_call_ack_disconnect_sync(
        am_route_bus,
        (guint16)handle,
        (guint16)connection_id,
        (guint16)usr_err,NULL, &err);
    return OK;
}

ErrorCode am_proxy_ack_set_source_state(int handle, int usr_err){
    AFB_DEBUG("Call %s handle:%d error:%d", __FUNCTION__, handle, usr_err);
    if(is_range_over_guint16(handle) == OUT_RANGE ||
       is_range_over_guint16(usr_err) == OUT_RANGE){
        return OUT_RANGE;
    }

    assert(am_route_bus != NULL);
    GError *err = NULL;
    audiomanager_routinginterface_call_ack_set_source_state_sync(
        am_route_bus,
        (guint16)handle,
        (guint16)usr_err,
        NULL, &err);
    return OK;
}

ErrorCode am_proxy_register_source(GVariant *source_data, int *source){
    AFB_DEBUG("Call %s", __FUNCTION__);
    assert(am_route_bus != NULL);
    guint16 ret = 0, g_source = 0;
    ErrorCode ec;
    GError *err = NULL;

    audiomanager_routinginterface_call_register_source_sync(
        am_route_bus,
        source_data,
        &g_source,
        &ret,
        NULL, &err);

    *source = (int)g_source;
    ec = check_send_error(err, ret);
    return ec;
}

ErrorCode am_proxy_deregister_source(int source){
    AFB_DEBUG("Call %s source:%d", __FUNCTION__, source);
    if(is_range_over_guint16(source) == OUT_RANGE){
        return OUT_RANGE;
    }
    guint16 ret = 0;
    ErrorCode ec;
    GError *err = NULL;

    assert(am_route_bus != NULL);
    audiomanager_routinginterface_call_deregister_source_sync(
        am_route_bus,
        (guint16)source,
        &ret,
        NULL, &err
    );
    ec = check_send_error(err, ret);
    return ec;
}

ErrorCode am_proxy_register_domain(GVariant* domain_data, int *domain){
    guint16 ret = 0, g_domain = 0;
    ErrorCode ec;
    GError *err = NULL;

    assert(am_route_bus != NULL);
    assert(domain_data);
    AFB_DEBUG("Call %s", __FUNCTION__);
    audiomanager_routinginterface_call_register_domain_sync(
        am_route_bus,
        domain_data,
        SOUND_MANAGER_BUS_NAME,
        SOUND_MANAGER_PATH,
        SOUND_MANAGER_RETURN_INTERFACE,
        &g_domain, &ret,
        NULL, &err);

    *domain = (int)g_domain;
    ec = check_send_error(err, ret);
    return ec;
}

GVariant* create_domain_data(struct domain_data* domain){
    /*
    *  This function doesn't check the range of guint32 or gint16
    *  Soundmanager must keep the input data is between the size of type.
    *  This function is considered to be only used by soundmanager.
    */
    GVariantBuilder builder;
    g_variant_builder_init (&builder, G_VARIANT_TYPE ("(qsssbbn)"));
    g_variant_builder_add (&builder, "q", (guint16)domain->domainID);
    g_variant_builder_add (&builder, "s", domain->name);
    g_variant_builder_add (&builder, "s", domain->busname);
    g_variant_builder_add (&builder, "s", domain->nodename);
    g_variant_builder_add (&builder, "b", (gboolean)domain->early);
    g_variant_builder_add (&builder, "b", (gboolean)domain->complete);
    g_variant_builder_add (&builder, "n", (gint16)domain->state);
    AFB_DEBUG("create domainData");
    return g_variant_builder_end (&builder);
}

GVariant* create_source_data(int sourceID, int domainID, const char* appname, int sourceClassID,
    int sourceState, int volume, bool visible, struct availability_s availables,
    int interrupt,  struct sound_property_s soundPropertyList, int connectionFormatList,
    struct main_sound_property_s mainPropertyList, struct notification_config_s NConfRouting,
    struct notification_config_s NConfCommand)
{
    GVariantBuilder builder;
    if(is_range_over_guint16(sourceID) == OUT_RANGE ||
        is_range_over_guint16(domainID) == OUT_RANGE ||
        is_range_over_gint16(volume) == OUT_RANGE ||
        is_range_over_guint16(interrupt) == OUT_RANGE ||
        is_range_over_gint16(soundPropertyList.value) == OUT_RANGE ||
        is_range_over_gint16(mainPropertyList.value) == OUT_RANGE ||
        is_range_over_gint16(NConfRouting.parameter) == OUT_RANGE ||
        is_range_over_gint16(NConfCommand.parameter) == OUT_RANGE)
    {
        return NULL;
    }
    g_variant_builder_init (&builder, G_VARIANT_TYPE ("(qqsqinb(ii)qa(in)aia(in)a(iin)a(iin))"));
    g_variant_builder_add (&builder, "q", (guint16)sourceID);
    g_variant_builder_add (&builder, "q", (guint16)domainID);
    g_variant_builder_add (&builder, "s", appname);
    g_variant_builder_add (&builder, "q", (guint16)sourceClassID);
    g_variant_builder_add (&builder, "i", (gint32)sourceState);
    g_variant_builder_add (&builder, "n", (gint16)volume);
    g_variant_builder_add (&builder, "b", (gboolean)visible);
    g_variant_builder_add (&builder, "(ii)", (gint32)availables.availability, (gint32)availables.avalilable_reason);
    g_variant_builder_add (&builder, "q", (guint16)interrupt);

    g_variant_builder_open(&builder, G_VARIANT_TYPE("a(in)"));
    g_variant_builder_open(&builder, G_VARIANT_TYPE("(in)"));
    g_variant_builder_add (&builder, "i", (gint32)soundPropertyList.type);
    g_variant_builder_add (&builder, "n", (gint16)soundPropertyList.value);
    g_variant_builder_close(&builder);
    g_variant_builder_close (&builder);

    g_variant_builder_open(&builder, G_VARIANT_TYPE("ai"));
    g_variant_builder_add (&builder, "i", (gint32)connectionFormatList);
    g_variant_builder_close (&builder);

    g_variant_builder_open(&builder, G_VARIANT_TYPE("a(in)"));
    g_variant_builder_open(&builder, G_VARIANT_TYPE("(in)"));
    g_variant_builder_add (&builder, "i", (gint32)mainPropertyList.type);
    g_variant_builder_add (&builder, "n", (gint16)mainPropertyList.value);
    g_variant_builder_close (&builder);
    g_variant_builder_close(&builder);

    g_variant_builder_open(&builder, G_VARIANT_TYPE("a(iin)"));
    g_variant_builder_open(&builder, G_VARIANT_TYPE("(iin)"));
    g_variant_builder_add (&builder, "i", (gint32)NConfRouting.type);
    g_variant_builder_add (&builder, "i", (gint32)NConfRouting.status);
    g_variant_builder_add (&builder, "n", (gint16)NConfRouting.parameter);
    g_variant_builder_close(&builder);
    g_variant_builder_close (&builder);


    g_variant_builder_open(&builder, G_VARIANT_TYPE("a(iin)"));
    g_variant_builder_open(&builder, G_VARIANT_TYPE("(iin)"));
    g_variant_builder_add (&builder, "i", (gint32)NConfCommand.type);
    g_variant_builder_add (&builder, "i", (gint32)NConfCommand.status);
    g_variant_builder_add (&builder, "n", (gint16)NConfCommand.parameter);
    g_variant_builder_close(&builder);
    g_variant_builder_close (&builder);

    AFB_DEBUG("create sourceData");
    return g_variant_builder_end (&builder);
}

static void *dbus_event_loop_run(void *args)
{
    GMainLoop* loop = g_main_loop_new(NULL, FALSE);
    g_main_loop_run(loop);
}

ErrorCode initialize_proxy(){
    if(am_cmd_bus || am_route_bus)
    {
        return NOT_INITIALIZED;
    }
    am_cmd_bus = audiomanager_commandinterface_proxy_new_for_bus_sync(
        G_BUS_TYPE_SYSTEM,
        G_DBUS_PROXY_FLAGS_NONE,
        AM_NAME,
        AM_CMD_PATH,
        NULL,
        NULL
        );
    if(!am_cmd_bus){
        AFB_ERROR("Failed to create command interface");
    }
    am_route_bus = audiomanager_routinginterface_proxy_new_for_bus_sync(
        G_BUS_TYPE_SYSTEM,
        G_DBUS_PROXY_FLAGS_NONE,
        AM_NAME,
        AM_ROUTE_PATH,
        NULL,
        NULL
        );
    if(!am_route_bus){
        AFB_ERROR("Failed to create routing interface");
    }
    return (am_cmd_bus && am_route_bus) ? OK : UNABLE_SEND;
}

void close_proxy(){
    if(am_cmd_bus) free(am_cmd_bus);
    if(am_route_bus) free(am_route_bus);
}

void set_event_callback(const am_event* callback){
    assert(am_cmd_bus != NULL);
    assert(am_route_bus != NULL);
    /* initialize signal from audio manager command interface */
    _am_event.on_main_connection_state_changed = callback->on_main_connection_state_changed;
    _am_event.on_new_main_connection = callback->on_new_main_connection;
    _am_event.on_removed_main_connection = callback->on_removed_main_connection;
    _am_event.on_set_routing_ready = callback->on_set_routing_ready;
    _am_event.on_set_routing_rundown = callback->on_set_routing_rundown;
    _am_event.on_sink_mute_state_changed = callback->on_sink_mute_state_changed;
    _am_event.on_volume_changed = callback->on_volume_changed;

    g_signal_connect(am_cmd_bus, "volume_changed",
        G_CALLBACK(_on_volume_changed),         NULL);
    g_signal_connect(am_cmd_bus, "new_main_connection",
        G_CALLBACK(_on_new_main_connection),    NULL);
    g_signal_connect(am_cmd_bus, "removed_main_connection",
        G_CALLBACK(_on_removed_main_connection),NULL);
    g_signal_connect(am_cmd_bus, "sink_mute_state_changed",
        G_CALLBACK(_on_sink_mute_state_changed),NULL);
    g_signal_connect(am_cmd_bus, "main_connection_state_changed",
        G_CALLBACK(_on_main_connection_state_changed), NULL);
    g_signal_connect(am_route_bus, "set_routing_ready",
        G_CALLBACK(_on_set_routing_ready),      NULL);
    g_signal_connect(am_route_bus, "set_routing_rundown",
        G_CALLBACK(_on_set_routing_rundown),    NULL);
}

static void on_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data){
    GError* error = NULL;
    AFB_DEBUG("%s is called", __FUNCTION__);

    sm_adapter = audiomanager_routing_soundmanager_skeleton_new();
    sm_itf = AUDIOMANAGER_ROUTING_SOUNDMANAGER_GET_IFACE(sm_adapter);

    /* initialize sound manager adapter */
    sm_itf->handle_async_abort            = _on_async_abort;
    sm_itf->handle_async_connect          = _on_async_connect;
    sm_itf->handle_async_disconnect       = _on_async_disconnect;
    sm_itf->handle_async_set_sink_volume  = _on_async_set_sink_volume;
    sm_itf->handle_async_set_source_state = _on_async_set_source_state;

    int sigret = g_signal_connect(sm_adapter, "handle-async-abort", G_CALLBACK(_on_async_abort),NULL);
    sigret     = g_signal_connect(sm_adapter, "handle-async-connect", G_CALLBACK(_on_async_connect),NULL);
    sigret     = g_signal_connect(sm_adapter, "handle-async-disconnect", G_CALLBACK(_on_async_disconnect),NULL);
    sigret     = g_signal_connect(sm_adapter, "handle-async-set-sink-volume", G_CALLBACK(_on_async_set_sink_volume),NULL);
    sigret     = g_signal_connect(sm_adapter, "handle-async-set-source-state", G_CALLBACK(_on_async_set_source_state),NULL);

    AFB_DEBUG("register %s", AUDIOMANAGER_ROUTING_SOUNDMANAGER_SKELETON(sm_adapter));

    gboolean rc = g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(sm_adapter), connection, SOUND_MANAGER_PATH, &error);
    AFB_DEBUG("export soundmanager path result: %d", rc);
    if (FALSE == rc)
    {
        AFB_ERROR( "failed to export");
        g_error_free(error);
        g_object_unref(connection);
    }
}

static void on_name_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data){
    AFB_DEBUG("%s is called", __FUNCTION__);
}

static void on_name_lost(GDBusConnection *connection, const gchar *name, gpointer user_data){
    AFB_DEBUG("bus name is lost");
    // TODO: soundmanager should register current status?
}

ErrorCode open_soundmanager_interface(const am_instruction *callback){
    _am_instruction.on_async_abort            = callback->on_async_abort;
    _am_instruction.on_async_connect          = callback->on_async_connect;
    _am_instruction.on_async_disconnect       = callback->on_async_disconnect;
    _am_instruction.on_async_set_sink_volume  = callback->on_async_set_sink_volume;
    _am_instruction.on_async_set_source_state = callback->on_async_set_source_state;

    guint owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, SOUND_MANAGER_BUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE,
        on_bus_acquired, on_name_acquired, on_name_lost, NULL/* (gpointer)callback */, NULL);
    // see https://developer.gnome.org/gio/stable/gio-Owning-Bus-Names.html#g-bus-own-name

    pthread_t thread_id;
    int ret = pthread_create(&thread_id, NULL, dbus_event_loop_run, NULL);
    if(ret != 0)
    {
        AFB_ERROR("Failed to create thread");
        g_bus_unown_name(owner_id);
        close_soundmanager_inerface();
        close_proxy();
        return NOT_INITIALIZED;
    }
    return OK;
}

void close_soundmanager_inerface(){
    g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(sm_adapter));
}