/* 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_session.h                                                   -*-C++-*-
#ifndef INCLUDED_BLPAPI_SESSION
#define INCLUDED_BLPAPI_SESSION

//@PURPOSE: Provide consumer session to get Bloomberg Service.
//
//@CLASSES:
// blpapi::Session: A consumer session for consuming service
//
//@DESCRIPTION: 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);

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_resubscribe(
        blpapi_Session_t *session,
        const blpapi_SubscriptionList_t *resubscriptionList,
        const char *requestLabel,
        int requestLabelLen);

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_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
}

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

namespace BloombergLP {
namespace blpapi {

class Session;
                         // ==================
                         // class EventHandler
                         // ==================

class EventHandler {
    // 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.

  public:
    virtual ~EventHandler() = 0;

    virtual bool processEvent(
            const Event& event,
            Session *session) = 0;
        // An implementation of processEvent should process the specified
        // 'event' which originates from the specified 'session' and
        // return true to indicate events should continue to be delivered
        // or false to terminate dispatching of events.
        //
        // If the application wishes to process the event further
        // after returning from the processEvents() call it must make
        // a copy of the Event to ensure the underlying data is not
        // freed.
};

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

                         // =============
                         // class Session
                         // =============

class Session : public AbstractSession {
    // 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 'nextEvent'
    // 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().

    blpapi_Session_t            *d_handle_p;
    EventHandler                *d_eventHandler_p;

  private:
    // 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
    };

    Session(const SessionOptions& options = SessionOptions(),
            EventHandler* eventHandler=0,
            EventDispatcher* eventDispatcher=0);
        // Construct a Session using the optionally specified
        // 'options', the optionally specified 'eventHandler' and the
        // optionally specified 'eventDispatcher'.
        //
        // See the SessionOptions documentation for details on what
        // can be specified in the 'options'.
        //
        // If 'eventHandler' is not null then this Session will operation
        // in asynchronous mode, otherwise the Session will operate in
        // synchronous mode.
        //
        // If 'eventDispatcher' 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 'eventDispatcher' 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 'eventHandler' is null and the
        // 'eventDispatcher' 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.

    // MANIPULATORS

    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 'true' is returned, otherwise 'false' 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 'true' if successful, otherwise return 'false'. 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 'timeoutMillis' milliseconds for an Event to
        // arrive. A value of 0 for 'timeoutMillis' (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 'EventType::TIMEOUT'.
        //
        // 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.

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

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

    void unsubscribe(const SubscriptionList& subscriptionList);
        // Cancel each of the current subscriptions identified by the
        // specified 'subscriptionList'. If the correlation ID of any
        // entry in the 'subscriptionList' 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
        // 'subscriptionList' 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
        // 'subscriptionList'. Also any Message where a reference has
        // been retained by the application may still contain a
        // correlation ID from the 'subscriptionList'. 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.

    void resubscribe(const SubscriptionList& subscriptions);
        // Modify each subscription in the specified
        // 'subscriptionList' to reflect the modified options
        // specified for it.
        //
        // For each entry in the 'subscriptionList' 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 'subscriptionList' does not identify a current
        // subscription then an exception is thrown.

    void resubscribe(
            const SubscriptionList& subscriptions,
            const char *requestLabel,
            int requestLabelLen);
        // Modify each subscription in the specified
        // 'subscriptionList' to reflect the modified options
        // specified for it. The specified 'requestLabel' and
        // 'requestLabelLen' define a string which
        // will be recorded along with any diagnostics for this
        // operation. There must be at least 'requestLabelLen'
        // printable characters at the location 'requestLabel'.
        //
        // For each entry in the 'subscriptionList' 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 'subscriptionList' does not identify a current
        // subscription then an exception is thrown.

    void resubscribe(
            const SubscriptionList& subscriptions,
            int resubscriptionId);
        // Modify each subscription in the specified
        // 'subscriptionList' to reflect the modified options
        // specified for it.
        //
        // For each entry in the 'subscriptionList' 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 'resubscriptionId' 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 'subscriptionList'
        // does not identify a current subscription then an exception
        // is thrown.

    void resubscribe(
            const SubscriptionList& subscriptions,
            int resubscriptionId,
            const char *requestLabel,
            int requestLabelLen);
        // Modify each subscription in the specified
        // 'subscriptionList' to reflect the modified options
        // specified for it. The specified 'requestLabel' and
        // 'requestLabelLen' define a string which
        // will be recorded along with any diagnostics for this
        // operation. There must be at least 'requestLabelLen'
        // printable characters at the location 'requestLabel'.
        //
        // For each entry in the 'subscriptionList' 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 'resubscriptionId' 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 'subscriptionList'
        // does not identify a current subscription then an exception
        // is thrown.

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

    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

    CorrelationId sendRequest(
            const Request& request,
            const CorrelationId& correlationId=CorrelationId(),
            EventQueue *eventQueue=0,
            const char *requestLabel = 0,
            int requestLabelLen = 0);
        // Send the specified 'request'. If the optionally specified
        // 'correlationId' is supplied use it otherwise create a CorrelationId.
        // The actual CorrelationId used is returned. If the optionally
        // specified 'eventQueue' is supplied all events relating to this
        // Request will arrive on that EventQueue. If the optional
        // 'requestLabel' and 'requestLabelLen' are provided they define a
        // string which will be recorded along with any diagnostics for this
        // operation. There must be at least 'requestLabelLen' printable
        // characters at the location 'requestLabel'.
        //
        // 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.

    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 'request' using the specified 'identity' for
        // authorization. If the optionally specified 'correlationId' is
        // supplied use it otherwise create a CorrelationId. The actual
        // CorrelationId used is returned. If the optionally specified
        // 'eventQueue' is supplied all events relating to this Request will
        // arrive on that EventQueue. If the optional 'requestLabel' and
        // 'requestLabelLen' are provided they define a string which will be
        // recorded along with any diagnostics for this operation. There must
        // be at least 'requestLabelLen' printable characters at the location
        // 'requestLabel'.
        //
        // 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.

    CorrelationId sendRequest(
            const RequestTemplate& requestTemplate,
            const CorrelationId& correlationId = CorrelationId());
        // Send a request defined by the specified 'requestTemplate'. If the
        // optionally specified 'correlationId' is supplied, use it otherwise
        // create a new 'CorrelationId'. The actual 'CorrelationId' used is
        // returned.
        //
        // A successful request will generate zero or more 'PARTIAL_RESPONSE'
        // events followed by exactly one 'RESPONSE' event. Once the final
        // 'RESPONSE' event 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.

    RequestTemplate createSnapshotRequestTemplate(
            const char *subscriptionString,
            const CorrelationId& statusCid,
            const Identity& identity = Identity());
        // Create a snapshot request template for getting subscription data
        // specified by the 'subscriptionString' using the optionally specified
        // 'identity' if all the following conditions are met: the session is
        // established, 'subscriptionString' is a valid subscription string and
        // 'statusCid' is not used in this session. If one or more conditions
        // are not met, an exception is thrown. The provided 'statusCid' will
        // be used for status updates about the created request template state
        // and an implied subscription associated with it delivered by
        // 'SUBSCRIPTION_STATUS' 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:
        // 'Pending', 'Available', and 'Terminated'. Right after creation a
        // request template is in the 'Pending' state.
        //
        // If a state is 'Pending', 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 'Pending'
        // state only from the 'Available' state. In this case the
        // 'RequestTemplatePending' message is generated.
        //
        // If state is 'Available', 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
        // 'Available' state concurrently with requests being sent, so no
        // guarantee of service from the cache can be provided. Request
        // template may transition into 'Available' state only from the
        // 'Pending' state. In this case the 'RequestTemplateAvailable' message
        // is generated. This message will also contain information about
        // currently used connection in the 'boundTo' field. Note that it is
        // possible to get the 'RequestTemplateAvailable' message with a new
        // connection information, even if a request template is already in the
        // 'Available' state.
        //
        // If state is 'Terminated', 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 'statusCid' will
        // be the 'RequestTemplateTerminated' 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 'RequestTemplateTerminated' message, 'statusCid' may
        // be reused.
        //
        // Note that resources used by a snapshot request template are released
        // only when request template transitions into the 'Terminated' state
        // or when session is destroyed. In order to release resources when
        // request template is not needed anymore, user should call the
        // 'Session::cancel(statusCid)' unless the 'RequestTemplateTerminated'
        // message was already received due to some problems. When the last
        // copy of a 'RequestTemplate' 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 'RequestTemplate'
        // 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.

    // ACCESSORS

    blpapi_Session_t* handle() const;
};
                         // ==========================
                         // class SubscriptionIterator
                         // ==========================

class SubscriptionIterator {
    // 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.

    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:
    SubscriptionIterator(Session *session);
        // Construct a forward iterator to iterate over the
        // subscriptions of the specified 'session'. 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 'true' on success and 'false' 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);
    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)
{
    blpapi_Event_t *event;
    ExceptionUtil::throwOnError(
        blpapi_Session_nextEvent(d_handle_p, &event, timeout));
    return Event(event);
}

inline
int Session::tryNextEvent(Event *event)
{
    blpapi_Event_t *impl;
    int ret = blpapi_Session_tryNextEvent(d_handle_p, &impl);
    if (0 == ret) {
        *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, requestLabel, requestLabelLen)
    );
}

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
void Session::resubscribe(const SubscriptionList& subscriptions)
{
    ExceptionUtil::throwOnError(
        blpapi_Session_resubscribe(d_handle_p, subscriptions.impl(),
            0, 0)
    );
}

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
void Session::resubscribe(const SubscriptionList& subscriptions,
                          int resubscriptionId)
{
    ExceptionUtil::throwOnError(
        BLPAPI_CALL_SESSION_RESUBSCRIBEWITHID(d_handle_p, subscriptions.impl(),
            resubscriptionId, 0, 0)
    );
}

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
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(),
        const_cast<blpapi_CorrelationId_t *>(&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(),
            const_cast<blpapi_CorrelationId_t *>(&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)
{
    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