/* FoundAdvertisedName callback */
void AJ_CALL found_advertised_name(const void* context, const char* name, alljoyn_transportmask transport, const char* namePrefix)
{
    QCC_UNUSED(context);
    printf("found_advertised_name(name=%s, prefix=%s, transport=0x%x)\n", name, namePrefix, (unsigned int)transport);

    /*
     * The access to global variable s_joinInitiated is serialized across multiple found_advertised_name callbacks
     * by accessing it only before calling alljoyn_busattachment_enableconcurrentcallbacks.
     */
    if ((QCC_FALSE == s_joinInitiated) && (0 == strcmp(name, OBJECT_NAME))) {
        /* We found a remote bus that is advertising basic service's well-known name, so connect to it */
        alljoyn_sessionopts opts = alljoyn_sessionopts_create(ALLJOYN_TRAFFIC_TYPE_MESSAGES, QCC_FALSE, ALLJOYN_PROXIMITY_ANY, ALLJOYN_TRANSPORT_ANY);

        if (NULL != opts) {
            QStatus status;
            s_joinInitiated = QCC_TRUE;

            /* alljoyn_busattachment_joinsession might block for a while, so allow other callbacks to run in parallel with it */
            alljoyn_busattachment_enableconcurrentcallbacks(s_msgBus);
            status = alljoyn_busattachment_joinsession(s_msgBus, name, SERVICE_PORT, NULL, &s_sessionId, opts);

            if (ER_OK != status) {
                printf("alljoyn_busattachment_joinsession failed (status=%s)\n", QCC_StatusText(status));
            } else {
                printf("alljoyn_busattachment_joinsession SUCCESS (Session id=%u)\n", (unsigned int)s_sessionId);
            }

            alljoyn_sessionopts_destroy(opts);
            s_joinComplete = QCC_TRUE;
        }
    }
}
/* Exposed concatinate method */
void cat_method(alljoyn_busobject bus, const alljoyn_interfacedescription_member* member, alljoyn_message msg)
{
    QStatus status;
    alljoyn_msgarg outArg;
    char* str1;
    char* str2;
    /* Concatenate the two input strings and reply with the result. */
    char result[256] = { 0 };
    QCC_UNUSED(member);
    status = alljoyn_msgarg_get(alljoyn_message_getarg(msg, 0), "s", &str1);
    if (ER_OK != status) {
        printf("Ping: Error reading alljoyn_message\n");
    }
    status = alljoyn_msgarg_get(alljoyn_message_getarg(msg, 1), "s", &str2);
    if (ER_OK != status) {
        printf("Ping: Error reading alljoyn_message\n");
    }
    snprintf(result, sizeof(result), "%s%s", str1, str2);
    outArg = alljoyn_msgarg_create_and_set("s", result);
    status = alljoyn_busobject_methodreply_args(bus, msg, outArg, 1);
    if (ER_OK != status) {
        printf("Ping: Error sending reply\n");
    }
    alljoyn_msgarg_destroy(outArg);
}
/** the PersonPassedThrough signal handler */
static void AJ_CALL person_passed_through(const alljoyn_interfacedescription_member* member, const char* path, alljoyn_message message)
{
    struct _listener_ctx* ctx = &listener_ctx; //alljoyn_c caveat: no way to pass a cookie into signal handlers.
    alljoyn_proxybusobject_ref proxyref = alljoyn_observer_get(ctx->observer, alljoyn_message_getsender(message), path);
    QCC_UNUSED(member);
    if (proxyref) {
        QStatus status;
        char* location = NULL;
        const char* who = NULL;
        alljoyn_proxybusobject proxy = alljoyn_proxybusobject_ref_get(proxyref);

        alljoyn_busattachment_enableconcurrentcallbacks(ctx->bus);

        status = proxy_get_location(proxy, &location);
        if (status == ER_OK) {
            status = alljoyn_message_parseargs(message, "s", &who);
        }
        if (status == ER_OK) {
            printf("[listener] %s passed through the door at location %s\n", who, (location != NULL) ? location : "<unknown>");
        } else {
            fprintf(stderr, "Something went wrong while parsing the received signal: %s\n", QCC_StatusText(status));
        }
        if (location) {
            free(location);
        }

        alljoyn_proxybusobject_ref_decref(proxyref);
    } else {
        fprintf(stderr, "Got PersonPassedThrough signal from an unknown door: %s:%s\n", alljoyn_message_getsender(message), path);
    }

    printf("> "); fflush(stdout);
}
static void AJ_CALL object_lost(const void* context, alljoyn_proxybusobject_ref proxyref)
{
    alljoyn_proxybusobject proxy = alljoyn_proxybusobject_ref_get(proxyref);
    QCC_UNUSED(context);
    printf("[listener] Door %s:%s no longer exists.\n\tLast known state for lost object:\n",
           alljoyn_proxybusobject_getuniquename(proxy), alljoyn_proxybusobject_getpath(proxy));
    print_door_state(proxy);
}
/* NameOwnerChanged callback */
void name_owner_changed(const void* context, const char* busName, const char* previousOwner, const char* newOwner)
{
    QCC_UNUSED(context);
    if (newOwner && (0 == strcmp(busName, OBJECT_NAME))) {
        printf("name_owner_changed: name=%s, oldOwner=%s, newOwner=%s\n",
               busName,
               previousOwner ? previousOwner : "<none>",
               newOwner ? newOwner : "<none>");
    }
}
/* FoundAdvertisedName callback */
void AJ_CALL found_advertised_name(const void* context, const char* name, alljoyn_transportmask transport, const char* namePrefix)
{
    QCC_UNUSED(context);
    QCC_UNUSED(transport);
    printf("found_advertised_name(name=%s, prefix=%s)\n", name, namePrefix);
    if (0 == strcmp(name, OBJECT_NAME)) {
        QStatus status;
        /* We found a remote bus that is advertising basic service's  well-known name so connect to it */
        alljoyn_sessionopts opts = alljoyn_sessionopts_create(ALLJOYN_TRAFFIC_TYPE_MESSAGES, QCC_FALSE, ALLJOYN_PROXIMITY_ANY, ALLJOYN_TRANSPORT_ANY);
        alljoyn_busattachment_enableconcurrentcallbacks(g_msgBus);
        status = alljoyn_busattachment_joinsession(g_msgBus, name, SERVICE_PORT, NULL, &s_sessionId, opts);

        if (ER_OK != status) {
            printf("alljoyn_busattachment_joinsession failed (status=%s)\n", QCC_StatusText(status));
        } else {
            printf("alljoyn_busattachment_joinsession SUCCESS (Session id=%d)\n", s_sessionId);
        }
        alljoyn_sessionopts_destroy(opts);
    }
    s_joinComplete = QCC_TRUE;
}
/*
 * This is the local implementation of the an AuthListener.  SrpKeyXListener is
 * designed to only handle SRP Key Exchange Authentication requests.
 *
 * When a Password request (CRED_PASSWORD) comes in using ALLJOYN_SRP_KEYX the
 * code will ask the user to enter the pin code that was generated by the
 * service. The pin code must match the service's pin code for Authentication to
 * be successful.
 *
 * If any other authMechanism is used other than SRP Key Exchange authentication
 * will fail.
 */
QCC_BOOL AJ_CALL request_credentials(const void* context, const char* authMechanism, const char* authPeer, uint16_t authCount,
                                     const char* userName, uint16_t credMask, alljoyn_credentials credentials)
{
    QCC_UNUSED(context);
    QCC_UNUSED(userName);
    printf("request_credentials for authenticating %s using mechanism %s\n", authPeer, authMechanism);
    if (strcmp(authMechanism, "ALLJOYN_SRP_KEYX") == 0) {
        if (credMask & ALLJOYN_CRED_PASSWORD) {
            if (authCount <= 3) {
                const int bufSize = 7;
                char buf[7];
                /* Take input from stdin and send it as a chat messages */
                printf("Please enter one time password : ");
                get_line(buf, bufSize, stdin);
                alljoyn_credentials_setpassword(credentials, buf);
                return QCC_TRUE;
            } else {
                return QCC_FALSE;
            }
        }
    }
    return QCC_FALSE;
}
/* AcceptSessionJoiner callback */
QCC_BOOL accept_session_joiner(const void* context, alljoyn_sessionport sessionPort,
                               const char* joiner,  const alljoyn_sessionopts opts)
{
    QCC_BOOL ret = QCC_FALSE;
    QCC_UNUSED(context);
    if (sessionPort != SERVICE_PORT) {
        printf("Rejecting join attempt on unexpected session port %d\n", sessionPort);
    } else {
        printf("Accepting join session request from %s (opts.proximity=%x, opts.traffic=%x, opts.transports=%x)\n",
               joiner, alljoyn_sessionopts_get_proximity(opts), alljoyn_sessionopts_get_traffic(opts), alljoyn_sessionopts_get_transports(opts));
        ret = QCC_TRUE;
    }
    return ret;
}
void ConnectionListener::SessionJoined(SessionPort sessionPort, SessionId id, const char* joiner)
{
	QCC_UNUSED(sessionPort);
	mSessionUser->setSessionId(id);
	printf("SessionJoined with %s (id=%d)\n", joiner, id);
	mBus->EnableConcurrentCallbacks();
	uint32_t timeout = 20;
	QStatus status = mBus->SetLinkTimeout(*(mSessionUser->getSessionId()), timeout);
	if (ER_OK == status) {
		printf("Set link timeout to %d\n", timeout);
	}
	else {
		printf("Set link timeout failed\n");
	}
}
/** returns a reference that must be decref'ed by the application */
static alljoyn_proxybusobject_ref get_door_at_location(alljoyn_busattachment bus, alljoyn_observer observer, const char* find_location)
{
    alljoyn_proxybusobject_ref proxyref;
    QCC_UNUSED(bus);
    for (proxyref = alljoyn_observer_getfirst(observer); proxyref; proxyref = alljoyn_observer_getnext(observer, proxyref)) {
        char* location;
        alljoyn_proxybusobject proxy = alljoyn_proxybusobject_ref_get(proxyref);
        QStatus status = proxy_get_location(proxy, &location);
        if (ER_OK != status) {
            fprintf(stderr, "Could not get Location property for object %s:%s.\n", alljoyn_proxybusobject_getuniquename(proxy), alljoyn_proxybusobject_getpath(proxy));
            continue;
        }
        if (location != NULL && !strcmp(find_location, location)) {
            free(location);
            return proxyref;
        }
        free(location);
    }
    return NULL;
}
static void list_doors(alljoyn_busattachment bus, alljoyn_observer observer)
{
    alljoyn_proxybusobject_ref proxyref;
    QCC_UNUSED(bus);
    for (proxyref = alljoyn_observer_getfirst(observer); proxyref; proxyref = alljoyn_observer_getnext(observer, proxyref)) {
        char* location;
        QCC_BOOL isOpen;
        alljoyn_proxybusobject proxy = alljoyn_proxybusobject_ref_get(proxyref);

        QStatus status = proxy_get_isopen(proxy, &isOpen);
        if (ER_OK != status) {
            fprintf(stderr, "Could not get IsOpen property for object %s:%s.\n", alljoyn_proxybusobject_getuniquename(proxy), alljoyn_proxybusobject_getpath(proxy));
            continue;
        }
        status = proxy_get_location(proxy, &location);
        if (ER_OK != status) {
            fprintf(stderr, "Could not get Location property for object %s:%s.\n", alljoyn_proxybusobject_getuniquename(proxy), alljoyn_proxybusobject_getpath(proxy));
            continue;
        }
        printf("Door location: %s open: %s\n", (location != NULL) ? location : "<unknown>", isOpen ? "yes" : "no");
        free(location);
    }
}
static void CDECL_CALL SigIntHandler(int sig)
{
    QCC_UNUSED(sig);
    s_interrupt = QCC_TRUE;
}
void ConnectionListener::LostAdvertisedName(const char* name, TransportMask transport, const char* namePrefix)
{
	QCC_UNUSED(namePrefix);
	printf("Got LostAdvertisedName for %s from transport 0x%x\n", name, transport);
}
static void AJ_CALL properties_changed(alljoyn_proxybusobject proxy, const char* intf, const alljoyn_msgarg changed, const alljoyn_msgarg invalidated, void* context)
{
    QStatus status;
    size_t nelem;
    alljoyn_msgarg elems;
    char* location = NULL;
    struct _listener_ctx* ctx = (struct _listener_ctx*) context;
    QCC_UNUSED(intf);
    printf("[listener] Door %s:%s has changed some properties.\n",
           alljoyn_proxybusobject_getuniquename(proxy), alljoyn_proxybusobject_getpath(proxy));

    alljoyn_busattachment_enableconcurrentcallbacks(ctx->bus);
    status = proxy_get_location(proxy, &location);
    if (status == ER_OK) {
        printf("\tThat's actually the door at location %s.\n", (location != NULL) ? location : "<unknown>");
        free(location);
    }

    if (ER_OK == status) {
        status = alljoyn_msgarg_get(changed, "a{sv}", &nelem, &elems);
    }
    if (ER_OK == status) {
        size_t i;
        for (i = 0; i < nelem; ++i) {
            const char* prop;
            alljoyn_msgarg val;
            status = alljoyn_msgarg_get(alljoyn_msgarg_array_element(elems, i), "{sv}", &prop, &val);
            if (ER_OK == status) {
                if (!strcmp(prop, "Location")) {
                    char* newloc;
                    status = alljoyn_msgarg_get_string(val, &newloc);
                    if (ER_OK == status) {
                        printf("->  location: %s\n", newloc);
                    }
                } else if (!strcmp(prop, "IsOpen")) {
                    QCC_BOOL isopen = QCC_FALSE;
                    status = alljoyn_msgarg_get_bool(val, &isopen);
                    if (ER_OK == status) {
                        printf("->   is open: %s\n", isopen ? "yes" : "no");
                    }
                }
            } else {
                break;
            }
        }
    }

    if (ER_OK == status) {
        status = alljoyn_msgarg_get(invalidated, "as", &nelem, &elems);
    }
    if (ER_OK == status) {
        size_t i;
        for (i = 0; i < nelem; ++i) {
            char* prop;
            status = alljoyn_msgarg_get(alljoyn_msgarg_array_element(elems, i), "s", &prop);
            if (status == ER_OK) {
                printf("  invalidated %s\n", prop);
            }
        }
    }

    printf("> ");
    fflush(stdout);
}
/* ObjectRegistered callback */
void busobject_object_registered(const void* context)
{
    QCC_UNUSED(context);
    printf("ObjectRegistered has been called\n");
}
static void SigIntHandler(int sig)
{
    QCC_UNUSED(sig);
    g_interrupt = QCC_TRUE;
}
void AJ_CALL authentication_complete(const void* context, const char* authMechanism, const char* peerName, QCC_BOOL success)
{
    QCC_UNUSED(context);
    QCC_UNUSED(peerName);
    printf("authentication_complete %s %s\n", authMechanism, success == QCC_TRUE ? "successful" : "failed");
}