/* 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.
 */

/** \file blpapi_session.h */
/** \defgroup blpapi_session Component blpapi_session
\brief Provide consumer session to get Bloomberg Service.
\file blpapi_session.h
\brief Provide consumer session to get Bloomberg Service.
*/

#ifndef INCLUDED_BLPAPI_SESSION
#define INCLUDED_BLPAPI_SESSION

/** \addtogroup blpapi
 * @{
 */
/** \addtogroup blpapi_session
 * @{
 * <A NAME="purpose"></A>
 * <A NAME="1"> \par Purpose: </A>
 * Provide consumer session to get Bloomberg Service.
 * \par
 * \par
 * <A NAME="classes"></A>
 * <A NAME="2"> \par Classes: </A>
 * <table>
 * <tr>
 * <td>blpapi::Session</td>
 * <td>A consumer session for consuming service</td>
 * </tr>
 * </table>
 * \par
 * \par
 * <A NAME="description"></A>
 * <A NAME="3"> \par Description: </A>
 *  This component implements a consumer session for getting
 * services.
 */
/** @} */
/** @} */

#ifndef INCLUDED_BLPAPI_ABSTRACTSESSION
#include <blpapi_abstractsession.h>
#endif

#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_EVENT
#include <blpapi_event.h>
#endif

#ifndef INCLUDED_BLPAPI_EVENTDISPATCHER
#include <blpapi_eventdispatcher.h>
#endif

#ifndef INCLUDED_BLPAPI_IDENTITY
#include <blpapi_identity.h>
#endif

#ifndef INCLUDED_BLPAPI_REQUEST
#include <blpapi_request.h>
#endif

#ifndef INCLUDED_BLPAPI_REQUESTTEMPLATE
#include <blpapi_requesttemplate.h>
#endif

#ifndef INCLUDED_BLPAPI_SERVICE
#include <blpapi_service.h>
#endif

#ifndef INCLUDED_BLPAPI_SESSIONOPTIONS
#include <blpapi_sessionoptions.h>
#endif

#ifndef INCLUDED_BLPAPI_SUBSCRIPTIONLIST
#include <blpapi_subscriptionlist.h>
#endif

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

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef void (*blpapi_EventHandler_t)(
        blpapi_Event_t *event, blpapi_Session_t *session, void *userData);

typedef void (*blpapi_SubscriptionPreprocessErrorHandler_t)(
        const blpapi_CorrelationId_t *correlationId,
        const char *subscriptionString,
        int errorCode,
        const char *errorDescription,
        void *userData);

BLPAPI_EXPORT
blpapi_Session_t *blpapi_Session_create(blpapi_SessionOptions_t *parameters,
        blpapi_EventHandler_t handler,
        blpapi_EventDispatcher_t *dispatcher,
        void *userData);

BLPAPI_EXPORT
void blpapi_Session_destroy(blpapi_Session_t *session);

BLPAPI_EXPORT
int blpapi_Session_start(blpapi_Session_t *session);

BLPAPI_EXPORT
int blpapi_Session_startAsync(blpapi_Session_t *session);

BLPAPI_EXPORT
int blpapi_Session_stop(blpapi_Session_t *session);

BLPAPI_EXPORT
int blpapi_Session_stopAsync(blpapi_Session_t *session);

BLPAPI_EXPORT
int blpapi_Session_nextEvent(blpapi_Session_t *session,
        blpapi_Event_t **eventPointer,
        unsigned int timeoutInMilliseconds);

BLPAPI_EXPORT
int blpapi_Session_tryNextEvent(
        blpapi_Session_t *session, blpapi_Event_t **eventPointer);

BLPAPI_EXPORT
int blpapi_Session_subscribe(blpapi_Session_t *session,
        const blpapi_SubscriptionList_t *subscriptionList,
        const blpapi_Identity_t *handle,
        const char *requestLabel,
        int requestLabelLen);

BLPAPI_EXPORT
int blpapi_Session_subscribeEx(blpapi_Session_t *session,
        const blpapi_SubscriptionList_t *subscriptionList,
        const blpapi_Identity_t *handle,
        const char *requestLabel,
        int requestLabelLen,
        blpapi_SubscriptionPreprocessErrorHandler_t errorHandler,
        void *userData);

BLPAPI_EXPORT
int blpapi_Session_resubscribe(blpapi_Session_t *session,
        const blpapi_SubscriptionList_t *resubscriptionList,
        const char *requestLabel,
        int requestLabelLen);

BLPAPI_EXPORT
int blpapi_Session_resubscribeEx(blpapi_Session_t *session,
        const blpapi_SubscriptionList_t *resubscriptionList,
        const char *requestLabel,
        int requestLabelLen,
        blpapi_SubscriptionPreprocessErrorHandler_t errorHandler,
        void *userData);

BLPAPI_EXPORT
int blpapi_Session_resubscribeWithId(blpapi_Session_t *session,
        const blpapi_SubscriptionList_t *resubscriptionList,
        int resubscriptionId,
        const char *requestLabel,
        int requestLabelLen);

BLPAPI_EXPORT
int blpapi_Session_resubscribeWithIdEx(blpapi_Session_t *session,
        const blpapi_SubscriptionList_t *resubscriptionList,
        int resubscriptionId,
        const char *requestLabel,
        int requestLabelLen,
        blpapi_SubscriptionPreprocessErrorHandler_t errorHandler,
        void *userData);

BLPAPI_EXPORT
int blpapi_Session_unsubscribe(blpapi_Session_t *session,
        const blpapi_SubscriptionList_t *unsubscriptionList,
        const char *requestLabel,
        int requestLabelLen);

BLPAPI_EXPORT
int blpapi_Session_cancel(blpapi_Session_t *session,
        const blpapi_CorrelationId_t *correlationIds,
        size_t numCorrelationIds,
        const char *requestLabel,
        int requestLabelLen);

BLPAPI_EXPORT
int blpapi_Session_setStatusCorrelationId(blpapi_Session_t *session,
        const blpapi_Service_t *service,
        const blpapi_Identity_t *identity,
        const blpapi_CorrelationId_t *correlationId);

BLPAPI_EXPORT
int blpapi_Session_sendRequest(blpapi_Session_t *session,
        const blpapi_Request_t *request,
        blpapi_CorrelationId_t *correlationId,
        blpapi_Identity_t *identity,
        blpapi_EventQueue_t *eventQueue,
        const char *requestLabel,
        int requestLabelLen);

BLPAPI_EXPORT
int blpapi_Session_sendRequestTemplate(blpapi_Session_t *session,
        const blpapi_RequestTemplate_t *requestTemplate,
        blpapi_CorrelationId_t *correlationId);

BLPAPI_EXPORT
int blpapi_Session_createSnapshotRequestTemplate(
        blpapi_RequestTemplate_t **requestTemplate,
        blpapi_Session_t *session,
        const char *subscriptionString,
        const blpapi_Identity_t *identity,
        blpapi_CorrelationId_t *correlationId);

BLPAPI_EXPORT
int blpapi_Session_sendAuthorizationRequest(blpapi_Session_t *session,
        const blpapi_Request_t *request,
        blpapi_Identity_t *identity,
        blpapi_CorrelationId_t *correlationId,
        blpapi_EventQueue_t *eventQueue,
        const char *requestLabel,
        int requestLabelLen);

BLPAPI_EXPORT
int blpapi_Session_openService(
        blpapi_Session_t *session, const char *serviceName);

BLPAPI_EXPORT
int blpapi_Session_openServiceAsync(blpapi_Session_t *session,
        const char *serviceName,
        blpapi_CorrelationId_t *correlationId);

BLPAPI_EXPORT
int blpapi_Session_generateToken(blpapi_Session_t *session,
        blpapi_CorrelationId_t *correlationId,
        blpapi_EventQueue_t *eventQueue);

BLPAPI_EXPORT
int blpapi_Session_getService(blpapi_Session_t *session,
        blpapi_Service_t **service,
        const char *serviceName);

// Deprecated: Use blpapi_Session_createIdentity instead
BLPAPI_EXPORT
blpapi_UserHandle_t *blpapi_Session_createUserHandle(
        blpapi_Session_t *session);

BLPAPI_EXPORT
blpapi_Identity_t *blpapi_Session_createIdentity(blpapi_Session_t *session);

BLPAPI_EXPORT
blpapi_AbstractSession_t *blpapi_Session_getAbstractSession(
        blpapi_Session_t *session);

BLPAPI_EXPORT
blpapi_SubscriptionIterator_t *blpapi_SubscriptionItr_create(
        blpapi_Session_t *session);

BLPAPI_EXPORT
void blpapi_SubscriptionItr_destroy(blpapi_SubscriptionIterator_t *iterator);

BLPAPI_EXPORT
int blpapi_SubscriptionItr_next(blpapi_SubscriptionIterator_t *iterator,
        const char **subscriptionString,
        blpapi_CorrelationId_t *correlationId,
        int *status);

BLPAPI_EXPORT
int blpapi_SubscriptionItr_isValid(
        const blpapi_SubscriptionIterator_t *iterator);

#ifdef __cplusplus
}

#include <cassert>

#ifndef INCLUDED_CSTRING
#include <cstring> // for std::memset
#define INCLUDED_CSTRING
#endif

/** \addtogroup blpapi
 * @{
 */
/** \addtogroup blpapi_session
 * @{
 */

namespace BloombergLP {
namespace blpapi {

class Session;

/*!
 *     Defines the callback supplied by the application to process
 *     Events.
 *
 *     Applications which use event driven programming supply
 *     instances of their own subclass of this class to the Session to
 *     receive callbacks when an Event is available.
 */
/*!
 * See \ref blpapi_session
 */
class EventHandler {

  public:
    virtual ~EventHandler() = 0;

    virtual bool processEvent(const Event& event, Session *session) = 0;
    /*!<
     * An implementation of <code>processEvent</code> should process the
     * specified <code>event</code> which originates from the specified
     * <code>session</code>. The return value is ignored.
     *
     * If the application wishes to further process the <code>messages</code>
     * in the <code>event</code> after returning from the
     * <code>processEvent()</code> call, the application must make a copy of
     * the <code>event</code> to ensure the underlying data is not freed.
     */
};

extern "C" void blpapi_eventHandlerAdapter(
        blpapi_Event_t *event, blpapi_Session_t *, void *userData);
/*!<
 * Adapter <code>blpapi_EventHandler_t</code> implementation that dispatches
 * the specified <code>event</code> to a <code>blpapi::Session</code> pointed
 * by <code>userData</code>.
 */

struct SubscriptionPreprocessError {
    enum Code {
        e_invalidSubscriptionString
        = BLPAPI_SUBSCRIPTIONPREPROCESS_INVALID_SUBSCRIPTION_STRING,
        ///< Invalid subscription string. \sa blpapi_subscriptionlist

        e_correlationIdError
        = BLPAPI_SUBSCRIPTIONPREPROCESS_CORRELATIONID_ERROR
        ///< Duplicate or missing CorrelationId. When calling subscribe() this
        /// error is raised if the CorrelationId is already in use. When
        /// calling resubscribe() this error is raised if the CorrelationId is
        /// not associated with a valid subscription.
    };

    CorrelationId correlationId;
    std::string subscriptionString;
    int code;
    /*
     *     The error code that is one of <code>Code</code> enum values.
     */
    std::string description;

    SubscriptionPreprocessError(const CorrelationId& correlationId,
            const std::string& subscriptionString,
            int code,
            const char *description)
        : correlationId(correlationId)
        , subscriptionString(subscriptionString)
        , code(code)
        , description(description)
    {
    }
};

inline std::ostream& operator<<(
        std::ostream& os, const SubscriptionPreprocessError& error)
{
    os << "{"
       << "correlationId: " << error.correlationId
       << ", subscriptionString: " << error.subscriptionString << ", code: ";
    if (error.code
            == SubscriptionPreprocessError::e_invalidSubscriptionString) {
        os << "InvalidSubscriptionString";
    } else if (error.code
            == SubscriptionPreprocessError::e_correlationIdError) {
        os << "CorrelationIdError";
    } else {
        os << "UNKNOWN";
    }

    os << "(" << error.code << ")";
    os << ", description: " << error.description << "}";

    return os;
}

extern "C" inline void blpapi_SubscriptionPreprocess_handleError(
        const blpapi_CorrelationId_t *correlationId,
        const char *subscriptionString,
        int errorCode,
        const char *description,
        void *userData)
{
    typedef std::vector<SubscriptionPreprocessError> Errors;
    Errors *errors = reinterpret_cast<Errors *>(userData);
    errors->emplace_back(CorrelationId(*correlationId),
            subscriptionString,
            errorCode,
            description);
}

struct SubscriptionPreprocessMode {
    /// Enumeration used to define how the invalid subscriptions are
    /// managed in a <code>SubscriptionList</code> provided to
    /// <code>Session::subscribe()</code> and
    /// <code>Session::resubscribe()</code>.
    enum Enum {

        e_failOnFirstError,
        ///< In this mode, an exception is thrown on the first invalid
        /// subscription in the 'SubscriptionList', and the entire batch will
        /// be considered failed. Otherwise, all the subscriptions will move
        /// forward and an empty list of 'SubscriptionPreprocessError' is
        /// returned.

        e_returnIndividualErrors
        ///< In this mode, instead of throwing exceptions, a list of
        /// 'SubscriptionPreprocessError' is returned, each representing an
        /// error due to an invalid subscription in the 'SubscriptionList'. The
        /// valid subscriptions will move forward.
    };
};

typedef std::vector<SubscriptionPreprocessError> SubscriptionPreprocessErrors;

/*!
 * This class provides a consumer session for making requests for Bloomberg
 * services.
 *
 * Sessions manage access to services either by requests and responses or
 * subscriptions. A Session can dispatch events and replies in either a
 * synchronous or asynchronous mode. The mode of a Session is determined when
 * it is constructed and cannot be changed subsequently.
 *
 * A Session is asynchronous if an EventHandler object is supplied when it is
 * constructed. All incoming events are delivered to the EventHandler supplied
 * on construction.  Calls to <code>nextEvent</code> on an asynchronous session
 * will fail.
 *
 * A Session is synchronous if an EventHandler object is not supplied when it
 * is constructed. The nextEvent() method must be called to read incoming
 * events.
 *
 * Several methods in Session take a CorrelationId parameter. The application
 * may choose to supply its own CorrelationId values or allow the Session to
 * create values. If the application supplies its own CorrelationId values it
 * must manage their lifetime such that the same value is not reused for more
 * than one operation at a time. The lifetime of a CorrelationId begins when it
 * is supplied in a method invoked on a Session and ends either when it is
 * explicitly cancelled using cancel() or unsubscribe(), when a RESPONSE Event
 * (not a PARTIAL_RESPONSE) containing it is received or when a
 * SUBSCRIPTION_STATUS Event which indicates that the subscription it refers to
 * has been terminated is received.
 *
 * When using an asynchronous Session the application must be aware that
 * because the callbacks are generated from another thread they may be
 * processed before the call which generates them has returned. For example,
 * the SESSION_STATUS Event generated by a startAsync() may be processed before
 * startAsync() has returned (even though startAsync() itself will not block).
 *
 * This becomes more significant when Session generated CorrelationIds are in
 * use. For example, if a call to subscribe() which returns a Session generated
 * CorrelationId has not completed before the first Events which contain that
 * CorrelationId arrive the application may not be able to interpret those
 * events correctly. For this reason, it is preferable to use user generated
 * CorrelationIds when using asynchronous Sessions. This issue does not arise
 * when using a synchronous Session as long as the calls to subscribe() etc.
 * are made on the same thread as the calls to nextEvent().
 */
/*!
 * See \ref blpapi_session
 */
class Session : public AbstractSession {

  public:
    typedef ::BloombergLP::blpapi::EventHandler EventHandler;

  private:
    blpapi_Session_t *d_handle_p;
    EventHandler *d_eventHandler_p;

    // NOT IMPLEMENTED
    Session(const Session&);
    Session& operator=(const Session&);

    friend void blpapi_eventHandlerAdapter(
            blpapi_Event_t *event, blpapi_Session_t *, void *userData);

  public:
    /*
     *     The possible statuses a subscription may be in.
     */
    enum SubscriptionStatus {
        UNSUBSCRIBED = BLPAPI_SUBSCRIPTIONSTATUS_UNSUBSCRIBED,
        ///< No longer active, terminated by API.
        SUBSCRIBING = BLPAPI_SUBSCRIPTIONSTATUS_SUBSCRIBING,
        ///< Initiated but no updates received.
        SUBSCRIBED = BLPAPI_SUBSCRIPTIONSTATUS_SUBSCRIBED,
        ///< Updates are flowing.
        CANCELLED = BLPAPI_SUBSCRIPTIONSTATUS_CANCELLED,
        ///< No longer active, terminated by Application.
        PENDING_CANCELLATION = BLPAPI_SUBSCRIPTIONSTATUS_PENDING_CANCELLATION
    };

    explicit Session(const SessionOptions& options = SessionOptions(),
            EventHandler *eventHandler = 0,
            EventDispatcher *eventDispatcher = 0);
    /*!<
     * Construct a Session using the optionally specified <code>options</code>,
     * the optionally specified <code>eventHandler</code> and the optionally
     * specified <code>eventDispatcher</code>.
     *
     * See the SessionOptions documentation for details on what can be
     * specified in the <code>options</code>.
     *
     * If <code>eventHandler</code> is not null then this Session will
     * operate in asynchronous mode, otherwise the Session will operate in
     * synchronous mode.
     *
     * If <code>eventDispatcher</code> is null then the Session will create a
     * default EventDispatcher for this Session which will use a single thread
     * for dispatching events. For more control over event dispatching a
     * specific instance of EventDispatcher can be supplied. This can be used
     * to share a single EventDispatcher amongst multiple Session objects.
     *
     * If an <code>eventDispatcher</code> is supplied which uses more than one
     * thread the Session will ensure that events which should be ordered are
     * passed to callbacks in a correct order. For example, partial response to
     * a request or updates to a single subscription.
     *
     * The behavior is undefined if <code>eventHandler</code> is null and the
     * <code>eventDispatcher</code> is not null.
     *
     * Each EventDispatcher uses its own thread or pool of threads so if you
     * want to ensure that a session which receives very large messages and
     * takes a long time to process them does not delay a session that receives
     * small messages and processes each one very quickly then give each one a
     * separate EventDispatcher.
     */

    explicit Session(blpapi_Session_t *handle);

    virtual ~Session();
    /*!<
     *     Destructor.
     */

    virtual bool start();
    /*!<
     * Attempt to start this Session and block until the Session has started or
     * failed to start. If the Session is started successfully
     * <code>true</code> is returned, otherwise <code>false</code> is returned.
     * Before start() returns a SESSION_STATUS Event is generated. If this is
     * an asynchronous Session then the SESSION_STATUS may be processed by the
     * registered EventHandler before start() has returned. A Session may only
     * be started once.
     */

    virtual bool startAsync();
    /*!<
     * Attempt to begin the process to start this Session and return
     * <code>true</code> if successful, otherwise return <code>false</code>.
     * The application must monitor events for a SESSION_STATUS Event which
     * will be generated once the Session has started or if it fails to start.
     * If this is an asynchronous Session then the SESSION_STATUS Event may be
     * processed by the registered EventHandler before startAsync() has
     * returned. A Session may only be started once.
     */

    virtual void stop();
    /*!<
     * Stop operation of this session and block until all callbacks to
     * EventHandler objects relating to this Session which are currently in
     * progress have completed (including the callback to handle the
     * SESSION_STATUS Event with SessionTerminated message this call
     * generates). Once this returns no further callbacks to EventHandlers will
     * occur. If stop() is called from within an EventHandler callback the
     * behavior is undefined and may result in a deadlock. Once a Session has
     * been stopped it can only be destroyed.
     */

    virtual void stopAsync();
    /*!<
     * Begin the process to stop this Session and return immediately. The
     * application must monitor events for a SESSION_STATUS Event with
     * SessionTerminated message which will be generated once the Session has
     * been stopped. After this SESSION_STATUS Event no further callbacks to
     * EventHandlers will occur. This method can be called from within an
     * EventHandler callback to stop Sessions using non-default (external)
     * EventDispatcher. Once a Session has been stopped it can only be
     * destroyed.
     */

    virtual Event nextEvent(int timeout = 0);
    /*!<
     * Returns the next available Event for this session. If there is no event
     * available this will block for up to the specified
     * <code>timeoutMillis</code> milliseconds for an Event to arrive. A value
     * of 0 for <code>timeoutMillis</code> (the default) indicates nextEvent()
     * should not timeout and will not return until the next Event is
     * available.
     *
     * If nextEvent() returns due to a timeout it will return an event of type
     * <code>EventType::TIMEOUT</code>.
     *
     * If this is invoked on a Session which was created in asynchronous mode
     * an InvalidStateException is thrown.
     */

    virtual int tryNextEvent(Event *event);
    /*!<
     * If there are Events available for the session, load the next Event into
     * event and return 0 indicating success. If there is no event available
     * for the session, return a non-zero value with no effect on event. This
     * method never blocks.
     */

    virtual void subscribe(const SubscriptionList& subscriptionList,
            const Identity& identity,
            const char *requestLabel = 0,
            int requestLabelLen = 0);
    /*!<
     * Begin subscriptions for each entry in the specified
     * <code>subscriptionList</code> using the specified <code>identity</code>
     * for authorization. If the optional <code>requestLabel</code> and
     * <code>requestLabelLen</code> are provided they define a string which
     * will be recorded along with any diagnostics for this operation. There
     * must be at least <code>requestLabelLen</code> printable characters at
     * the location <code>requestLabel</code>.
     *
     * A SUBSCRIPTION_STATUS Event will be generated for each entry in the
     * <code>subscriptionList</code>.
     */

    virtual SubscriptionPreprocessErrors subscribe(
            const SubscriptionList& subscriptionList,
            const Identity& identity,
            SubscriptionPreprocessMode::Enum mode,
            const char *requestLabel = 0,
            int requestLabelLen = 0);
    /*!<
     * Begin subscriptions for each entry in the specified
     * <code>subscriptionList</code> using the specified <code>identity</code>
     * for authorization.
     *
     * The specified <code>mode</code> is used to determine how to preprocess
     * subscriptions. Refer to <code>SubscriptionPreprocessMode</code> for an
     * explanation of the available modes.
     *
     * If the optional <code>requestLabel</code> and
     * <code>requestLabelLen</code> are provided they define a string which
     * will be recorded along with any diagnostics for this operation. There
     * must be at least <code>requestLabelLen</code> printable characters at
     * the location <code>requestLabel</code>.
     *
     * A SUBSCRIPTION_STATUS Event will be generated for each valid
     * subscriptions in the list.
     */

    virtual void subscribe(const SubscriptionList& subscriptionList,
            const char *requestLabel = 0,
            int requestLabelLen = 0);
    /*!<
     * Begin subscriptions for each entry in the specified
     * <code>subscriptionList</code> using the session identity information. If
     * the optional <code>requestLabel</code> and <code>requestLabelLen</code>
     * are provided they define a string which will be recorded along with any
     * diagnostics for this operation. There must be at least
     * <code>requestLabelLen</code> printable characters at the location
     * <code>requestLabel</code>.
     *
     * A SUBSCRIPTION_STATUS Event will be generated for each entry in the
     * <code>subscriptionList</code>.
     *
     * The session identity will be used if it has been authorized.
     */

    virtual SubscriptionPreprocessErrors subscribe(
            const SubscriptionList& subscriptionList,
            SubscriptionPreprocessMode::Enum mode,
            const char *requestLabel = 0,
            int requestLabelLen = 0);
    /*!<
     * Begin subscriptions for each entry in the specified
     * <code>subscriptionList</code> using the session identity information.
     *
     * The specified <code>mode</code> is used to determine how to preprocess
     * subscriptions. Refer to <code>SubscriptionPreprocessMode</code> for an
     * explanation of the available modes.
     *
     * If the optional <code>requestLabel</code> and
     * <code>requestLabelLen</code> are provided they define a string which
     * will be recorded along with any diagnostics for this operation. There
     * must be at least <code>requestLabelLen</code> printable characters at
     * the location <code>requestLabel</code>.
     *
     * A SUBSCRIPTION_STATUS Event will be generated for each entry in the
     * <code>subscriptionList</code>.
     *
     * The session identity will be used if it has been authorized.
     */

    virtual void unsubscribe(const SubscriptionList& subscriptionList);
    /*!<
     * Cancel each of the current subscriptions identified by the specified
     * <code>subscriptionList</code>. If the correlation ID of any entry in the
     * <code>subscriptionList</code> does not identify a current subscription
     * then that entry is ignored. All entries which have valid correlation IDs
     * will be cancelled.
     *
     * Once this call returns the correlation ids in the
     * <code>subscriptionList</code> will not be seen in any subsequent Message
     * obtained from a MessageIterator by calling next(). However, any Message
     * currently pointed to by a MessageIterator when unsubscribe() is called
     * is not affected even if it has one of the correlation IDs in the
     * <code>subscriptionList</code>. Also any Message where a reference has
     * been retained by the application may still contain a correlation ID from
     * the <code>subscriptionList</code>. For these reasons, although
     * technically an application is free to re-use the correlation IDs as soon
     * as this method returns it is preferable not to aggressively re-use
     * correlation IDs, particularly with an asynchronous Session.
     */

    virtual void resubscribe(const SubscriptionList& subscriptions);
    /*!<
     * Modify each subscription in the specified <code>subscriptionList</code>
     * to reflect the modified options specified for it.
     *
     * For each entry in the <code>subscriptionList</code> which has a
     * correlation ID which identifies a current subscription the modified
     * options replace the current options for the subscription and a
     * SUBSCRIPTION_STATUS event will be generated in the event stream before
     * the first update based on the new options. If the correlation ID of an
     * entry in the <code>subscriptionList</code> does not identify a current
     * subscription then an exception is thrown.
     */

    virtual SubscriptionPreprocessErrors resubscribe(
            const SubscriptionList& subscriptions,
            SubscriptionPreprocessMode::Enum mode);
    /*!<
     * Modify each subscription in the specified <code>subscriptionList</code>
     * to reflect the modified options specified for it.
     *
     * For each entry in the <code>subscriptionList</code> which has a
     * correlation ID which identifies a current subscription the modified
     * options replace the current options for the subscription and a
     * SUBSCRIPTION_STATUS event will be generated in the event stream before
     * the first update based on the new options.
     *
     * The specified <code>mode</code> is used to determine how to preprocess
     * subscriptions. Refer to <code>SubscriptionPreprocessMode</code> for an
     * explanation of the available modes.
     */

    virtual void resubscribe(const SubscriptionList& subscriptions,
            const char *requestLabel,
            int requestLabelLen);
    /*!<
     * Modify each subscription in the specified <code>subscriptionList</code>
     * to reflect the modified options specified for it. The specified
     * <code>requestLabel</code> and <code>requestLabelLen</code> define a
     * string which will be recorded along with any diagnostics for this
     * operation. There must be at least <code>requestLabelLen</code> printable
     * characters at the location <code>requestLabel</code>.
     *
     * For each entry in the <code>subscriptionList</code> which has a
     * correlation ID which identifies a current subscription the modified
     * options replace the current options for the subscription and a
     * SUBSCRIPTION_STATUS event will be generated in the event stream before
     * the first update based on the new options. If the correlation ID of an
     * entry in the <code>subscriptionList</code> does not identify a current
     * subscription then an exception is thrown.
     */

    virtual SubscriptionPreprocessErrors resubscribe(
            const SubscriptionList& subscriptions,
            const char *requestLabel,
            int requestLabelLen,
            SubscriptionPreprocessMode::Enum mode);
    /*!<
     * Modify each subscription in the specified <code>subscriptionList</code>
     * to reflect the modified options specified for it. The specified
     * <code>requestLabel</code> and <code>requestLabelLen</code> define a
     * string which will be recorded along with any diagnostics for this
     * operation. There must be at least <code>requestLabelLen</code> printable
     * characters at the location <code>requestLabel</code>.
     *
     * For each entry in the <code>subscriptionList</code> which has a
     * correlation ID which identifies a current subscription the modified
     * options replace the current options for the subscription and a
     * SUBSCRIPTION_STATUS event will be generated in the event stream before
     * the first update based on the new options.
     *
     * The specified <code>mode</code> is used to determine how to preprocess
     * subscriptions. Refer to <code>SubscriptionPreprocessMode</code> for an
     * explanation of the available modes.
     */

    virtual void resubscribe(
            const SubscriptionList& subscriptions, int resubscriptionId);
    /*!<
     * Modify each subscription in the specified <code>subscriptionList</code>
     * to reflect the modified options specified for it.
     *
     * For each entry in the <code>subscriptionList</code> which has a
     * correlation ID which identifies a current subscription the modified
     * options replace the current options for the subscription and a
     * SUBSCRIPTION_STATUS event containing the specified
     * <code>resubscriptionId</code> will be generated in the event stream
     * before the first update based on the new options. If the correlation ID
     * of an entry in the <code>subscriptionList</code> does not identify a
     * current subscription then an exception is thrown.
     */

    virtual SubscriptionPreprocessErrors resubscribe(
            const SubscriptionList& subscriptions,
            int resubscriptionId,
            SubscriptionPreprocessMode::Enum mode);
    /*!<
     * Modify each subscription in the specified <code>subscriptionList</code>
     * to reflect the modified options specified for it.
     *
     * For each entry in the <code>subscriptionList</code> which has a
     * correlation ID which identifies a current subscription the modified
     * options replace the current options for the subscription and a
     * SUBSCRIPTION_STATUS event containing the specified
     * <code>resubscriptionId</code> will be generated in the event stream
     * before the first update based on the new options.
     *
     * The specified <code>mode</code> is used to determine how to preprocess
     * subscriptions. Refer to <code>SubscriptionPreprocessMode</code> for an
     * explanation of the available modes.
     */

    virtual void resubscribe(const SubscriptionList& subscriptions,
            int resubscriptionId,
            const char *requestLabel,
            int requestLabelLen);
    /*!<
     * Modify each subscription in the specified <code>subscriptionList</code>
     * to reflect the modified options specified for it. The specified
     * <code>requestLabel</code> and <code>requestLabelLen</code> define a
     * string which will be recorded along with any diagnostics for this
     * operation. There must be at least <code>requestLabelLen</code> printable
     * characters at the location <code>requestLabel</code>.
     *
     * For each entry in the <code>subscriptionList</code> which has a
     * correlation ID which identifies a current subscription the modified
     * options replace the current options for the subscription and a
     * SUBSCRIPTION_STATUS event containing the specified
     * <code>resubscriptionId</code> will be generated in the event stream
     * before the first update based on the new options. If the correlation ID
     * of an entry in the <code>subscriptionList</code> does not identify a
     * current subscription then an exception is thrown.
     */

    virtual SubscriptionPreprocessErrors resubscribe(
            const SubscriptionList& subscriptions,
            int resubscriptionId,
            const char *requestLabel,
            int requestLabelLen,
            SubscriptionPreprocessMode::Enum mode);
    /*!<
     * Modify each subscription in the specified <code>subscriptionList</code>
     * to reflect the modified options specified for it. The specified
     * <code>requestLabel</code> and <code>requestLabelLen</code> define a
     * string which will be recorded along with any diagnostics for this
     * operation. There must be at least <code>requestLabelLen</code> printable
     * characters at the location <code>requestLabel</code>.
     *
     * For each entry in the <code>subscriptionList</code> which has a
     * correlation ID which identifies a current subscription the modified
     * options replace the current options for the subscription and a
     * SUBSCRIPTION_STATUS event containing the specified
     * <code>resubscriptionId</code> will be generated in the event stream
     * before the first update based on the new options.
     *
     * The specified <code>mode</code> is used to determine how to preprocess
     * subscriptions. Refer to <code>SubscriptionPreprocessMode</code> for an
     * explanation of the available modes.
     */

    virtual void setStatusCorrelationId(
            const Service& service, const CorrelationId& correlationID);

    virtual void setStatusCorrelationId(const Service& service,
            const Identity& identity,
            const CorrelationId& correlationID);
    /*!<
     * Set the CorrelationID on which service status messages will be received.
     * Note: No service status messages are received prior to this call
     */

    virtual CorrelationId sendRequest(const Request& request,
            const CorrelationId& correlationId = CorrelationId(),
            EventQueue *eventQueue = 0,
            const char *requestLabel = 0,
            int requestLabelLen = 0);
    /*!<
     * Send the specified <code>request</code>. If the optionally specified
     * <code>correlationId</code> is supplied use it otherwise create a
     * CorrelationId. The actual CorrelationId used is returned. If the
     * optionally specified <code>eventQueue</code> is supplied all events
     * relating to this Request will arrive on that EventQueue. If the optional
     * <code>requestLabel</code> and <code>requestLabelLen</code> are provided
     * they define a string which will be recorded along with any diagnostics
     * for this operation. There must be at least <code>requestLabelLen</code>
     * printable characters at the location <code>requestLabel</code>.
     *
     * A successful request will generate zero or more PARTIAL_RESPONSE
     * Messages followed by exactly one RESPONSE Message. Once the final
     * RESPONSE Message has been received the correlation ID associated with
     * this request may be re-used. If the request fails at any stage a
     * REQUEST_STATUS will be generated after which the correlation ID
     * associated with the request may be re-used.
     *
     * The session identity will be used if it has been authorized.
     */

    virtual CorrelationId sendRequest(const Request& request,
            const Identity& user,
            const CorrelationId& correlationId = CorrelationId(),
            EventQueue *eventQueue = 0,
            const char *requestLabel = 0,
            int requestLabelLen = 0);
    /*!<
     * Send the specified <code>request</code> using the specified
     * <code>identity</code> for authorization. If the optionally specified
     * <code>correlationId</code> is supplied use it otherwise create a
     * CorrelationId. The actual CorrelationId used is returned. If the
     * optionally specified <code>eventQueue</code> is supplied all events
     * relating to this Request will arrive on that EventQueue. If the optional
     * <code>requestLabel</code> and <code>requestLabelLen</code> are provided
     * they define a string which will be recorded along with any diagnostics
     * for this operation. There must be at least <code>requestLabelLen</code>
     * printable characters at the location <code>requestLabel</code>.
     *
     * A successful request will generate zero or more PARTIAL_RESPONSE
     * Messages followed by exactly one RESPONSE Message. Once the final
     * RESPONSE Message has been received the CorrelationId associated with
     * this request may be re-used. If the request fails at any stage a
     * REQUEST_STATUS will be generated after which the CorrelationId
     * associated with the request may be re-used.
     */

    virtual CorrelationId sendRequest(const RequestTemplate& requestTemplate,
            const CorrelationId& correlationId = CorrelationId());
    /*!<
     * Send a request defined by the specified <code>requestTemplate</code>. If
     * the optionally specified <code>correlationId</code> is supplied, use it
     * otherwise create a new <code>CorrelationId</code>. The actual
     * <code>CorrelationId</code> used is returned.
     *
     * A successful request will generate zero or more
     * <code>PARTIAL_RESPONSE</code> events followed by exactly one
     * <code>RESPONSE</code> event. Once the final <code>RESPONSE</code> event
     * has been received the <code>CorrelationId</code> associated with  this
     * request may be re-used. If the request fails at any stage a
     * <code>REQUEST_STATUS</code> will be generated after which the
     * <code>CorrelationId</code> associated with the request may be re-used.
     *
     * The session identity will be used if it has been authorized.
     */

    virtual RequestTemplate createSnapshotRequestTemplate(
            const char *subscriptionString,
            const CorrelationId& statusCid,
            const Identity& identity = Identity());
    /*!<
     * Create a snapshot request template for getting subscription data
     * specified by the <code>subscriptionString</code> using the optionally
     * specified <code>identity</code> if all the following conditions are met:
     * the session is established, <code>subscriptionString</code> is a valid
     * subscription string and <code>statusCid</code> is not used in this
     * session. If one or more conditions are not met, an exception is thrown.
     * The provided <code>statusCid</code> will be used for status updates
     * about the created request template state and an implied subscription
     * associated with it delivered by <code>SUBSCRIPTION_STATUS</code> events.
     *
     * The benefit of the snapshot request templates is that these requests may
     * be serviced from a cache and the user may expect to see significantly
     * lower response time.
     *
     * There are 3 possible states for a created request template:
     * <code>Pending</code>, <code>Available</code>, and
     * <code>Terminated</code>. Right after creation a request template is in
     * the <code>Pending</code> state.
     *
     * If a state is <code>Pending</code>, the user may send a request using
     * this template but there are no guarantees about response time since
     * cache is not available yet. Request template may transition into
     * <code>Pending</code> state only from the <code>Available</code> state.
     * In this case the <code>RequestTemplatePending</code> message is
     * generated.
     *
     * If state is <code>Available</code>, all requests will be serviced from a
     * cache and the user may expect to see significantly reduced latency.
     * Note, that a snapshot request template can transition out of the
     * <code>Available</code> state concurrently with requests being sent, so
     * no guarantee of service from the cache can be provided. Request template
     * may transition into <code>Available</code> state only from the
     * <code>Pending</code> state. In this case the
     * <code>RequestTemplateAvailable</code> message is generated. This message
     * will also contain information about currently used connection in the
     * <code>boundTo</code> field. Note that it is possible to get the
     * <code>RequestTemplateAvailable</code> message with a new connection
     * information, even if a request template is already in the
     * <code>Available</code> state.
     *
     * If state is <code>Terminated</code>, sending request will always result
     * in a failure response. Request template may transition into this state
     * from any other state. This is a final state and it is guaranteed that
     * the last message associated with the provided <code>statusCid</code>
     * will be the <code>RequestTemplateTerminated</code> message which is
     * generated when a request template transitions into this state. If a
     * request template transitions into this state, all outstanding requests
     * will be failed and appropriate messages will be generated for each
     * request. After receiving the <code>RequestTemplateTerminated</code>
     * message, <code>statusCid</code> may be reused.
     *
     * Note that resources used by a snapshot request template are released
     * only when request template transitions into the <code>Terminated</code>
     * state or when session is destroyed. In order to release resources when
     * request template is not needed anymore, user should call the
     * <code>Session::cancel(statusCid)</code> unless the
     * <code>RequestTemplateTerminated</code> message was already received due
     * to some problems. When the last copy of a <code>RequestTemplate</code>
     * object goes out of scope and there are no outstanding requests left, the
     * snapshot request template will be destroyed automatically. If the last
     * copy of a <code>RequestTemplate</code> object goes out of scope while
     * there are still some outstanding requests left, snapshot service request
     * template will be destroyed automatically when the last request gets a
     * final response.
     *
     * Note that it is possible to have a situation when response contains data
     * that were sent by a backend before user actually sends a request
     * template. For example it is possible if user requested data were already
     * in an incoming TCP queue.
     *
     * If <code>identity</code> is not provided, the session identity will
     * be used if it has been authorized.
     */

    blpapi_Session_t *handle() const;
};

/*!
 * An iterator which steps through all the subscriptions in a Session.
 *
 * The SubscriptionIterator can be used to iterate over all the active
 * subscriptions for a Session. However, with an asynchronous Session it is
 * possible for the set of active subscriptions to change whilst the
 * SubscriptionIterator is being used. The SubscriptionIterator is guaranteed
 * to never return the same subscription twice. However, the subscription the
 * iterator points to may no longer be active. In this case the result of
 * subscriptionStatus() will be UNSUBSCRIBED or CANCELLED.
 */
/*!
 * See \ref blpapi_session
 */
class SubscriptionIterator {

    blpapi_SubscriptionIterator_t *d_handle_p;
    const char *d_subscriptionString;
    CorrelationId d_correlationId;
    int d_status;
    bool d_isValid;

  private:
    // NOT IMPLEMENTED
    SubscriptionIterator(const SubscriptionIterator&);
    SubscriptionIterator& operator=(const SubscriptionIterator&);

  public:
    explicit SubscriptionIterator(Session *session);
    /*!<
     * Construct a forward iterator to iterate over the subscriptions of the
     * specified <code>session</code>. The SubscriptionIterator is created in a
     * state where next() must be called to advance it to the first item.
     */

    ~SubscriptionIterator();
    /*!<
     * Destructor.
     */

    // MANIPULATORS

    bool next();
    /*!<
     * Attempt to advance this iterator to the next subscription record.
     * Returns <code>true</code> on success and <code>false</code> if there are
     * no more subscriptions. After next() returns true isValid() is guaranteed
     * to return true until the next call to next(). After next() returns false
     * isValid() will return false.
     */

    // ACCESSORS

    bool isValid() const;
    /*!<
     * Returns true if this iterator is currently positioned on a valid
     * subscription.  Returns false otherwise.
     */

    const char *subscriptionString() const;
    /*!<
     * Returns a pointer to a null-terminated string which contains the
     * subscription string for this subscription. The pointer returned remains
     * valid until this SubscriptionIterator is destroyed or the underlying
     * Session is destroyed or next() is called.
     */

    const CorrelationId& correlationId() const;
    /*!<
     * Returns the CorrelationId for this subscription.
     */

    Session::SubscriptionStatus subscriptionStatus() const;
    /*!<
     * Returns the status of this subscription.
     */

    blpapi_SubscriptionIterator_t *handle() const;
};

/** @} */
/** @} */

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

// ------------------
// class EventHandler
// ------------------

inline EventHandler::~EventHandler() { }

// FREE FUNCTIONS
extern "C" inline void blpapi_eventHandlerAdapter(
        blpapi_Event_t *event, blpapi_Session_t *, void *userData)
{
    Session *session = reinterpret_cast<Session *>(userData);
    assert(session);
    assert(session->d_eventHandler_p);
    session->d_eventHandler_p->processEvent(Event(event), session);
}

// -------------
// class Session
// -------------

inline Session::Session(const SessionOptions& parameters,
        EventHandler *handler,
        EventDispatcher *dispatcher)
    : d_eventHandler_p(handler)
{
    if (dispatcher && !handler) {
        throw InvalidArgumentException(
                "Event dispatcher provided without handler.");
    }
    d_handle_p = blpapi_Session_create(parameters.handle(),
            handler ? &blpapi_eventHandlerAdapter : 0,
            dispatcher ? dispatcher->impl() : 0,
            this);
    initAbstractSessionHandle(blpapi_Session_getAbstractSession(d_handle_p));
}

inline Session::Session(blpapi_Session_t *newHandle)
    : d_handle_p(newHandle)
{
    initAbstractSessionHandle(blpapi_Session_getAbstractSession(d_handle_p));
}

inline Session::~Session() { blpapi_Session_destroy(d_handle_p); }

inline bool Session::start()
{
    return blpapi_Session_start(d_handle_p) ? false : true;
}

inline bool Session::startAsync()
{
    return blpapi_Session_startAsync(d_handle_p) ? false : true;
}

inline void Session::stop() { blpapi_Session_stop(d_handle_p); }

inline void Session::stopAsync() { blpapi_Session_stopAsync(d_handle_p); }

inline Event Session::nextEvent(int timeout)
{
    if (timeout < 0) {
        throw InvalidArgumentException("Timeout must be >= 0.");
    }

    blpapi_Event_t *event = 0;
    ExceptionUtil::throwOnError(blpapi_Session_nextEvent(
            d_handle_p, &event, static_cast<unsigned>(timeout)));
    return Event(event);
}

inline int Session::tryNextEvent(Event *event)
{
    blpapi_Event_t *impl = 0;
    int ret = blpapi_Session_tryNextEvent(d_handle_p, &impl);
    if (0 == ret) {
        assert(event);

        *event = Event(impl);
    }
    return ret;
}

inline void Session::subscribe(const SubscriptionList& subscriptions,
        const char *requestLabel,
        int requestLabelLen)
{
    ExceptionUtil::throwOnError(blpapi_Session_subscribe(d_handle_p,
            subscriptions.impl(),
            0, // identityHandle
            requestLabel,
            requestLabelLen));
}

inline SubscriptionPreprocessErrors Session::subscribe(
        const SubscriptionList& subscriptions,
        SubscriptionPreprocessMode::Enum mode,
        const char *requestLabel,
        int requestLabelLen)
{
    SubscriptionPreprocessErrors errors;
    if (mode == SubscriptionPreprocessMode::e_failOnFirstError) {
        subscribe(subscriptions, requestLabel, requestLabelLen);
    } else {
        ExceptionUtil::throwOnError(
                BLPAPI_CALL(blpapi_Session_subscribeEx)(d_handle_p,
                        subscriptions.impl(),
                        0, // identityHandle
                        requestLabel,
                        requestLabelLen,
                        &blpapi_SubscriptionPreprocess_handleError,
                        &errors));
    }

    return errors;
}

inline void Session::subscribe(const SubscriptionList& subscriptions,
        const Identity& identity,
        const char *requestLabel,
        int requestLabelLen)
{
    ExceptionUtil::throwOnError(blpapi_Session_subscribe(d_handle_p,
            subscriptions.impl(),
            identity.handle(),
            requestLabel,
            requestLabelLen));
}

inline SubscriptionPreprocessErrors Session::subscribe(
        const SubscriptionList& subscriptions,
        const Identity& identity,
        SubscriptionPreprocessMode::Enum mode,
        const char *requestLabel,
        int requestLabelLen)
{
    SubscriptionPreprocessErrors errors;
    if (mode == SubscriptionPreprocessMode::e_failOnFirstError) {
        subscribe(subscriptions, identity, requestLabel, requestLabelLen);
    } else {
        ExceptionUtil::throwOnError(
                BLPAPI_CALL(blpapi_Session_subscribeEx)(d_handle_p,
                        subscriptions.impl(),
                        identity.handle(),
                        requestLabel,
                        requestLabelLen,
                        &blpapi_SubscriptionPreprocess_handleError,
                        &errors));
    }

    return errors;
}

inline void Session::resubscribe(const SubscriptionList& subscriptions)
{
    ExceptionUtil::throwOnError(blpapi_Session_resubscribe(d_handle_p,
            subscriptions.impl(),
            0, // requestLabel
            0)); // requestLabelLen
}

inline SubscriptionPreprocessErrors Session::resubscribe(
        const SubscriptionList& subscriptions,
        SubscriptionPreprocessMode::Enum mode)
{
    SubscriptionPreprocessErrors errors;
    if (mode == SubscriptionPreprocessMode::e_failOnFirstError) {
        resubscribe(subscriptions);
    } else {
        ExceptionUtil::throwOnError(
                BLPAPI_CALL(blpapi_Session_resubscribeEx)(d_handle_p,
                        subscriptions.impl(),
                        0, // requestLabel
                        0, // requestLabelLen
                        &blpapi_SubscriptionPreprocess_handleError,
                        &errors));
    }

    return errors;
}

inline void Session::resubscribe(const SubscriptionList& subscriptions,
        const char *requestLabel,
        int requestLabelLen)
{
    ExceptionUtil::throwOnError(blpapi_Session_resubscribe(
            d_handle_p, subscriptions.impl(), requestLabel, requestLabelLen));
}

inline SubscriptionPreprocessErrors Session::resubscribe(
        const SubscriptionList& subscriptions,
        const char *requestLabel,
        int requestLabelLen,
        SubscriptionPreprocessMode::Enum mode)
{
    SubscriptionPreprocessErrors errors;
    if (mode == SubscriptionPreprocessMode::e_failOnFirstError) {
        resubscribe(subscriptions, requestLabel, requestLabelLen);
    } else {
        ExceptionUtil::throwOnError(
                BLPAPI_CALL(blpapi_Session_resubscribeEx)(d_handle_p,
                        subscriptions.impl(),
                        requestLabel,
                        requestLabelLen,
                        &blpapi_SubscriptionPreprocess_handleError,
                        &errors));
    }

    return errors;
}

inline void Session::resubscribe(
        const SubscriptionList& subscriptions, int resubscriptionId)
{
    ExceptionUtil::throwOnError(
            BLPAPI_CALL_SESSION_RESUBSCRIBEWITHID(d_handle_p,
                    subscriptions.impl(),
                    resubscriptionId,
                    0, // requestLabel
                    0)); // requestLabelLen
}

inline SubscriptionPreprocessErrors Session::resubscribe(
        const SubscriptionList& subscriptions,
        int resubscriptionId,
        SubscriptionPreprocessMode::Enum mode)
{
    SubscriptionPreprocessErrors errors;
    if (mode == SubscriptionPreprocessMode::e_failOnFirstError) {
        resubscribe(subscriptions, resubscriptionId);
    } else {
        ExceptionUtil::throwOnError(
                BLPAPI_CALL(blpapi_Session_resubscribeWithIdEx)(d_handle_p,
                        subscriptions.impl(),
                        resubscriptionId,
                        0, // requestLabel
                        0, // requestLabelLen
                        &blpapi_SubscriptionPreprocess_handleError,
                        &errors));
    }

    return errors;
}

inline void Session::resubscribe(const SubscriptionList& subscriptions,
        int resubscriptionId,
        const char *requestLabel,
        int requestLabelLen)
{
    ExceptionUtil::throwOnError(
            BLPAPI_CALL_SESSION_RESUBSCRIBEWITHID(d_handle_p,
                    subscriptions.impl(),
                    resubscriptionId,
                    requestLabel,
                    requestLabelLen));
}

inline SubscriptionPreprocessErrors Session::resubscribe(
        const SubscriptionList& subscriptions,
        int resubscriptionId,
        const char *requestLabel,
        int requestLabelLen,
        SubscriptionPreprocessMode::Enum mode)
{
    SubscriptionPreprocessErrors errors;
    if (mode == SubscriptionPreprocessMode::e_failOnFirstError) {
        resubscribe(subscriptions,
                resubscriptionId,
                requestLabel,
                requestLabelLen);
    } else {
        ExceptionUtil::throwOnError(
                BLPAPI_CALL(blpapi_Session_resubscribeWithIdEx)(d_handle_p,
                        subscriptions.impl(),
                        resubscriptionId,
                        requestLabel,
                        requestLabelLen,
                        &blpapi_SubscriptionPreprocess_handleError,
                        &errors));
    }

    return errors;
}

inline void Session::unsubscribe(const SubscriptionList& list)
{
    ExceptionUtil::throwOnError(
            blpapi_Session_unsubscribe(d_handle_p, list.impl(), 0, 0));
}

inline void Session::setStatusCorrelationId(
        const Service& service, const CorrelationId& correlationID)
{
    ExceptionUtil::throwOnError(blpapi_Session_setStatusCorrelationId(
            d_handle_p, service.handle(), 0, &correlationID.impl()));
}

inline void Session::setStatusCorrelationId(const Service& service,
        const Identity& identity,
        const CorrelationId& correlationID)
{
    ExceptionUtil::throwOnError(
            blpapi_Session_setStatusCorrelationId(d_handle_p,
                    service.handle(),
                    identity.handle(),
                    &correlationID.impl()));
}

inline CorrelationId Session::sendRequest(const Request& request,
        const Identity& identity,
        const CorrelationId& correlationId,
        EventQueue *eventQueue,
        const char *requestLabel,
        int requestLabelLen)
{
    CorrelationId retCorrelationId(correlationId);

    ExceptionUtil::throwOnError(blpapi_Session_sendRequest(d_handle_p,
            request.handle(),
            &retCorrelationId.impl(),
            identity.handle(),
            eventQueue ? eventQueue->handle() : 0,
            requestLabel,
            requestLabelLen));

    return retCorrelationId;
}

inline CorrelationId Session::sendRequest(const Request& request,
        const CorrelationId& correlationId,
        EventQueue *eventQueue,
        const char *requestLabel,
        int requestLabelLen)
{
    CorrelationId retCorrelationId(correlationId);

    ExceptionUtil::throwOnError(blpapi_Session_sendRequest(d_handle_p,
            request.handle(),
            &retCorrelationId.impl(),
            0,
            eventQueue ? eventQueue->handle() : 0,
            requestLabel,
            requestLabelLen));

    return retCorrelationId;
}

inline CorrelationId Session::sendRequest(
        const RequestTemplate& requestTemplate,
        const CorrelationId& correlationId)
{
    CorrelationId retCorrelationId(correlationId);

    ExceptionUtil::throwOnError(BLPAPI_CALL(
            blpapi_Session_sendRequestTemplate)(
            d_handle_p, requestTemplate.impl(), &retCorrelationId.impl()));

    return retCorrelationId;
}

inline RequestTemplate Session::createSnapshotRequestTemplate(
        const char *subscriptionString,
        const CorrelationId& correlationID,
        const Identity& identity)
{
    blpapi_RequestTemplate_t *requestTemplate = 0;

    ExceptionUtil::throwOnError(BLPAPI_CALL(
            blpapi_Session_createSnapshotRequestTemplate)(&requestTemplate,
            d_handle_p,
            subscriptionString,
            identity.handle(),
            const_cast<blpapi_CorrelationId_t *>(&correlationID.impl())));

    return RequestTemplate(requestTemplate);
}

inline blpapi_Session_t *Session::handle() const { return d_handle_p; }

// --------------------------
// class SubscriptionIterator
// --------------------------

inline SubscriptionIterator::SubscriptionIterator(Session *session)
    : d_isValid(false)
{
    assert(session);

    d_handle_p = blpapi_SubscriptionItr_create(session->handle());
}

inline SubscriptionIterator::~SubscriptionIterator()
{
    blpapi_SubscriptionItr_destroy(d_handle_p);
}

inline bool SubscriptionIterator::next()
{
    blpapi_CorrelationId_t nextCorrelationIdImpl;
    d_isValid = !blpapi_SubscriptionItr_next(d_handle_p,
            &d_subscriptionString,
            &nextCorrelationIdImpl,
            &d_status);

    d_correlationId = d_isValid ? CorrelationId(nextCorrelationIdImpl)
                                : CorrelationId();

    return d_isValid;
}

inline bool SubscriptionIterator::isValid() const { return d_isValid; }

inline blpapi_SubscriptionIterator_t *SubscriptionIterator::handle() const
{
    return d_handle_p;
}

inline const char *SubscriptionIterator::subscriptionString() const
{
    if (!isValid()) {
        ExceptionUtil::throwOnError(BLPAPI_ERROR_ILLEGAL_STATE);
    }

    return d_subscriptionString;
}

inline const CorrelationId& SubscriptionIterator::correlationId() const
{
    if (!isValid()) {
        ExceptionUtil::throwOnError(BLPAPI_ERROR_ILLEGAL_STATE);
    }

    return d_correlationId;
}

inline Session::SubscriptionStatus
SubscriptionIterator::subscriptionStatus() const
{
    if (!isValid()) {
        ExceptionUtil::throwOnError(BLPAPI_ERROR_ILLEGAL_STATE);
    }

    return static_cast<Session::SubscriptionStatus>(d_status);
}

} // close namespace blpapi
} // close namespace BloombergLP

#endif // ifdef __cplusplus

#endif // INCLUDED_BLPAPI_SESSION
