/* Copyright 2012. Bloomberg Finance L.P.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:  The above
 * copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */
// blpapi_subscriptionlist.h                                          -*-C++-*-
#ifndef INCLUDED_BLPAPI_SUBSCRIPTIONLIST
#define INCLUDED_BLPAPI_SUBSCRIPTIONLIST

//@PURPOSE: Provide a list of subscriptions.
//
//@CLASSES:
// blpapi::SubscriptionList: Represents a list of subscriptions.
//
//@DESCRIPTION: This component provides a structure to hold the data used (and
// returned) by the 'Session::subscribe', 'Session::resubscribe', and
// 'Session::unsubscribe' methods.  This structure comprises a list in which
// each list entry contains two primary fields: a 'CorrelationId' associated
// with the subscription, and a string, called a *subscription* *string*,
// describing the data to be delivered as a part of the subscription.
//
// STRUCTURE OF SUBSCRIPTION STRING
// ---------------------------------
// The simplest form of a subscription string is a *fully* *qualified*
// subscription string, which has the following structure:
//
// "//blp/mktdata/ticker/IBM US Equity?fields=BID,ASK&interval=2"
//  \-----------/\------/\-----------/\------------------------/
//        |          |         |                  |
//     Service    Prefix   Instrument           Suffix
//
// Such a fully-qualified string is composed of:
//: Service Identifier: a string matching the expression
//:   '^//[-_.a-zA-Z0-9]+/[-_.a-zA-Z0-9]+$', e.g. //blp/mktdata.  See
//:   'blpapi_abstractsession' for further details.
//:
//: Prefix: a string matching the expression '/([-_.a-zA-Z0-9]+/)?', often used
//:   as a symbology identifier.  Common examples include '/ticker/' and
//:   '/cusip/'.  Not all services make use of prefices.  Note than an "empty"
//:   topic prefix consists of the string "/", so the topic prefix always
//:   separates the service string from the instrument string.
//:
//: Instrument: a non-empty string that does not contain the character '?'
//:   (i.e. a string matching '[^?]+') e.g. "IBM US Equity", or "SPX Index".
//:   The service, prefix, and instrument together uniquely identify a source
//:   for subscription data.
//:
//: Suffix: a suffix contains a question mark followed by a list of options
//:   which can affect the content delivery.  The format of these options is
//:   service specific, but they generally follow URI query string conventions:
//:   a sequence of "key=value" pairs separated by "&" characters.  Further,
//:   many common services follow the convention that the value given for the
//:   'fields' key is formatted as a comma-separated list of field names.
//:   BLPAPI provides several convenience functions to assist in formatting
//:   subscription strings for services that make use of these conventions;
//:   see the 'SubscriptionList::add' methods for details.
//
// Subscription strings need not be fully qualified: BLPAPI allows the service
// and prefix to be omitted from subscription strings, and automatically
// qualifies these strings using information stored in a 'Session' object.
//
// QUALIFYING SUBSCRIPTION STRINGS
// -------------------------------
// The subscription strings passed to 'Session::subscribe' and
// 'Session::resubscribe' are automatically qualified if the service identifier
// is missing (i.e. if the subscription string does not start with "//"). The
// subscription parameters (i.e. the optional part after instrument identifier)
// are never modified.
// The rules for qualifying the subscription string are:
//
//: o If the subscription string begins with "//" then it is assumed to be a
//:   a fully qualified subscription string including service identifier,
//:   prefix, and instrument.  In this case the string will not be modified and
//:   session options defaults have no affect on the subscription.
//:
//: o If the subscription string begins with a '/' and the second character is
//:   not '/', then the string is assumed to begin with the topic prefix, but
//:   no service identifier. In this case the string is qualified by prepending
//:   the 'SessionOptions::defaultSubscriptionService()' to the specified
//:   string.
//:
//: o If the subscription string does not begin with a '/' it is assumed to
//:   begin with an instrument identifier.  In this case the string is
//:   qualified by prepending the
//:   'SessionOptions::defaultSubscriptionService()' followed by
//:   'SessionOptions::defaultTopicPrefix()' to the specified string.
//:   If the 'defaultTopicPrefix' is empty or null, then the prefix used is
//:   '/'.  Otherwise (in the case of a nontrivial prefix) if the separator '/'
//:   is not specified at the beginning or the end of the 'defaultTopicPrefix',
//:   then it will be added.

#ifndef INCLUDED_BLPAPI_CALL
#include <blpapi_call.h>
#endif

#ifndef INCLUDED_BLPAPI_CORRELATIONID
#include <blpapi_correlationid.h>
#endif

#ifndef INCLUDED_BLPAPI_DEFS
#include <blpapi_defs.h>
#endif

#ifndef INCLUDED_BLPAPI_EXCEPTION
#include <blpapi_exception.h>
#endif

#ifndef INCLUDED_BLPAPI_TYPES
#include <blpapi_types.h>
#endif

#include <stddef.h>

struct blpapi_SubscriptionList;
typedef struct blpapi_SubscriptionList blpapi_SubscriptionList_t;

#ifdef __cplusplus
extern "C" {
#endif

BLPAPI_EXPORT
blpapi_SubscriptionList_t *blpapi_SubscriptionList_create(void);

BLPAPI_EXPORT
void blpapi_SubscriptionList_destroy(blpapi_SubscriptionList_t *list);

BLPAPI_EXPORT
int blpapi_SubscriptionList_add(
                             blpapi_SubscriptionList_t     *list,
                             const char                    *subscriptionString,
                             const blpapi_CorrelationId_t  *correlationId,
                             const char                   **fields,
                             const char                   **options,
                             size_t                         numfields,
                             size_t                         numOptions);

BLPAPI_EXPORT
int blpapi_SubscriptionList_addResolved(
                              blpapi_SubscriptionList_t    *list,
                              const char                   *subscriptionString,
                              const blpapi_CorrelationId_t *correlationId);

BLPAPI_EXPORT
int blpapi_SubscriptionList_clear(blpapi_SubscriptionList_t *list);

BLPAPI_EXPORT
int blpapi_SubscriptionList_append(blpapi_SubscriptionList_t       *dest,
                                   const blpapi_SubscriptionList_t *src);

BLPAPI_EXPORT
int blpapi_SubscriptionList_size(const blpapi_SubscriptionList_t *list);

BLPAPI_EXPORT
int blpapi_SubscriptionList_correlationIdAt(
                                       const blpapi_SubscriptionList_t *list,
                                       blpapi_CorrelationId_t          *result,
                                       size_t                           index);

BLPAPI_EXPORT
int blpapi_SubscriptionList_topicStringAt(blpapi_SubscriptionList_t  *list,
                                          const char                **result,
                                          size_t                      index);

BLPAPI_EXPORT
int blpapi_SubscriptionList_isResolvedAt(blpapi_SubscriptionList_t *list,
                                         int                       *result,
                                         size_t                     index);


#ifdef __cplusplus
}

#ifndef INCLUDED_CSTRING
#include <cstring>
#define INCLUDED_CSTRING
#endif

#ifndef INCLUDED_STRING
#include <string>
#define INCLUDED_STRING
#endif

#ifndef INCLUDED_VECTOR
#include <vector>
#define INCLUDED_VECTOR
#endif

namespace BloombergLP {
namespace blpapi {
                         // ======================
                         // class SubscriptionList
                         // ======================

class SubscriptionList {
    // Contains a list of subscriptions used when subscribing and
    // unsubscribing.
    //
    // A 'SubscriptionList' is used when calling 'Session::subscribe()',
    // 'Session::resubscribe()' and 'Session::unsubscribe()'.  The entries can
    // be constructed in a variety of ways.
    // The two important elements when creating a subscription are
    //: Subscription String: A subscription string represents a topic whose
    //:  updates user is interested in.  A subscription string follows a
    //:  structure as specified below.
    //: CorrelationId: the unique identifier to tag all data associated with
    //:  this subscription.
    //
    // The following table describes how various operations use the above
    // elements:
    // --------------|--------------------------------------------------------|
    //  OPERATION    |  SUBSCRIPTION STRING  |       CORRELATION ID           |
    // --------------|-----------------------+--------------------------------|
    //  'subscribe'  |Used to specify the    |Identifier for the subscription.|
    //               |topic to subscribe to. |If uninitialized correlationid  |
    //               |                       |was specified an internally     |
    //               |                       |generated correlationId will be |
    //               |                       |set for the subscription.       |
    // --------------+-----------------------+--------------------------------|
    // 'resubscribe' |Used to specify the new|Identifier of the subscription  |
    //               |topic to which the     |which needs to be modified.     |
    //               |subscription should be |                                |
    //               |modified to.           |                                |
    // --------------+-----------------------+--------------------------------|
    // 'unsubscribe' |        NOT USED       |Identifier of the subscription  |
    //               |                       |which needs to be canceled.     |
    // -----------------------------------------------------------------------|

    blpapi_SubscriptionList_t *d_handle_p;

  public:
    SubscriptionList();
        // Create an empty 'SubscriptionList'.
    SubscriptionList(const SubscriptionList& original);
        // Create a 'SubscriptionList' object initialized to the list of
        // subscriptions from the specified 'original'.

    ~SubscriptionList();
        // Destroy this object.

    // MANIPULATORS

    int add(const char *subscriptionString);
        // Append the specified 'subscriptionString' to this 'SubscriptionList'
        // object, associating an internally generated 'CorrelationId' with it.
        // The subscription string may include options.  Note that the
        // subscription string provided to this function may be resolved by
        // functions operating on this 'SubscriptionList' object; use
        // 'SubscriptionList::addResolved', below, to request that such
        // functions bypass resolution.

    int add(const char           *subscriptionString,
            const CorrelationId&  correlationId);
        // Append the specified 'subscriptionString' to this 'SubscriptionList'
        // object, associating the specified 'correlationId' with it.  The
        // subscription string may include options.  Note that the subscription
        // string provided to this function  may be resolved by functions
        // operating on this 'SubscriptionList' object; use
        // 'SubscriptionList::addResolved', below, to request that such
        // functions bypass resolution.

    int add(const char                      *subscriptionWithoutOptions,
            const std::vector<std::string>&  fields,
            const std::vector<std::string>&  options,
            const CorrelationId&             correlationId);
        // Append the specified 'subscriptionString', with the specified
        // 'fields' and the specified 'options', to this 'SubscriptionList'
        // object, associating the specified 'correlationId' with it.

    int add(const char           *subscriptionWithoutOptions,
            const char           *fields,
            const char           *options,
            const CorrelationId&  correlationId);
        // Append the specified 'subscriptionString' with the specified
        // 'fields', which must be formatted as a comma-separated list, and the
        // specified 'options', which must be formatted as an
        // ampersand-separated list, to this 'SubscriptionList' object, and
        // associate the specified 'correlationId' with it.

    int add(const CorrelationId& correlationId);
        // Append the specified 'correlationId' to this 'SubscriptionList'
        // object.  A 'SubscriptionList' entry containing only a
        // 'CorrelationId' can be used only in a 'Session::unsubscribe' call.

    int addResolved(const char *subscriptionString);
        // Add the specified 'subscriptionString' to this 'SubscriptionList',
        // associating an internally generated 'CorrelationId' with it.  The
        // subscription string may include options.  The behavior of this
        // function, and of functions operating on this 'SubscriptionList'
        // object, is undefined unless 'subscriptionString' is a
        // fully-resolved subscription string; clients that cannot provide
        // fully-resolved subscription strings should use
        // 'SubscriptionList::add' instead.  Note that it is at the discretion
        // of each function operating on a 'SubscriptionList' whether to
        // perform resolution on this subscription.

    int addResolved(const char           *subscriptionString,
                    const CorrelationId&  correlationId);
        // Add the specified 'subscriptionString' to this 'SubscriptionList'
        // object, associating the specified 'correlationId' with it.  The
        // subscription string may include options.  The behavior of this
        // function, and of functions operating on this 'SubscriptionList'
        // object, is undefined unless 'subscriptionString' is a
        // fully-resolved subscription string; clients that cannot provide
        // fully-resolved subscription strings should use
        // 'SubscriptionList::add' instead.  Note that it is at the discretion
        // of each function operating on a 'SubscriptionList' whether to
        // perform resolution on this subscription.

    int append(const SubscriptionList& other);
        // Extend this object by appending a copy of each entry in the
        // specified 'other'.  Note that this function adds 'other.size()' new
        // entries to this object.  Note also that this function is alias-safe;
        // i.e. 'x.append(x)' has well-defined behavior.

    void clear();
        // Remove all entries from this object.

    SubscriptionList& operator=(const SubscriptionList& rhs);
        // Replace the contents of this 'SubscriptionList' object with the
        // contents of the specified 'rhs' and return a modifiable reference to
        // this 'SubscriptionList' object.

    // ACCESSORS

    size_t size() const;
        // Return the number of entries in this object.

    CorrelationId correlationIdAt(size_t index) const;
        // Return the 'CorrelationId' of the specified 'index'th entry in this
        // 'SubscriptionList' object.  An exception is thrown if
        // 'index >= size()'.

    const char *topicStringAt(size_t index) const;
        // Return a pointer to a null-terminated string which contains the full
        // topic string (including any field and option portions) of the
        // 'index'th entry in this 'SubscriptionList' object.  The pointer
        // remains valid until this 'SubscriptionList' object is destroyed or
        // 'clear()' is called.  An exception is thrown if 'index >= size()'.

    bool isResolvedTopicAt(size_t index) const;
        // Return 'true' if the 'index'th entry in this 'SubscriptionList'
        // object was created using 'SubscriptionList::addResolved' and 'false'
        // if it was created using 'SubscriptionList::add'.  An exception is
        // thrown if 'index >= size()'.

    const blpapi_SubscriptionList_t *impl() const;
};

// ============================================================================
//                      INLINE FUNCTION DEFINITIONS
// ============================================================================

                            // ----------------------
                            // class SubscriptionList
                            // ----------------------
inline
SubscriptionList::SubscriptionList()
: d_handle_p(blpapi_SubscriptionList_create())
{
}

inline
SubscriptionList::SubscriptionList(const SubscriptionList& original)
: d_handle_p(blpapi_SubscriptionList_create())
{
    blpapi_SubscriptionList_append(d_handle_p, original.d_handle_p);
}

inline
SubscriptionList::~SubscriptionList()
{
    blpapi_SubscriptionList_destroy(d_handle_p);
}

inline
int SubscriptionList::add(const char *subscriptionString)
{
    blpapi_CorrelationId_t correlationId;
    std::memset(&correlationId, 0, sizeof(correlationId));
    return blpapi_SubscriptionList_add(d_handle_p,
                                       subscriptionString,
                                       &correlationId,
                                       0,
                                       0,
                                       0,
                                       0);
}

inline
int SubscriptionList::add(const char           *subscriptionString,
                          const CorrelationId&  correlationId)
{
    return blpapi_SubscriptionList_add(d_handle_p,
                                       subscriptionString,
                                       &correlationId.impl(),
                                       0,
                                       0,
                                       0,
                                       0);
}

inline
int SubscriptionList::add(const char                      *subscriptionString,
                          const std::vector<std::string>&  fields,
                          const std::vector<std::string>&  options,
                          const CorrelationId&             correlationId)
{
    std::vector<const char *> tmpVector;
    const char *arena[256];
    const char **tmpArray = arena;
    size_t sizeNeeded = fields.size() + options.size();

    if (sizeNeeded > sizeof(arena)/sizeof(arena[0])) {
        tmpVector.resize(sizeNeeded);
        tmpArray = &tmpVector[0];
    }

    const char **p = tmpArray;
    for (std::vector<std::string>::const_iterator itr = fields.begin(),
            end = fields.end(); itr != end; ++itr, ++p) {
        *p = itr->c_str();
    }

    for (std::vector<std::string>::const_iterator itr = options.begin(),
            end = options.end(); itr != end; ++itr, ++p) {
        *p = itr->c_str();
    }

    return blpapi_SubscriptionList_add(d_handle_p,
                                       subscriptionString,
                                       &correlationId.impl(),
                                       tmpArray,
                                       tmpArray + fields.size(),
                                       fields.size(),
                                       options.size());
}

inline
int SubscriptionList::add(const char           *subscriptionString,
                          const char           *fields,
                          const char           *options,
                          const CorrelationId&  correlationId)
{
    return blpapi_SubscriptionList_add(d_handle_p,
                                       subscriptionString,
                                       &correlationId.impl(),
                                       &fields,
                                       &options,
                                       fields ? 1 : 0,
                                       options ? 1 : 0 );

}

inline
int SubscriptionList::add(const CorrelationId& correlationId)
{
    return blpapi_SubscriptionList_add(d_handle_p,
                                       "",
                                       &correlationId.impl(),
                                       0,
                                       0,
                                       0,
                                       0);
}

inline
int SubscriptionList::addResolved(const char *subscriptionString)
{
    blpapi_CorrelationId_t correlationId;
    std::memset(&correlationId, 0, sizeof(correlationId));
    return BLPAPI_CALL_SUBSCRIPTIONLIST_ADDRESOLVED(d_handle_p,
                                                    subscriptionString,
                                                    &correlationId);
}

inline
int SubscriptionList::addResolved(const char *subscriptionString,
                                  const CorrelationId& correlationId)
{
    return BLPAPI_CALL_SUBSCRIPTIONLIST_ADDRESOLVED(d_handle_p,
                                                    subscriptionString,
                                                    &correlationId.impl());
}

inline
int SubscriptionList::append(const SubscriptionList& other)
{
    return blpapi_SubscriptionList_append(d_handle_p, other.d_handle_p);
}

inline
void SubscriptionList::clear()
{
    blpapi_SubscriptionList_clear(d_handle_p);
}

inline
SubscriptionList& SubscriptionList::operator=(const SubscriptionList& rhs)
{
    blpapi_SubscriptionList_clear(d_handle_p);
    blpapi_SubscriptionList_append(d_handle_p, rhs.d_handle_p);
    return *this;
}

inline
size_t SubscriptionList::size() const
{
    return blpapi_SubscriptionList_size(d_handle_p);
}

inline
CorrelationId SubscriptionList::correlationIdAt(size_t index) const
{
    blpapi_CorrelationId_t correlationId;

    ExceptionUtil::throwOnError(
            blpapi_SubscriptionList_correlationIdAt(d_handle_p,
                                                    &correlationId,
                                                    index));

    return CorrelationId(correlationId);
}

inline
const char *SubscriptionList::topicStringAt(size_t index) const
{
    const char *result;

    ExceptionUtil::throwOnError(
            blpapi_SubscriptionList_topicStringAt(d_handle_p,
                                                  &result,
                                                  index));

    return result;
}

inline
bool SubscriptionList::isResolvedTopicAt(size_t index) const
{
    int result;

    ExceptionUtil::throwOnError(
            BLPAPI_CALL_SUBSCRIPTIONLIST_ISRESOLVEDAT(d_handle_p,
                                                      &result,
                                                      index));

    return result ? true : false;
}

inline
const blpapi_SubscriptionList_t *SubscriptionList::impl() const
{
    return d_handle_p;
}

}  // close package namespace
}  // close enterprise namespace

#endif // ifdef __cplusplus


#endif // #ifndef INCLUDED_BLPAPI_SUBSCRIPTIONLIST
