/* =========================================================================

DESCRIPTION
   Netease dbus program

Copyright (c) 2017 by Netease, Co,LTD.  All Rights Reserved.
============================================================================ */

/* =========================================================================

                             REVISION

when                       who why
--------                    ---------
-------------------------------------------
2017/06/28          wangzijiao                                  Created.
============================================================================ */

/* ------------------------------------------------------------------------
** Includes
** ------------------------------------------------------------------------ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <uvdbus/libuv_dbus.h>
#include <uvdbus/log.h>

#include "dbus.h"
#include "error.h"

/* ------------------------------------------------------------------------
** Macros
** ------------------------------------------------------------------------ */

/* ------------------------------------------------------------------------
** Defines
** ------------------------------------------------------------------------ */

/* ------------------------------------------------------------------------
** Types
** ------------------------------------------------------------------------ */

/* ------------------------------------------------------------------------
** Global Variable Definitions
** ------------------------------------------------------------------------ */
uv_loop_t *pLoop;
DBusConnection *pBus = NULL;
OnDBusMessage OemMsgCb = NULL;

/* ------------------------------------------------------------------------
** Function Definitions
** ------------------------------------------------------------------------ */
void Netease_dbus_get_playerstatus() {
    Netease_Dbus_Send_CMD(MODULE_VOICEENGINE, MODULE_CONTROLLER,
                          CMD_CONTROLLER_REQMSG_PLAYERSTATUS, "", 0);
}

void Netease_dbus_initargs() {
    Netease_Dbus_Send_CMD(MODULE_VOICEENGINE, MODULE_CONTROLLER,
                          CMD_CONTROLLER_REQMSG_INITARGS, "", 0);
}

int Netease_dbus_oem_init(OnDBusMessage msgcb) {
    DBusError err;
    DBusConnection *conn;
    int ret = NETEASE_SUCCESS;
    int funcret;
    char MatchRule[DBUS_MAXIMUM_MATCH_RULE_LENGTH - 1];

    dbus_error_init(&err);
    OemMsgCb = msgcb;
    pBus = dbus_bus_get(DBUS_BUS_SESSION, &err);
    if (dbus_error_is_set(&err)) {
        n_error("Dbus connect error (%s)\n", err.message);
        dbus_error_free(&err);
    }

    if (pBus == NULL) {
        ret = NETEASE_ERROR_DBUS_CONNECT;
        goto _exit;
    }

    funcret = dbus_bus_request_name(
        pBus, g_pModInfoTable[MODULE_VOICEENGINE].modAliase,
        DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
    if (dbus_error_is_set(&err)) {
        n_error("Name error (%s) \n", err.message);
        // dbus_error_free(&err);
        ret = NETEASE_ERROR_DBUS_REQNAME;
        goto _exit;
    }

    if (funcret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
        n_error("dbus: name is not the primary one! ret=%d\n", funcret);
        // dbus_error_free(&err);
        ret = NETEASE_ERROR_DBUS_REQNAME;
        goto _exit;
    }

    sprintf(MatchRule, "type='signal', interface='%s'",
            DBUS_MESSAGE_INTERFACE_NAME);
    dbus_bus_add_match(pBus, MatchRule, &err);
    dbus_connection_flush(pBus);

    if (dbus_error_is_set(&err)) {
        n_error("dbus: add match signal error (%s)\n", err.message);
        ret = NETEASE_ERROR_DBUS_MATCHSIGNAL;
        goto _exit;
    }

_exit:
    dbus_error_free(&err);
    return ret;
}

void Netease_dbus_oem_start() {
    while (1) {
        DBusError error;
        int ret;
        ret = dbus_connection_read_write(pBus, 30);
        // n_debug("dbus wait ret:%d\n", ret);
        if (!ret) {
            // n_debug("Will continue %d\n", ret);
            continue;
        }

        while (1) {
            DBusMessage *msg = dbus_connection_pop_message(pBus);

            if (msg == NULL) {
                break;
            }

            dbus_error_init(&error);

            if (dbus_message_is_signal(msg, DBUS_MESSAGE_INTERFACE_NAME,
                                       "Notify")) {
                n_debug("In signal\n");
                PDBUS_MSG_PACK msgPack =
                    (PDBUS_MSG_PACK)malloc(sizeof(DBUS_MSG_PACK));
                memset(msgPack, 0, sizeof(DBUS_MSG_PACK));

                if (dbus_message_get_args(
                        msg, &error, DBUS_TYPE_UINT32, &msgPack->msgSrc, // from
                        DBUS_TYPE_UINT32,
                        &msgPack->msgDests, // to  -1 means all except it's self
                        DBUS_TYPE_UINT32, &msgPack->busCmd, // command type
                        DBUS_TYPE_STRING,
                        &msgPack->pMsg, // message context if had
                        DBUS_TYPE_INVALID)) {
                    msgPack->msgSize = strlen((char *)msgPack->pMsg);
                    if (!!OemMsgCb) {
                        OemMsgCb(NULL, pBus, msgPack);
                    }
                } else {
                    n_error("Receive Notify Message Error: %s\n",
                            error.message);
                }
                free(msgPack);
            } else if (dbus_message_is_method_call(
                           msg, DBUS_MESSAGE_INTERFACE_NAME, "API")) {
                n_debug("In method call\n");
                PDBUS_MSG_PACK pMsgPack =
                    (PDBUS_MSG_PACK)malloc(sizeof(DBUS_MSG_PACK));

                memset(pMsgPack, 0, sizeof(DBUS_MSG_PACK));

                if (dbus_message_get_args(
                        msg, &error, DBUS_TYPE_UINT32,
                        &pMsgPack->msgSrc, // from
                        DBUS_TYPE_UINT32,
                        &pMsgPack
                             ->msgDests, // to  -1 means all except it's self
                        DBUS_TYPE_UINT32, &pMsgPack->busCmd, // command type
                        DBUS_TYPE_UINT32,
                        &pMsgPack->msgSize, // message size(in bytes)
                        DBUS_TYPE_STRING,
                        &pMsgPack->pMsg, // message context if had
                        DBUS_TYPE_INVALID)) {
                    if (!!OemMsgCb) {
                        n_debug("Will cb msgcb, get str:%d\n", pMsgPack->pMsg);
                        OemMsgCb(NULL, pBus, pMsgPack);
                    } else {
                        n_debug("msgcb is null\n");
                    }
                } else {
                    n_error("Receive API Message Error: %s\n", error.message);
                }

                free(pMsgPack);
            }

            dbus_error_free(&error);
            dbus_message_unref(msg);
        }
    }
}

int Netease_Dbus_Init(OnDBusMessage msgcb) {
    pLoop = uv_default_loop();
    int ret = NETEASE_SUCCESS;

    pBus =
        DBusWithLibuvInit(pLoop, g_pModInfoTable[MODULE_VOICEENGINE].modAliase,
                          msgcb, NULL, NULL, &ret);
    if (!pBus || ret != 0) {
        n_error("Uv dbus init error! RET:%d\n", ret);
        ret = NETEASE_ERROR_FAIL;
    }

    return ret;
}

void Netease_Dbus_Start_Sync() {
    if (!!pBus && !!pLoop) {
        // uv_run(pLoop, UV_RUN_DEFAULT);
        RunUVLoop(pLoop);

        while (TRUE) {
            usleep(1000);
        }
    } else {
        n_error("Run uvdbus error!\n");
    }
}

int Netease_Dbus_Send_CMD_ERROR(unsigned int from, unsigned int dest,
                                unsigned int buscmd, int error) {
    char tmp[30];
    sprintf(tmp, "{\"errorcode\":%d}", error);
    return Netease_Dbus_Send_CMD(from, dest, buscmd, tmp, strlen(tmp));
}

static unsigned long long justnow;
int Netease_Dbus_Send_CMD(unsigned int from, unsigned int dest,
                          unsigned int buscmd, void *data, size_t datalen) {
    if (!pBus)
        return NETEASE_ERROR_NULL_POINTER;

    if (dest >= MODULE_MAX)
        return NETEASE_ERROR_EXCEPTION;
    Netease_caltime(&justnow, 1);
    struct timeval tv;
    gettimeofday(&tv, NULL);
    unsigned int msgTomask = 1 << dest;

    // n_debug("Now %llu\n", Netease_caltime(&justnow, 1));
    // n_debug("%d-%d\n", tv.tv_sec, tv.tv_usec);
    DBusMessage *pMsg = dbus_message_new_method_call(
        g_pModInfoTable[dest].modAliase, g_pModInfoTable[dest].modPath,
        DBUS_MESSAGE_INTERFACE_NAME, "GOAPI");
    // n_debug("Now %llu\n", Netease_caltime(&justnow, 1));
    if (!pMsg) {
        return NETEASE_ERROR_NULL_POINTER;
    }

    dbus_message_set_no_reply(pMsg, TRUE);

    dbus_message_append_args(
        pMsg, DBUS_TYPE_UINT32, &from, // from
        DBUS_TYPE_UINT32, &msgTomask,  // to  -1 means all except it's self

        DBUS_TYPE_UINT32, &tv.tv_sec,  // timestamp for second
        DBUS_TYPE_UINT32, &tv.tv_usec, // timestamp for usecond

        DBUS_TYPE_UINT32, &buscmd,  // command type
        DBUS_TYPE_UINT32, &datalen, // message size(in bytes)
        // msgLen[0, 512): pad to message; msgLen[512, ~): memory map key,
        DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, datalen, DBUS_TYPE_INVALID);
    // n_debug("Now %llu\n", Netease_caltime(&justnow, 1));
    if (!dbus_connection_send(pBus, pMsg, NULL)) {
        return NETEASE_ERROR_FAIL;
    }
    // n_debug("Now %llu\n", Netease_caltime(&justnow, 1));
    // dbus_connection_flush(pBus);
    dbus_message_unref(pMsg);
    // n_debug("Now %llu\n", Netease_caltime(&justnow, 1));
    return NETEASE_SUCCESS;
}
