#ifndef __DWC_NOTIFIER_H__
#define __DWC_NOTIFIER_H__

#ifdef __cplusplus
extern "C" {
#endif

#include "dwc_os.h"

/** @file
 *
 * A simple implementation of the Observer pattern.  Any "module" can
 * register as an observer or notifier.  The notion of "module" is abstract and
 * can mean anything used to identify either an observer or notifier.  Usually
 * it will be a pointer to a data structure which contains some state, ie an
 * object.
 *
 * Before any notifiers can be added, the global notification manager must be
 * brought up with dwc_alloc_notification_manager().
 * dwc_free_notification_manager() will bring it down and free all resources.
 * These would typically be called upon module load and unload.  The
 * notification manager is a single global instance that handles all registered
 * observable modules and observers so this should be done only once.
 *
 * A module can be observable by using Notifications to publicize some general
 * information about it's state or operation.  It does not care who listens, or
 * even if anyone listens, or what they do with the information.  The observable
 * modules do not need to know any information about it's observers or their
 * interface, or their state or data.
 *
 * Any module can register to emit Notifications.  It should publish a list of
 * notifications that it can emit and their behavior, such as when they will get
 * triggered, and what information will be provided to the observer.  Then it
 * should register itself as an observable module. See dwc_register_notifier().
 *
 * Any module can observe any observable, registered module, provided it has a
 * handle to the other module and knows what notifications to observe.  See
 * dwc_add_observer().
 *
 * A function of type dwc_notifier_callback_t is called whenever a notification
 * is triggered with one or more observers observing it.  This function is
 * called in it's own process so it may sleep or block if needed.  It is
 * guaranteed to be called sometime after the notification has occurred and will
 * be called once per each time the notification is triggered.  It will NOT be
 * called in the same process context used to trigger the notification.
 *
 * @section Limitations
 *
 * Keep in mind that Notifications that can be triggered in rapid succession may
 * schedule too many processes too handle.  Be aware of this limitation when
 * designing to use notifications, and only add notifications for appropriate
 * observable information.
 *
 * Also Notification callbacks are not synchronous.  If you need to synchronize
 * the behavior between module/observer you must use other means.  And perhaps
 * that will mean Notifications are not the proper solution.
 */

struct dwc_notifier;
typedef struct dwc_notifier dwc_notifier_t;

/** The callback function must be of this type.
 *
 * @param object This is the object that is being observed.
 * @param notification This is the notification that was triggered.
 * @param observer This is the observer
 * @param notification_data This is notification-specific data that the notifier
 * has included in this notification.  The value of this should be published in
 * the documentation of the observable module with the notifications.
 * @param user_data This is any custom data that the observer provided when
 * adding itself as an observer to the notification. */
typedef void (*dwc_notifier_callback_t)(void *object, char *notification, void *observer,
					void *notification_data, void *user_data);

/** Brings up the notification manager. */
extern int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx);
/** Brings down the notification manager. */
extern void dwc_free_notification_manager(void);

/** This function registers an observable module.  A dwc_notifier_t object is
 * returned to the observable module.  This is an opaque object that is used by
 * the observable module to trigger notifications.  This object should only be
 * accessible to functions that are authorized to trigger notifications for this
 * module.  Observers do not need this object. */
extern dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object);

/** This function unregisters an observable module.  All observers have to be
 * removed prior to unregistration. */
extern void dwc_unregister_notifier(dwc_notifier_t *notifier);

/** Add a module as an observer to the observable module.  The observable module
 * needs to have previously registered with the notification manager.
 *
 * @param observer The observer module
 * @param object The module to observe
 * @param notification The notification to observe
 * @param callback The callback function to call
 * @param user_data Any additional user data to pass into the callback function */
extern int dwc_add_observer(void *observer, void *object, char *notification,
			    dwc_notifier_callback_t callback, void *user_data);

/** Removes the specified observer from all notifications that it is currently
 * observing. */
extern int dwc_remove_observer(void *observer);

/** This function triggers a Notification.  It should be called by the
 * observable module, or any module or library which the observable module
 * allows to trigger notification on it's behalf.  Such as the dwc_cc_t.
 *
 * dwc_notify is a non-blocking function.  Callbacks are scheduled called in
 * their own process context for each trigger.  Callbacks can be blocking.
 * dwc_notify can be called from interrupt context if needed.
 *
 */
void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data);

#ifdef __cplusplus
}
#endif

#endif /* __DWC_NOTIFIER_H__ */
