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

//@PURPOSE: A common interface shared between publish and consumer sessions.
//
//@CLASSES:
// blpapi::AbstractSession: shared interface between different session types.
//
//@SEE_ALSO: blpapi_session, blpapi_providersession
//
//@DESCRIPTION: This file defines an abstract class 'AbstractSession' - an
// interfaces which are shared between its concrete implementations 'Session'
// and 'ProviderSession'.
//
// SERVICE IDENTIFIER
// ------------------
// A service identifier is the fully qualified service name which uniquely
// identifies the service in the API infrastructure.
// A service must be of the form "//<namespace>/<local-name>" where
// '<namespace>' and '<local-name>' are non-empty strings of characters from
// the set '[-_.a-zA-Z0-9]'. Service identifiers are case-insensitive, but
// clients are encouraged to prefer identifiers without upper-case characters.
// Note that the <namespace> and <local-name> cannot contain the character
// '/'.

#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_SERVICE
#include <blpapi_service.h>
#endif

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

#ifdef __cplusplus
extern "C" {
#endif

// All of the blpapi_UserHandle_* functions have been deprecated. Please use
// blpapi_Identity_* versions of these functions instead.

BLPAPI_EXPORT
void blpapi_UserHandle_release(blpapi_UserHandle_t *handle);

BLPAPI_EXPORT
int blpapi_UserHandle_addRef(blpapi_UserHandle_t *handle);

BLPAPI_EXPORT
int blpapi_UserHandle_hasEntitlements(
        const blpapi_UserHandle_t *handle,
        const blpapi_Service_t    *service,
        const blpapi_Element_t    *eidElement,
        const int                 *entitlementIds,
        size_t                     numEntitlements,
        int                       *failedEntitlements,
        int                       *failedEntitlementsCount);

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

BLPAPI_EXPORT
int blpapi_AbstractSession_sendAuthorizationRequest(
        blpapi_AbstractSession_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_AbstractSession_openService(
        blpapi_AbstractSession_t *session,
        const char               *serviceIdentifier);

BLPAPI_EXPORT
int blpapi_AbstractSession_openServiceAsync(
        blpapi_AbstractSession_t *session,
        const char               *serviceIdentifier,
        blpapi_CorrelationId_t   *correlationId);

BLPAPI_EXPORT
int blpapi_AbstractSession_generateToken(
        blpapi_AbstractSession_t *session,
        blpapi_CorrelationId_t   *correlationId,
        blpapi_EventQueue_t      *eventQueue);

BLPAPI_EXPORT
int blpapi_AbstractSession_getService(
        blpapi_AbstractSession_t  *session,
        blpapi_Service_t         **service,
        const char                *serviceIdentifier);

BLPAPI_EXPORT
blpapi_Identity_t *blpapi_AbstractSession_createIdentity(
        blpapi_AbstractSession_t *session);

#ifdef __cplusplus
}

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

namespace BloombergLP {
namespace blpapi {

typedef Identity UserHandle;

                         // =====================
                         // class AbstractSession
                         // =====================

class AbstractSession {
    // This class provides an abstract session which defines shared interface
    // between publish and consumer requests for Bloomberg
    //
    // 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. The setEventHandler() method
    // may be called to adjust the way events are handled subsequently
    // and the nextEvent() method may not be called. All incoming
    // events are delivered to the EventHandler(s) supplied on
    // construction or subsequently using setEventHandler().
    //
    // 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 and the setEventHandler() method
    // may not be called.
    //
    // 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_AbstractSession_t            *d_handle_p;

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

  protected:
    AbstractSession();
        // Create an abstract session object.

    void initAbstractSessionHandle(blpapi_AbstractSession_t *handle);
        // Initialize the handle of this abstract session.

  public:
    virtual ~AbstractSession();
        // Destructor.

    // MANIPULATORS
    virtual bool start() = 0;
        // Attempt to start this Session and blocks 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() = 0;
        // 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() = 0;
        // 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() = 0;
        // 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) = 0;
        // Return 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) = 0;
        // 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.

    bool openService(const char *serviceIdentifier);
        // Attempt to open the service identified by the specified
        // 'serviceIdentifier' and block until the service is either opened
        // successfully or has failed to be opened. Return 'true' if
        // the service is opened successfully and 'false' if the
        // service cannot be successfully opened.
        //
        // The 'serviceIdentifier' must contain a fully qualified service name.
        // That is, it must be of the form "//<namespace>/<local-name>".
        //
        // Before openService() returns a SERVICE_STATUS Event is
        // generated. If this is an asynchronous Session then this
        // Event may be processed by the registered EventHandler
        // before openService() has returned.

    CorrelationId openServiceAsync(
                        const char           *serviceIdentifier,
                        const CorrelationId&  correlationId = CorrelationId());
        // Begin the process to open the service identified by the
        // specified 'serviceIdentifier' and return immediately. The optional
        // specified 'correlationId' is used to track Events generated
        // as a result of this call. The actual correlationId which
        // will identify Events generated as a result of this call is
        // returned.
        //
        // The 'serviceIdentifier' must contain a fully qualified service name.
        // That is, it must be of the form "//<namespace>/<local-name>".
        //
        // The application must monitor events for a SERVICE_STATUS
        // Event which will be generated once the service has been
        // successfully opened or the opening has failed.

    CorrelationId sendAuthorizationRequest(
                           const Request&        authorizationRequest,
                           Identity             *identity,
                           const CorrelationId&  correlationId=CorrelationId(),
                           EventQueue           *eventQueue=0);
        // Send the specified 'authorizationRequest' and update the
        // specified 'identity' with the results. If the optionally
        // specified 'correlationId' is supplied, it is used; 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.
        //
        // The underlying user information must remain valid until the
        // Request has completed successfully or failed.
        //
        // 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 specified 'identity' will have been updated to contain
        // the users entitlement information and the CorrelationId
        // associated with the request may be re-used. If the request
        // fails at any stage a REQUEST_STATUS will be generated, the
        // specified 'identity' will not be modified and the
        // CorrelationId may be re-used.
        //
        // The 'identity' supplied must have been returned from this
        // Session's createIdentity() method. For example
        //..
        // Identity handle(session.createIdentity());
        // session.sendAuthorizationRequest(authRequest, &handle, ...)
        //..

    void cancel(const CorrelationId& correlationId);
        // If the specified 'correlationId' identifies a current
        // request then cancel that request.
        //
        // Once this call returns the specified 'correlationId' 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
        // cancel() is called is not affected even if it has the
        // specified 'correlationId'. Also any Message where a
        // reference has been retained by the application may still
        // contain the 'correlationId'. For these reasons, although
        // technically an application is free to re-use
        // 'correlationId' as soon as this method returns it is
        // preferable not to aggressively re-use correlation IDs,
        // particularly with an asynchronous Session.

    void cancel(const std::vector<CorrelationId>& correlationIds);
        // For each value in the specified 'correlationIds' which
        // identifies a current request then cancel that request. Any
        // values in the specified 'correlationIds' which do not
        // identify a current request are ignored.
        //
        // Once this call returns the specified 'correlationIds' 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
        // cancel() is called is not affected even if it has one
        // of the specified 'correlationIds'. Also any Message where a
        // reference has been retained by the application may still
        // contain one of the 'correlationIds'. For these reasons,
        // although technically an application is free to re-use any
        // of the 'correlationIds' as soon as this method returns it
        // is preferable not to aggressively re-use correlation IDs,
        // particularly with an asynchronous Session.

    void cancel(const CorrelationId *correlationIds,
                size_t               numCorrelationIds);
        // For each value specified 'correlationIds' and
        // 'numCorrelationIds' which identifies a current request then
        // cancel that request. Any specified CorrelationId's which do
        // not identify a current request are ignored.
        //
        // Once this call returns the specified 'correlationIds' 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
        // cancel() is called is not affected even if it has one
        // of the specified 'correlationIds'. Also any Message where a
        // reference has been retained by the application may still
        // contain one of the 'correlationIds'. For these reasons,
        // although technically an application is free to re-use any
        // of the 'correlationIds' as soon as this method returns it
        // is preferable not to aggressively re-use correlation IDs,
        // particularly with an asynchronous Session.

    CorrelationId generateToken(
                         const CorrelationId&  correlationId = CorrelationId(),
                         EventQueue           *eventQueue = 0);
        // Generate a token to be used for authorization.
        // If invalid authentication option is specified in session option or
        // there is failure to get authentication information based on
        // authentication option, then an InvalidArgumentException is thrown.

    // ACCESSORS
    Service getService(const char *serviceIdentifier) const;
        // Return a Service object representing the service
        // identified by the specified 'serviceIdentifier'
        //
        // The 'serviceIdentifier' must contain a fully qualified service name.
        // That is, it must be of the form "//<namespace>/<local-name>".
        //
        // If the service identified by 'serviceIdentifier' is not open or
        // registered already then a 'NotFoundException' is thrown.

    UserHandle createUserHandle();
        // Deprecated: Use createIdentity() instead. TODO: doxy
        // Return a UserHandle which is valid but has not been
        // authorized.

    Identity createIdentity();
        // Return a Identity which is valid but has not been
        // authorized.

    blpapi_AbstractSession_t *abstractSessionHandle() const;
        // Return the handle of this abstract session.
};

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

                            // ---------------------
                            // class AbstractSession
                            // ---------------------

inline
AbstractSession::AbstractSession()
: d_handle_p(0)
{
}

inline
AbstractSession::~AbstractSession()
{
}

inline
void
AbstractSession::initAbstractSessionHandle(blpapi_AbstractSession_t *handle)
{
    d_handle_p = handle;
}

inline
Service AbstractSession::getService(const char *serviceIdentifier) const
{
    blpapi_Service_t *service;
    ExceptionUtil::throwOnError(
            blpapi_AbstractSession_getService(d_handle_p,
                                              &service,
                                              serviceIdentifier));
    return service;
}

inline
CorrelationId AbstractSession::sendAuthorizationRequest(
                                    const Request&        authorizationRequest,
                                    Identity             *identity,
                                    const CorrelationId&  correlationId,
                                    EventQueue           *eventQueue)
{
    CorrelationId retCorrelationId(correlationId);

    ExceptionUtil::throwOnError(
        blpapi_AbstractSession_sendAuthorizationRequest(
            d_handle_p,
            authorizationRequest.handle(),
            identity->handle(),
            const_cast<blpapi_CorrelationId_t *>(&retCorrelationId.impl()),
            eventQueue ? eventQueue->handle() : 0, 0, 0));

    return retCorrelationId;
}

inline
void AbstractSession::cancel(const CorrelationId& correlationId)
{
    blpapi_AbstractSession_cancel(d_handle_p, &correlationId.impl(), 1, 0, 0);
}

inline
void AbstractSession::cancel(const std::vector<CorrelationId>& correlationIds)
{
    if (!correlationIds.size()) {
        return;
    }
    cancel(&correlationIds[0], correlationIds.size());
}

inline
void AbstractSession::cancel(const CorrelationId *correlationIds,
                             size_t               numCorrelationIds)
{
    blpapi_AbstractSession_cancel(
            d_handle_p,
            reinterpret_cast<const blpapi_CorrelationId_t*>(correlationIds),
            numCorrelationIds, 0, 0);
}

inline
CorrelationId AbstractSession::generateToken(
                                           const CorrelationId&  correlationId,
                                           EventQueue           *eventQueue)
{
    CorrelationId retCorrelationId(correlationId);

    ExceptionUtil::throwOnError(blpapi_AbstractSession_generateToken(
            d_handle_p,
            const_cast<blpapi_CorrelationId_t *>(&retCorrelationId.impl()),
            eventQueue ? eventQueue->handle() : 0));

    return retCorrelationId;
}

inline
bool AbstractSession::openService(const char *serviceIdentifier)
{
    return blpapi_AbstractSession_openService(d_handle_p, serviceIdentifier)
           ? false
           : true;
}

inline
CorrelationId AbstractSession::openServiceAsync(
                                       const char           *serviceIdentifier,
                                       const CorrelationId&  correlationId)
{
    blpapi_CorrelationId_t retv = correlationId.impl();
    ExceptionUtil::throwOnError(
        blpapi_AbstractSession_openServiceAsync(
            d_handle_p,
            serviceIdentifier,
            &retv));

    return retv;
}

inline
UserHandle AbstractSession::createUserHandle()
{
    return blpapi_AbstractSession_createIdentity(d_handle_p);
}

inline
Identity AbstractSession::createIdentity()
{
    return blpapi_AbstractSession_createIdentity(d_handle_p);
}

inline
blpapi_AbstractSession_t *AbstractSession::abstractSessionHandle() const
{
    return d_handle_p;
}

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


#endif // #ifdef __cplusplus
#endif // #ifndef INCLUDED_BLPAPI_ABSTRACTSESSION