// balb_controlmanager.h                                              -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BALB_CONTROLMANAGER
#define INCLUDED_BALB_CONTROLMANAGER

//@PURPOSE: Provide a mechanism for mapping control messages to callbacks.
//
//@CLASSES:
//   balb::ControlManager: mechanism that maps control messages
//
//@DESCRIPTION: The 'balb::ControlManager' mechanism provided by this component
// maps control messages to callback functions on the basis of message
// prefixes.
//
///Callback Function Requirements
///------------------------------
// Functions registered as callbacks for messages must be invokable as
// 'void(*)(const bsl::string&, bsl::istream&)'.  (This signature is
// 'balb::ControlManager::ControlHandler').  When the function is invoked, the
// first argument is the message prefix, and the second is a stream on the
// remainder of the message.
//
///Thread Safety
///-------------
// This component is thread-safe and thread-enabled: it is safe to access and
// manipulate multiple distinct instances from different threads, and it is
// safe to access and manipulate a single shared instance from different
// threads.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Creating an ECHO Message Handler
///- - - - - - - - - - - - - - - - - - - - - -
// First define a trivial callback to be invoked when an "ECHO" message is
// received:
//..
//  void onEcho(const bsl::string& prefix, bsl::istream& stream)
//  {
//     bsl::string word;
//     bsl::cout << "onEcho: \"" << prefix;
//     while (stream.good()) {
//        stream >> word;
//        bsl::cout << ' ' << word;
//     }
//     bsl::cout << '\"' << bsl::endl;
//  }
//..
// Now create a 'balb::ControlManager' object and register a handler for
// "ECHO".  Also register a handler for HELP to observe the auto-generated
// documentation for ECHO:
//..
//  balb::ControlManager manager;
//  manager.registerHandler("ECHO", "<text>",
//                          "Print specified text to the standard output",
//                          &onEcho);
//  manager.registerHandler("HELP", "",
//                          "Print documentation",
//                          bdlf::BindUtil::bind(
//                                  &balb::ControlManager::printUsageHelper,
//                                  &manager, &bsl::cout, bsl::string(
//               "The following commands are accepted by the test driver:")));
//
//  manager.dispatchMessage("ECHO repeat this text");
//  manager.dispatchMessage("echo matching is case-insensitive");
//  manager.dispatchMessage("HELP");
//..

#include <balscm_version.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bslmt_rwmutex.h>

#include <bsl_functional.h>
#include <bsl_iosfwd.h>
#include <bsl_map.h>
#include <bsl_string.h>
#include <bsl_vector.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslalg_typetraits.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace balb {

                            // ====================
                            // class ControlManager
                            // ====================

class ControlManager {
    // Dispatch control messages to callbacks by name.

  public:
    // TYPES
    typedef bsl::function<void(const bsl::string& prefix,
                               bsl::istream&      stream)> ControlHandler;
        // Defines a type alias for the function called to handle control
        // messages.  The 'prefix' argument is the first space-delimited word
        // read from the message, and the 'stream' argument is the
        // 'bsl::istream' containing the remainder of the message.

    // PRIVATE TYPES
                         // ==========================
                         // class ControlManager_Entry
                         // ==========================

// IMPLEMENTATION NOTE: The Sun Studio 12.3 compiler does not support 'map's
// holding types that are incomplete at the point of declaration of a data
// member.  Other compilers allow us to complete 'CalendarChache_Entry' at a
// later point in the code, but before any operation (such as 'insert') that
// would require the type to be complete.  If we did not have to support this
// compiler, this whole class could be defined in the .cpp file; as it stands,
// it *must* be defined before class 'CalendarCache'.

class ControlManager_Entry {
    // This component-private class represents a function with documentation.

    // INSTANCE DATA
    ControlManager::ControlHandler d_callback;    // processing callback
    bsl::string                    d_arguments;   // argument description
    bsl::string                    d_description; // function description

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(ControlManager_Entry,
                                   bslma::UsesBslmaAllocator);

    // CREATORS
    explicit
    ControlManager_Entry(bslma::Allocator *basicAllocator = 0);
        // Create a 'ControlManager_Entry' object.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.

    ControlManager_Entry(
                    const ControlManager::ControlHandler&  callback,
                    const bsl::string_view&                arguments,
                    const bsl::string_view&                description,
                    bslma::Allocator                      *basicAllocator = 0);
        // Create an ControlManager_Entry object with the specified initial
        // values.

    ControlManager_Entry(const ControlManager_Entry&  original,
                         bslma::Allocator            *basicAllocator = 0);
        // Create an ControlManager_Entry object having the value of the
        // specified 'original' object.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.

    ~ControlManager_Entry();
        // Destroy this object.

    // MANIPULATORS
    ControlManager_Entry& operator=(const ControlManager_Entry& rhs);
        // Assign to this object the value of the specified 'rhs' object.

    void setCallback(const ControlManager::ControlHandler& callback);
        // Set the specified 'callback' as the value of the 'callback' member
        // of this object.

    bsl::string& arguments();
        // Return a modifiable reference to the 'arguments' member of this
        // object.

    bsl::string& description();
        // Return a modifiable reference to the 'description' member of this
        // object.

    // ACCESSORS
    const ControlManager::ControlHandler& callback() const;
        // Return a non-modifiable reference to the 'callback' member of this
        // object.

    const bsl::string& arguments() const;
        // Return a non-modifiable reference to the 'arguments' member of this
        // object.

    const bsl::string& description() const;
        // Return a non-modifiable reference to the 'arguments' member of this
        // object.
};

    struct CaselessLessThan {
        // TYPES
        typedef void is_transparent;

        // ACCESSOR
        bool operator()(const bsl::string_view& lhs,
                        const bsl::string_view& rhs) const;
            // Return 'true' if the specified 'lhs' is less than the specified
            // 'rhs' in a case-insensitive comparison, and 'false' otherwise.
    };

    typedef bsl::map<bsl::string,
                     ControlManager_Entry,
                     CaselessLessThan> Registry;
        // Defines a type alias for the ordered associative data structure
        // that maps a message prefix to a 'StringComparator' functor.

    // INSTANCE DATA
    bslma::Allocator       *d_allocator_p;    // memory allocator (held)
    Registry                d_registry;       // registry
    mutable bslmt::RWMutex  d_registryMutex;  // registry mutex

    // NOT IMPLEMENTED
    ControlManager(const ControlManager&);             // = deleted
    ControlManager& operator=(const ControlManager&);  // = deletd

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(ControlManager, bslma::UsesBslmaAllocator);

    // CREATORS
    explicit
    ControlManager(bslma::Allocator *basicAllocator = 0);
        // Create a control manager object.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.

    ~ControlManager();
        // Destroy this object.

    // MANIPULATORS
    int registerHandler(const bsl::string_view&    prefix,
                        const bsl::string_view&    arguments,
                        const bsl::string_view&    description,
                        const ControlHandler&      handler);
        // Register the specified 'handler' to be invoked whenever a control
        // message having the specified case-insensitive 'prefix' is received
        // for this control manager.  Also register the specified 'arguments'
        // string to describe the arguments accepted by the message, and the
        // specified 'description' to describe its operation; these are
        // printed by 'printUsage'.  Return a positive value if an existing
        // callback was replaced, return 0 if no replacement occurred, and
        // return a negative value otherwise.

    int deregisterHandler(const bsl::string_view& prefix);
        // Deregister the callback function previously registered to handle the
        // specified 'prefix'.  Return 0 on success or a non-zero value
        // otherwise.

    // ACCESSOR
    int dispatchMessage(const bsl::string_view& message) const;
        // Parse the specified complete 'message' and dispatch it.  Return
        // 0 on success, and a non-zero value otherwise; in particular return
        // non-zero if no registered callback could be found for the
        // case-insensitive prefix in 'message'.

    int dispatchMessage(const bsl::string& prefix, bsl::istream& stream) const;
        // Dispatch the message contained in the specified 'stream' to the
        // callback associated with the specified 'prefix'.  Return 0 on
        // success, and a non-zero value otherwise; in particular return
        // non-zero if no registered callback could be found for the
        // case-insensitive 'prefix'.

    void printUsage(bsl::ostream&           stream,
                    const bsl::string_view& preamble) const;
        // Print to the specified 'stream' the specified 'preamble' text,
        // followed by the registered commands and documentation for this
        // control manager.  Note that a newline is appended to 'preamble' in
        // the output.

    void printUsageHelper(bsl::ostream            *stream,
                          const bsl::string_view&  preamble) const;
        // Invoke 'printUsage' passing the specified '*stream' and 'preamble'.
        // Suitable for binding using the bdlf::BindUtil package.

};

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

                        // --------------------------
                        // class ControlManager::ControlManager_Entry
                        // --------------------------

// MANIPULATORS
inline
void ControlManager::ControlManager_Entry::setCallback(
                                const ControlManager::ControlHandler& callback)
{
    d_callback = callback;
}

inline
bsl::string& ControlManager::ControlManager_Entry::arguments()
{
    return d_arguments;
}

inline
bsl::string& ControlManager::ControlManager_Entry::description()
{
    return d_description;
}

// ACCESSORS
inline
const ControlManager::ControlHandler&
ControlManager::ControlManager_Entry::callback() const
{
    return d_callback;
}

inline
const bsl::string& ControlManager::ControlManager_Entry::arguments() const
{
    return d_arguments;
}

inline
const bsl::string& ControlManager::ControlManager_Entry::description() const
{
    return d_description;
}

                            // --------------------
                            // class ControlManager
                            // --------------------

// ACCESSORS
inline
void ControlManager::printUsageHelper(bsl::ostream            *stream,
                                      const bsl::string_view&  preamble) const
{
    printUsage(*stream, preamble);
}

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

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------