// Copyright 2018 The NATS Authors
// 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 "../examples.h"

static const char *usage = ""\
"-txt           text to send (default is 'hello')\n";

typedef struct __myPubMsgInfo
{
    const char  *payload;
    int         size;
    char        ID[30];

} myPubMsgInfo;

static volatile bool done = false;

static void
_pubAckHandler(const char *guid, const char *error, void *closure)
{
    myPubMsgInfo *pubMsg = (myPubMsgInfo*) closure;

    printf("Ack handler for message ID=%s Data=%.*s GUID=%s - ",
           pubMsg->ID, pubMsg->size, pubMsg->payload, guid);

    if (error != NULL)
        printf("Error= %s\n", error);
    else
        printf("Success!\n");

    // This is a good place to free the pubMsg info since
    // we no longer need it.
    free(pubMsg);

    // Notify the main thread that we are done. This is
    // not the proper way and you should use some locking.
    done = true;
}

int main(int argc, char **argv)
{
    natsStatus      s;
    stanConnOptions *connOpts = NULL;
    stanConnection  *sc       = NULL;
    myPubMsgInfo    *pubMsg   = NULL;

    // This example demonstrates the use of the pubAckHandler closure
    // to correlate published messages and their acks.

    opts = parseArgs(argc, argv, usage);

    printf("Sending 1 message to channel '%s'\n", subj);

    // Now create STAN Connection Options and set the NATS Options.
    s = stanConnOptions_Create(&connOpts);
    if (s == NATS_OK)
        s = stanConnOptions_SetNATSOptions(connOpts, opts);

    // Create the Connection using the STAN Connection Options
    if (s == NATS_OK)
        s = stanConnection_Connect(&sc, cluster, clientID, connOpts);

    // Once the connection is created, we can destroy the options
    natsOptions_Destroy(opts);
    stanConnOptions_Destroy(connOpts);

    // Create an object that represents our message
    if (s == NATS_OK)
    {
        pubMsg = (myPubMsgInfo*) calloc(1, sizeof(myPubMsgInfo));
        if (pubMsg == NULL)
            s = NATS_NO_MEMORY;

        if (s == NATS_OK)
        {
            // Say we want to bind the data that we are going to send
            // to some unique ID that we know about this message.
            pubMsg->payload = payload;
            pubMsg->size    = (int)strlen(payload);
            snprintf(pubMsg->ID, sizeof(pubMsg->ID), "%s:%d", "xyz", 234);
        }
    }
    // We send the message and pass our message info as the closure
    // for the pubAckHandler.
    if (s == NATS_OK)
    {
        s = stanConnection_PublishAsync(sc, subj, pubMsg->payload, pubMsg->size,
                                        _pubAckHandler, (void*) pubMsg);

        // Note that if this call fails, then we need to free the pubMsg
        // object here since it won't be passed to the ack handler.
        if (s != NATS_OK)
            free(pubMsg);
    }

    if (s == NATS_OK)
    {
        while (!done)
            nats_Sleep(15);
    }

    if (s != NATS_OK)
    {
        printf("Error: %d - %s\n", s, natsStatus_GetText(s));
        nats_PrintLastErrorStack(stderr);
    }

    // Destroy the connection
    stanConnection_Destroy(sc);

    // To silence reports of memory still in-use with valgrind.
    nats_Sleep(50);
    nats_Close();

    return 0;
}
