// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef STATE_MODEL_H
#define STATE_MODEL_H

/// @file state_model.h This file defines the class StateModel.

#include <exceptions/exceptions.h>
#include <util/labeled_value.h>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <map>
#include <string>

namespace isc {
namespace util {

/// @brief Thrown if the state machine encounters a general error.
class StateModelError : public isc::Exception {
public:
    StateModelError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};

/// @brief Define an Event.
typedef LabeledValue Event;

/// @brief Define Event pointer.
typedef LabeledValuePtr EventPtr;

/// @brief Defines a pointer to an instance method for handling a state.
typedef boost::function<void()> StateHandler;

/// @brief State machine pausing modes.
///
/// Supported modes are:
/// - always pause in the given state,
/// - never pause in the given state,
/// - pause upon first transition to the given state.
enum StatePausing {
    STATE_PAUSE_ALWAYS,
    STATE_PAUSE_NEVER,
    STATE_PAUSE_ONCE
};

/// @brief Defines a State within the State Model.
///
/// This class provides the means to define a state within a set or dictionary
/// of states, and assign the state an handler method to execute the state's
/// actions.  It derives from LabeledValue which allows a set of states to be
/// keyed by integer constants.
///
/// Because a state model can be paused in selected states, this class also
/// provides the means for specifying a pausing mode and for checking whether
/// the state model should be paused when entering this state.
class State : public LabeledValue {
public:
    /// @brief Constructor
    ///
    /// @param value is the numeric value of the state
    /// @param label is the text label to assign to the state
    /// @param handler is the bound instance method which handles the state's
    /// action.
    /// @param state_pausing pausing mode selected for the given state. The
    /// default value is @c STATE_PAUSE_NEVER.
    ///
    /// A typical invocation might look this:
    ///
    /// @code
    ///     State(SOME_INT_VAL, "SOME_INT_VAL",
    ///            boost::bind(&StateModelDerivation::someHandler, this));
    /// @endcode
    ///
    /// @throw StateModelError if label is null or blank.
    State(const int value, const std::string& label, StateHandler handler,
          const StatePausing& state_pausing = STATE_PAUSE_NEVER);

    /// @brief Destructor
    virtual ~State();

    /// @brief Invokes the State's handler.
    void run();

    /// @brief Indicates if the state model should pause upon entering
    /// this state.
    ///
    /// It modifies the @c was_paused_ flag if the state model should
    /// pause. That way, it keeps track of visits in this particular state,
    /// making it possible to pause only upon the first transition to the
    /// state when @c STATE_PAUSE_ONCE mode is used.
    bool shouldPause();

private:
    /// @brief Bound instance method pointer to the state's handler method.
    StateHandler handler_;

    /// @brief Specifies selected pausing mode for a state.
    StatePausing pausing_;

    /// @brief Indicates if the state machine was already paused in this
    /// state.
    bool was_paused_;
};

/// @brief Defines a shared pointer to a State.
typedef boost::shared_ptr<State> StatePtr;

/// @brief Implements a unique set or dictionary of states.
///
/// This class provides the means to construct and access a unique set of
/// states.  This provide the ability to validate state values, look up their
/// text labels, and their handlers.
class StateSet : public LabeledValueSet {
public:
    /// @brief Constructor
    StateSet();

    /// @brief Destructor
    virtual ~StateSet();

    /// @brief Adds a state definition to the set of states.
    ///
    /// @param value is the numeric value of the state
    /// @param label is the text label to assign to the state
    /// @param handler is the bound instance method which handles the state's
    /// @param state_pausing state pausing mode for the given state.
    ///
    /// @throw StateModelError if the value is already defined in the set, or
    /// if the label is null or blank.
    void add(const int value, const std::string& label, StateHandler handler,
             const StatePausing& state_pausing);

    /// @brief Fetches a state for the given value.
    ///
    /// @param value the numeric value of the state desired
    ///
    /// @return A constant pointer the State found.
    /// Note, this relies on dynamic cast and cannot return a pointer reference.
    ///
    /// @throw StateModelError if the value is undefined.
    const StatePtr getState(int value);
};

/// @brief Implements a finite state machine.
///
/// StateModel is an abstract class that provides the structure and mechanics
/// of a basic finite state machine.
///
/// The state model implementation used is a very basic approach. The model
/// uses numeric constants to identify events and states, and maintains
/// dictionaries of defined events and states.  Event and state definitions
/// include a text label for logging purposes.  Additionally, each state
/// definition includes a state handler. State handlers are methods which
/// implement the actions that need to occur when the model is "in" a given
/// state.  The implementation provides methods to add entries to and verify
/// the contents of both dictionaries.
///
/// During model execution, the following context is tracked:
///
/// * current state - The current state of the model
/// * previous state -  The state the model was in prior to the current state
/// * next event - The next event to be consumed
/// * last event - The event most recently consumed
///
/// When invoked, a state handler determines what it should do based upon the
/// next event including what the next state and event should be. In other
/// words the state transition knowledge is distributed among the state
/// handlers rather than encapsulated in some form of state transition table.
///
/// Events "posted" from within the state handlers are "internally" triggered
/// events.  Events "posted" from outside the state model, such as through
/// the invocation of a callback are "externally" triggered.
///
/// StateModel defines two states:
///
/// * NEW_ST - State that a model is in following instantiation. It remains in
/// this state until model execution has begun.
/// * END_ST - State that a model is in once it has reached its conclusion.
///
/// and the following events:
///
/// * START_EVT - Event used to start model execution.
/// * NOP_EVT - Event used to signify that the model stop and wait for an
/// external event, such as the completion of an asynchronous IO operation.
/// * END_EVT - Event used to trigger a normal conclusion of the model. This
/// means only that the model was traversed from start to finish, without any
/// model violations (i.e. invalid state, event, or transition) or uncaught
/// exceptions.
/// * FAIL_EVT - Event to trigger an abnormal conclusion of the model. This
/// event is posted internally when model execution fails due to a model
/// violation or uncaught exception.  It signifies that the model has reached
/// an inoperable condition.
///
/// Derivations add their own states and events appropriate for their state
/// model.  Note that NEW_ST and END_ST do not support handlers.  No work can
/// be done (events consumed) prior to starting the model nor can work be done
/// once the model has ended.
///
/// Model execution consists of iteratively invoking the state handler
/// indicated by the current state which should consume the next event. As the
/// handlers post events and/or change the state, the model is traversed. The
/// loop stops whenever the model cannot continue without an externally
/// triggered event or when it has reached its final state.  In the case of
/// the former, the loop may be re-entered upon arrival of the external event.
///
/// This loop is implemented in the runModel method.  This method accepts an
/// event as argument which it "posts" as the next event.  It then retrieves the
/// handler for the current state from the handler map and invokes it. runModel
/// repeats this process until either a NOP_EVT posts or the state changes
/// to END_ST.  In other words each invocation of runModel causes the model to
/// be traversed from the current state until it must wait or ends.
///
/// Re-entering the "loop" upon the occurrence of an external event is done by
/// invoking runModel with the appropriate event.  As before, runModel will
/// loop until either the NOP_EVT occurs or until the model reaches its end.
///
/// A StateModel (derivation) is in the NEW_ST when constructed and remains
/// there until it has been "started".  Starting the model is done by invoking
/// the startModel method which accepts a state as a parameter.  This parameter
/// specifies the "start" state and it becomes the current state.

/// The first task undertaken by startModel is to initialize and verify the
/// the event and state dictionaries.  The following virtual methods are
/// provided for this:
///
/// * defineEvents - define events
/// * verifyEvents - verifies that the expected events are defined
/// * defineStates - defines states
/// * verifyStates - verifies that the expected states are defined
///
/// The concept behind the verify methods is to provide an initial sanity
/// check of the dictionaries.  This should help avoid using undefined event
/// or state values accidentally.
///
/// These methods are intended to be implemented by each "layer" in a StateModel
/// derivation hierarchy.  This allows each layer to define additional events
/// and states.
///
/// Once the dictionaries have been properly initialized, the startModel method
/// invokes runModel with an event of START_EVT.  From this point forward and
/// until the model reaches the END_ST or fails, it is considered to be
/// "running".  If the model encounters a NOP_EVT then it is "running" and
/// "waiting".   If the model reaches END_ST with an END_EVT it is considered
/// "done".  If the  model fails (END_ST with a FAILED_EVT) it is considered
/// "done" and "failed".  There are several boolean status methods which may
/// be used to check these conditions.
///
/// To progress from one state to the another, state handlers invoke use
/// the method, transition.  This method accepts a state and an event as
/// parameters.  These values become the current state and the next event
/// respectively.  This has the effect of entering the given state having posted
/// the given event.  The postEvent method may be used to post a new event
/// to the current state.
///
/// Bringing the model to a normal end is done by invoking the endModel method
/// which transitions the model to END_ST with END_EVT.  Bringing the model to
/// an abnormal end is done via the abortModel method, which transitions the
/// model to END_ST with FAILED_EVT.
///
/// The model can be paused in the selected states. The states in which the
/// state model should pause (always or only once) are determined within the
/// @c StateModel::defineStates method. The state handlers can check whether
/// the state machine is paused or not by calling @c StateModel::isModelPaused
/// and act accordingy. Typically, the state handler would simply post the
/// @c NOP_EVT when it finds that the state model is paused. The model
/// remains paused until @c StateModel::unpauseModel is called.
class StateModel {
public:

    //@{ States common to all models.
    /// @brief State that a state model is in immediately after construction.
    static const int NEW_ST = 0;

    /// @brief Final state, all the state model has reached its conclusion.
    static const int END_ST = 1;

    /// @brief Value at which custom states in a derived class should begin.
    static const int SM_DERIVED_STATE_MIN = 11;
    //@}

    //@{ Events common to all state models.
    /// @brief Signifies that no event has occurred.
    /// This is event used to interrupt the event loop to allow waiting for
    /// an IO event or when there is no more work to be done.
    static const int NOP_EVT = 0;

    /// @brief Event issued to start the model execution.
    static const int START_EVT = 1;

    /// @brief Event issued to end the model execution.
    static const int END_EVT = 2;

    /// @brief Event issued to abort the model execution.
    static const int FAIL_EVT = 3;

    /// @brief Value at which custom events in a derived class should begin.
    static const int SM_DERIVED_EVENT_MIN = 11;
    //@}

    /// @brief Constructor
    StateModel();

    /// @brief Destructor
    virtual ~StateModel();

    /// @brief Begins execution of the model.
    ///
    /// This method invokes initDictionaries method to initialize the event
    /// and state dictionaries and then starts the model execution setting
    /// the current state to the given start state, and the event to START_EVT.
    ///
    /// @param start_state is the state in which to begin execution.
    ///
    /// @throw StateModelError or others indirectly, as this method calls
    /// dictionary define and verify methods.
    void startModel(const int start_state);

    /// @brief Processes events through the state model
    ///
    /// This method implements the state model "execution loop".  It uses
    /// the given event as the next event to process and begins invoking
    /// the state handler for the current state.   As described above, the
    /// invoked state handler consumes the next event and then determines the
    /// next event and the current state as required to implement the business
    /// logic. The method continues to loop until the next event posted is
    /// NOP_EVT or the model ends.
    ///
    /// Any exception thrown during the loop is caught, logged, and the
    /// model is aborted with a FAIL_EVT.  The derivation's state
    /// model is expected to account for any possible errors so any that
    /// escape are treated as unrecoverable.
    ///
    /// @note This method is made virtual for the unit tests which require
    /// customizations allowing for more control over the state model
    /// execution.
    ///
    /// @param event is the next event to process
    ///
    /// This method is guaranteed not to throw.
    virtual void runModel(unsigned int event);

    /// @brief Conducts a normal transition to the end of the model.
    ///
    /// This method posts an END_EVT and sets the current state to END_ST.
    /// It should be called by any state handler in the model from which
    /// an exit leads to the model end.  In other words, if the transition
    /// out of a particular state is to the end of the model, that state's
    /// handler should call endModel.
    void endModel();

    /// @brief Unpauses state model.
    void unpauseModel();

    /// @brief An empty state handler.
    ///
    /// This method is primarily used to permit special states, NEW_ST and
    /// END_ST to be included in the state dictionary.  Currently it is an
    /// empty method.
    void nopStateHandler();

protected:
    /// @brief Initializes the event and state dictionaries.
    ///
    /// This method invokes the define and verify methods for both events and
    /// states to initialize their respective dictionaries.
    ///
    /// @throw StateModelError or others indirectly, as this method calls
    /// dictionary define and verify methods.
    void initDictionaries();

    /// @brief Populates the set of events.
    ///
    /// This method is used to construct the set of valid events. Each class
    /// within a StateModel derivation hierarchy uses this method to add any
    /// events it defines to the set.  Each derivation's implementation must
    /// also call it's superclass's implementation.  This allows each class
    /// within the hierarchy to make contributions to the set of defined
    /// events. Implementations use the method, defineEvent(), to add event
    /// definitions.  An example of the derivation's implementation follows:
    ///
    /// @code
    /// void StateModelDerivation::defineEvents() {
    ///     // Call the superclass implementation.
    ///     StateModelDerivation::defineEvents();
    ///
    ///     // Add the events defined by the derivation.
    ///     defineEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
    ///     defineEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
    ///     :
    /// }
    /// @endcode
    virtual void defineEvents();

    /// @brief Adds an event value and associated label to the set of events.
    ///
    /// @param value is the numeric value of the event
    /// @param label is the text label of the event used in log messages and
    /// exceptions.
    ///
    /// @throw StateModelError if the model has already been started, if
    /// the value is already defined, or if the label is empty.
    void defineEvent(unsigned int value, const std::string& label);

    /// @brief Fetches the event referred to by value.
    ///
    /// @param value is the numeric value of the event desired.
    ///
    /// @return returns a constant pointer reference to the event if found
    ///
    /// @throw StateModelError if the event is not defined.
    const EventPtr& getEvent(unsigned int value);

    /// @brief Validates the contents of the set of events.
    ///
    /// This method is invoked immediately after the defineEvents method and
    /// is used to verify that all the required events are defined.  If the
    /// event set is determined to be invalid this method should throw a
    /// StateModelError.  As with the defineEvents method, each class within
    /// a StateModel derivation hierarchy must supply an implementation
    /// which calls it's superclass's implementation as well as verifying any
    /// events added by the derivation.  Validating an event is accomplished
    /// by simply attempting to fetch an event by its value from the event set.
    /// An example of the derivation's implementation follows:
    ///
    /// @code
    /// void StateModelDerivation::verifyEvents() {
    ///     // Call the superclass implementation.
    ///     StateModelDerivation::verifyEvents();
    ///
    ///     // Verify the events defined by the derivation.
    ///     getEvent(SOME_CUSTOM_EVT_1, "CUSTOM_EVT_1");
    ///     getEvent(SOME_CUSTOM_EVT_2, "CUSTOM_EVT_2");
    ///     :
    /// }
    /// @endcode
    virtual void verifyEvents();

    /// @brief Populates the set of states.
    ///
    /// This method is used to construct the set of valid states. Each class
    /// within a StateModel derivation hierarchy uses this method to add any
    /// states it defines to the set.  Each derivation's implementation must
    /// also call it's superclass's implementation.  This allows each class
    /// within the hierarchy to make contributions to the set of defined
    /// states. Implementations use the method, defineState(), to add state
    /// definitions.  An example of the derivation's implementation follows:
    ///
    /// @code
    /// void StateModelDerivation::defineStates() {
    ///     // Call the superclass implementation.
    ///     StateModelDerivation::defineStates();
    ///
    ///     // Add the states defined by the derivation.
    ///     defineState(SOME_ST, "SOME_ST",
    ///                 boost::bind(&StateModelDerivation::someHandler, this));
    ///     :
    /// }
    /// @endcode
    virtual void defineStates();

    /// @brief Adds an state value and associated label to the set of states.
    ///
    /// @param value is the numeric value of the state
    /// @param label is the text label of the state used in log messages and
    /// exceptions.
    /// @param handler is the bound instance method which implements the state's
    /// actions.
    /// @param state_pausing pausing mode selected for the given state. The
    /// default value is @c STATE_PAUSE_NEVER.
    ///
    /// @throw StateModelError if the model has already been started, if
    /// the value is already defined, or if the label is empty.
    void defineState(unsigned int value, const std::string& label,
                     StateHandler handler,
                     const StatePausing& state_pausing = STATE_PAUSE_NEVER);

    /// @brief Fetches the state referred to by value.
    ///
    /// @param value is the numeric value of the state desired.
    ///
    /// @return returns a constant pointer to the state if found
    ///
    /// @throw StateModelError if the state is not defined.
    const StatePtr getState(unsigned int value);

    /// @brief Validates the contents of the set of states.
    ///
    /// This method is invoked immediately after the defineStates method and
    /// is used to verify that all the required states are defined.  If the
    /// state set is determined to be invalid this method should throw a
    /// StateModelError.  As with the defineStates method, each class within
    /// a StateModel derivation hierarchy must supply an implementation
    /// which calls it's superclass's implementation as well as verifying any
    /// states added by the derivation.  Validating an state is accomplished
    /// by simply attempting to fetch the state by its value from the state set.
    /// An example of the derivation's implementation follows:
    ///
    /// @code
    /// void StateModelDerivation::verifyStates() {
    ///     // Call the superclass implementation.
    ///     StateModelDerivation::verifyStates();
    ///
    ///     // Verify the states defined by the derivation.
    ///     getState(SOME_CUSTOM_EVT_2);
    ///     :
    /// }
    /// @endcode
    virtual void verifyStates();

    /// @brief Handler for fatal model execution errors.
    ///
    /// This method is called when an unexpected error renders during
    /// model execution, such as a state handler throwing an exception.
    /// It provides derivations an opportunity to act accordingly by setting
    /// the appropriate status or taking other remedial action.   This allows
    /// the model execution loop to remain exception safe.  This default
    /// implementation does nothing.
    ///
    /// @param explanation text detailing the error and state machine context
    virtual void onModelFailure(const std::string& explanation);

    /// @brief Sets up the model to transition into given state with a given
    /// event.
    ///
    /// This updates the model's notion of the current state and the next
    /// event to process.  State handlers use this method to move from one state
    /// to the next.
    ///
    /// @param state the new value to assign to the current state.
    /// @param event the new value to assign to the next event.
    ///
    /// @throw StateModelError if the state is invalid.
    void transition(unsigned int state, unsigned int event);

    /// @brief Aborts model execution.
    ///
    /// This method posts a FAILED_EVT and sets the current state to END_ST.
    /// It is called internally when a model violation occurs. Violations are
    /// any sort of inconsistency such as attempting to reference an invalid
    /// state, or if the next event is not valid for the current state, or a
    /// state handler throws an uncaught exception.
    ///
    /// @param explanation is text detailing the reason for aborting.
    void abortModel(const std::string& explanation);

    /// @brief Sets the current state to the given state value.
    ///
    /// This updates the model's notion of the current state and is the
    /// state whose handler will be executed on the next iteration of the run
    /// loop.  This is intended primarily for internal use and testing. It is
    /// unlikely that transitioning to a new state without a new event is of
    /// much use.
    ///
    /// @param state the new value to assign to the current state.
    ///
    /// @throw StateModelError if the state is invalid.
    void setState(unsigned int state);

    /// @brief Sets the next event to the given event value.
    ///
    /// This updates the model's notion of the next event and is the
    /// event that will be passed into the current state's handler on the next
    /// iteration of the run loop.
    ///
    /// @param event the numeric event value to post as the next event.
    ///
    /// @throw StateModelError if the event is undefined
    void postNextEvent(unsigned int event);

    /// @brief Checks if on entry flag is true.
    ///
    /// This method acts as a one-shot test of whether or not  the model is
    /// transitioning into a new state.  It returns true if the on-entry flag
    /// is true upon entering this method and will set the flag false prior
    /// to exit.  It may be used within state handlers to perform steps that
    /// should only occur upon entry into the state.
    ///
    /// @return true if the on entry flag is true, false otherwise.
    bool doOnEntry();

    /// @brief Checks if on exit flag is true.
    ///
    /// This method acts as a one-shot test of whether or not the model is
    /// transitioning out of the current state.  It returns true if the
    /// on-exit flag is true upon entering this method and will set the flag
    /// false prior to exiting.  It may be used within state handlers to perform
    /// steps that should only occur upon exiting out of the current state.
    ///
    /// @return true if the on entry flag is true, false otherwise.
    bool doOnExit();

public:
    /// @brief Fetches the model's current state.
    ///
    /// This returns the model's notion of the current state. It is the
    /// state whose handler will be executed on the next iteration of the run
    /// loop.
    ///
    /// @return An unsigned int representing the current state.
    unsigned int getCurrState() const;

    /// @brief Fetches the model's previous state.
    ///
    /// @return An unsigned int representing the previous state.
    unsigned int getPrevState() const;

    /// @brief Fetches the model's last event.
    ///
    /// @return An unsigned int representing the last event.
    unsigned int getLastEvent() const;

    /// @brief Fetches the model's next event.
    ///
    /// This returns the model's notion of the next event. It is the
    /// event that will be passed into the current state's handler on the next
    /// iteration of the run loop.
    ///
    /// @return An unsigned int representing the next event.
    unsigned int getNextEvent() const;

    /// @brief Returns whether or not the model is new.
    ///
    /// @return Boolean true if the model has not been started.
    bool isModelNew() const;

    /// @brief Returns whether or not the model is running.
    ///
    /// @return Boolean true if the model has been started but has not yet
    /// ended.
    bool isModelRunning() const;

    /// @brief Returns whether or not the model is waiting.
    ///
    /// @return Boolean true if the model is running but is waiting for an
    /// external event for resumption.
    bool isModelWaiting() const;

    /// @brief Returns whether or not the model has finished execution.
    ///
    /// @return Boolean true if the model has reached the END_ST.
    bool isModelDone() const;

    /// @brief Returns whether or not the model is paused.
    ///
    /// @return Boolean true if the model is paused, false otherwise.
    bool isModelPaused() const;

    /// @brief Returns whether or not the model failed.
    ///
    /// @return Boolean true if the model has reached the END_ST and the last
    /// event indicates a model violation, FAILED_EVT.
    bool didModelFail() const;

    /// @brief Fetches the label associated with an event value.
    ///
    /// @param event is the numeric event value for which the label is desired.
    ///
    /// @return Returns a string containing the event label or
    /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
    std::string getEventLabel(const int event) const;

    /// @brief Fetches the label associated with an state value.
    ///
    /// @param state is the numeric state value for which the label is desired.
    ///
    /// @return Returns a const char* containing the state label or
    /// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
    std::string getStateLabel(const int state) const;

    /// @brief Convenience method which returns a string rendition of the
    /// current state and next event.
    ///
    /// The string will be of the form:
    ///
    ///   current state: [ {state} {label} ] next event: [ {event} {label} ]
    ///
    /// @return Returns a std::string of the format described above.
    std::string getContextStr() const;

    /// @brief Convenience method which returns a string rendition of the
    /// previous state and last event.
    ///
    /// The string will be of the form:
    ///
    ///   previous state: [ {state} {label} ] last event: [ {event} {label} ]
    ///
    /// @return Returns a std::string of the format described above.
    std::string getPrevContextStr() const;

private:
    /// @brief The dictionary of valid events.
    LabeledValueSet events_;

    /// @brief The dictionary of valid states.
    StateSet states_;

    /// @brief Indicates if the event and state dictionaries have been initted.
    bool dictionaries_initted_;

    /// @brief The current state within the model's state model.
    unsigned int curr_state_;

    /// @brief The previous state within the model's state model.
    unsigned int prev_state_;

    /// @brief The event last processed by the model.
    unsigned int last_event_;

    /// @brief The event the model should process next.
    unsigned int next_event_;

    /// @brief Indicates if state entry logic should be executed.
    bool on_entry_flag_;

    /// @brief Indicates if state exit logic should be executed.
    bool on_exit_flag_;

    /// @brief Indicates if the state model is paused.
    bool paused_;
};

/// @brief Defines a pointer to a StateModel.
typedef boost::shared_ptr<StateModel> StateModelPtr;

} // namespace isc::util
} // namespace isc
#endif
