/**
* @file llnotifications.h
* @brief Non-UI manager and support for keeping a prioritized list of notifications
* @author Q (with assistance from Richard and Coco)
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
* $/LicenseInfo$
*/

#ifndef LL_LLNOTIFICATIONS_H
#define LL_LLNOTIFICATIONS_H

/**
 * This system is intended to provide a singleton mechanism for adding
 * notifications to one of an arbitrary set of event channels.
 *
 * Controlling JIRA: DEV-9061
 *
 * Every notification has (see code for full list):
 *  - a textual name, which is used to look up its template in the XML files
 *  - a payload, which is a block of LLSD
 *  - a channel, which is normally extracted from the XML files but
 *    can be overridden.
 *  - a timestamp, used to order the notifications
 *  - expiration time -- if nonzero, specifies a time after which the
 *    notification will no longer be valid.
 *  - a callback name and a couple of status bits related to callbacks (see below)
 *
 * There is a management class called LLNotifications, which is an LLSingleton.
 * The class maintains a collection of all of the notifications received
 * or processed during this session, and also manages the persistence
 * of those notifications that must be persisted.
 *
 * We also have Channels. A channel is a view on a collection of notifications;
 * The collection is defined by a filter function that controls which
 * notifications are in the channel, and its ordering is controlled by
 * a comparator.
 *
 * There is a hierarchy of channels; notifications flow down from
 * the management class (LLNotifications, which itself inherits from
 * The channel base class) to the individual channels.
 * Any change to notifications (add, delete, modify) is
 * automatically propagated through the channel hierarchy.
 *
 * We provide methods for adding a new notification, for removing
 * one, and for managing channels. Channels are relatively cheap to construct
 * and maintain, so in general, human interfaces should use channels to
 * select and manage their lists of notifications.
 *
 * We also maintain a collection of templates that are loaded from the
 * XML file of template translations. The system supports substitution
 * of named variables from the payload into the XML file.
 *
 * By default, only the "unknown message" template is built into the system.
 * It is not an error to add a notification that's not found in the
 * template system, but it is logged.
 *
 */

#include <string>
#include <list>
#include <vector>
#include <map>
#include <set>
#include <iomanip>
#include <sstream>

#include <boost/utility.hpp>
#include <boost/type_traits.hpp>
#include <boost/signals2.hpp>
#include <boost/range.hpp>

#include "llevents.h"
#include "llfunctorregistry.h"
#include "llinitparam.h"
#include "llinstancetracker.h"
#include "llmortician.h"
#include "llnotificationptr.h"
#include "llpointer.h"
#include "llrefcount.h"
#include "llsdparam.h"

#include "llnotificationslistener.h"

class LLAvatarName;
typedef enum e_notification_priority
{
    NOTIFICATION_PRIORITY_UNSPECIFIED,
    NOTIFICATION_PRIORITY_LOW,
    NOTIFICATION_PRIORITY_NORMAL,
    NOTIFICATION_PRIORITY_HIGH,
    NOTIFICATION_PRIORITY_CRITICAL
} ENotificationPriority;

struct NotificationPriorityValues : public LLInitParam::TypeValuesHelper<ENotificationPriority, NotificationPriorityValues>
{
    static void declareValues();
};

class LLNotificationResponderInterface
{
public:
    LLNotificationResponderInterface(){};
    virtual ~LLNotificationResponderInterface(){};

    virtual void handleRespond(const LLSD& notification, const LLSD& response) = 0;

    virtual LLSD asLLSD() = 0;

    virtual void fromLLSD(const LLSD& params) = 0;
};

typedef boost::function<void (const LLSD&, const LLSD&)> LLNotificationResponder;

typedef std::shared_ptr<LLNotificationResponderInterface> LLNotificationResponderPtr;

typedef LLFunctorRegistry<LLNotificationResponder> LLNotificationFunctorRegistry;
typedef LLFunctorRegistration<LLNotificationResponder> LLNotificationFunctorRegistration;

// context data that can be looked up via a notification's payload by the display logic
// derive from this class to implement specific contexts
class LLNotificationContext : public LLInstanceTracker<LLNotificationContext, LLUUID>
{
public:

    LLNotificationContext() : LLInstanceTracker<LLNotificationContext, LLUUID>(LLUUID::generateNewID())
    {
    }

    virtual ~LLNotificationContext() {}

    LLSD asLLSD() const
    {
        return getKey();
    }

private:

};

// Contains notification form data, such as buttons and text fields along with
// manipulator functions
class LLNotificationForm
{
    LOG_CLASS(LLNotificationForm);

public:
    struct FormElementBase : public LLInitParam::Block<FormElementBase>
    {
        Optional<std::string>   name;
        Optional<bool>          enabled;

        FormElementBase();
    };

    struct FormIgnore : public LLInitParam::Block<FormIgnore, FormElementBase>
    {
        Optional<std::string>   text;
        Optional<bool>          save_option;
        Optional<std::string>   control;
        Optional<bool>          invert_control;
        Optional<bool>          session_only;
        Optional<bool>          checkbox_only;

        FormIgnore();
    };

    struct FormButton : public LLInitParam::Block<FormButton, FormElementBase>
    {
        Mandatory<S32>          index;
        Mandatory<std::string>  text;
        Optional<std::string>   ignore;
        Optional<bool>          is_default;
        Optional<S32>           width;

        Mandatory<std::string>  type;

        FormButton();
    };

    struct FormInput : public LLInitParam::Block<FormInput, FormElementBase>
    {
        Mandatory<std::string>  type;
        Optional<S32>           width;
        Optional<S32>           max_length_chars;
        Optional<bool>          allow_emoji;
        Optional<std::string>   text;

        Optional<std::string>   value;
        FormInput();
    };

    struct FormElement : public LLInitParam::ChoiceBlock<FormElement>
    {
        Alternative<FormButton> button;
        Alternative<FormInput>  input;

        FormElement();
    };

    struct FormElements : public LLInitParam::Block<FormElements>
    {
        Multiple<FormElement> elements;
        FormElements();
    };

    struct Params : public LLInitParam::Block<Params>
    {
        Optional<std::string>   name;
        Optional<FormIgnore>    ignore;
        Optional<FormElements>  form_elements;

        Params();
    };

    typedef enum e_ignore_type
    {
        IGNORE_CHECKBOX_ONLY = -1, // ignore won't be handled, will set value/checkbox only
        IGNORE_NO = 0,
        IGNORE_WITH_DEFAULT_RESPONSE,
        IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY,
        IGNORE_WITH_LAST_RESPONSE,
        IGNORE_SHOW_AGAIN
    } EIgnoreType;

    LLNotificationForm();
    LLNotificationForm(const LLNotificationForm&);
    LLNotificationForm(const LLSD& sd);
    LLNotificationForm(const std::string& name, const Params& p);

    void fromLLSD(const LLSD& sd);
    LLSD asLLSD() const;

    S32 getNumElements() { return mFormData.size(); }
    LLSD getElement(S32 index) { return mFormData.get(index); }
    LLSD getElement(const std::string& element_name);
    void getElements(LLSD& elements, S32 offset = 0);
    bool hasElement(const std::string& element_name) const;
    bool getElementEnabled(const std::string& element_name) const;
    void setElementEnabled(const std::string& element_name, bool enabled);
    void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD(), bool enabled = true);
    void formatElements(const LLSD& substitutions);
    // appends form elements from another form serialized as LLSD
    void append(const LLSD& sub_form);
    std::string getDefaultOption();
    LLPointer<class LLControlVariable> getIgnoreSetting();
    bool getIgnored();
    void setIgnored(bool ignored);

    EIgnoreType getIgnoreType() { return mIgnore; }
    std::string getIgnoreMessage() { return mIgnoreMsg; }

private:
    LLSD                                mFormData;
    EIgnoreType                         mIgnore;
    std::string                         mIgnoreMsg;
    LLPointer<class LLControlVariable>  mIgnoreSetting;
    bool                                mInvertSetting;
};

typedef std::shared_ptr<LLNotificationForm> LLNotificationFormPtr;


struct LLNotificationTemplate;

// we want to keep a map of these by name, and it's best to manage them
// with smart pointers
typedef std::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr;


struct LLNotificationVisibilityRule;

typedef std::shared_ptr<LLNotificationVisibilityRule> LLNotificationVisibilityRulePtr;

/**
 * @class LLNotification
 * @brief The object that expresses the details of a notification
 *
 * We make this noncopyable because
 * we want to manage these through LLNotificationPtr, and only
 * ever create one instance of any given notification.
 *
 * The enable_shared_from_this flag ensures that if we construct
 * a smart pointer from a notification, we'll always get the same
 * shared pointer.
 */
class LLNotification  :
    boost::noncopyable,
    public std::enable_shared_from_this<LLNotification>
{
LOG_CLASS(LLNotification);
friend class LLNotifications;

public:

    // parameter object used to instantiate a new notification
    struct Params : public LLInitParam::Block<Params>
    {
        friend class LLNotification;

        Mandatory<std::string>                  name;
        Optional<LLUUID>                        id;
        Optional<LLSD>                          substitutions,
                                                form_elements,
                                                payload;
        Optional<ENotificationPriority, NotificationPriorityValues> priority;
        Optional<LLDate>                        time_stamp,
                                                expiry;
        Optional<LLNotificationContext*>        context;
        Optional<void*>                         responder;
        Optional<bool>                          offer_from_agent;
        Optional<bool>                          is_dnd;

        struct Functor : public LLInitParam::ChoiceBlock<Functor>
        {
            Alternative<std::string>                                        name;
            Alternative<LLNotificationFunctorRegistry::ResponseFunctor> function;
            Alternative<LLNotificationResponderPtr>                     responder;
            Alternative<LLSD>                                           responder_sd;

            Functor()
            :   name("responseFunctor"),
                function("functor"),
                responder("responder"),
                responder_sd("responder_sd")
            {}
        };
        Optional<Functor>                       functor;

        Params()
        :   name("name"),
            id("id"),
            priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
            time_stamp("time"),
            payload("payload"),
            form_elements("form"),
            substitutions("substitutions"),
            expiry("expiry"),
            offer_from_agent("offer_from_agent", false),
            is_dnd("is_dnd", false)
        {
            time_stamp = LLDate::now();
            responder = NULL;
        }

        Params(const std::string& _name)
        :   name("name"),
            priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED),
            time_stamp("time"),
            payload("payload"),
            form_elements("form"),
            substitutions("substitutions"),
            expiry("expiry"),
            offer_from_agent("offer_from_agent", false),
            is_dnd("is_dnd", false)
        {
            functor.name = _name;
            name = _name;
            time_stamp = LLDate::now();
            responder = NULL;
        }
    };

    LLNotificationResponderPtr getResponderPtr() { return mResponder; }

private:

    const LLUUID mId;
    LLSD mPayload;
    LLSD mSubstitutions;
    LLDate mTimestamp;
    LLDate mExpiresAt;
    bool mCancelled;
    bool mRespondedTo;  // once the notification has been responded to, this becomes true
    LLSD mResponse;
    bool mIgnored;
    ENotificationPriority mPriority;
    LLNotificationFormPtr mForm;
    void* mResponderObj; // TODO - refactor/remove this field
    LLNotificationResponderPtr mResponder;
    bool mOfferFromAgent;
    bool mIsDND;

    // a reference to the template
    LLNotificationTemplatePtr mTemplatep;

    /*
     We want to be able to store and reload notifications so that they can survive
     a shutdown/restart of the client. So we can't simply pass in callbacks;
     we have to specify a callback mechanism that can be used by name rather than
     by some arbitrary pointer -- and then people have to initialize callbacks
     in some useful location. So we use LLNotificationFunctorRegistry to manage them.
     */
     std::string mResponseFunctorName;

    /*
     In cases where we want to specify an explict, non-persisted callback,
     we store that in the callback registry under a dynamically generated
     key, and store the key in the notification, so we can still look it up
     using the same mechanism.
     */
    bool mTemporaryResponder;

    // keep track of other notifications combined with COMBINE_WITH_NEW
    std::vector<LLNotificationPtr> mCombinedNotifications;

    void init(const std::string& template_name, const LLSD& form_elements);

    void cancel();

public:
    LLNotification(const LLSDParamAdapter<Params>& p);

    void setResponseFunctor(std::string const &responseFunctorName);

    void setResponseFunctor(const LLNotificationFunctorRegistry::ResponseFunctor& cb);

    void setResponseFunctor(const LLNotificationResponderPtr& responder);

    typedef enum e_response_template_type
    {
        WITHOUT_DEFAULT_BUTTON,
        WITH_DEFAULT_BUTTON
    } EResponseTemplateType;

    // return response LLSD filled in with default form contents and (optionally) the default button selected
    LLSD getResponseTemplate(EResponseTemplateType type = WITHOUT_DEFAULT_BUTTON);

    // returns index of first button with value==TRUE
    // usually this the button the user clicked on
    // returns -1 if no button clicked (e.g. form has not been displayed)
    static S32 getSelectedOption(const LLSD& notification, const LLSD& response);
    // returns name of first button with value==TRUE
    static std::string getSelectedOptionName(const LLSD& notification);

    // after someone responds to a notification (usually by clicking a button,
    // but sometimes by filling out a little form and THEN clicking a button),
    // the result of the response (the name and value of the button clicked,
    // plus any other data) should be packaged up as LLSD, then passed as a
    // parameter to the notification's respond() method here. This will look up
    // and call the appropriate responder.
    //
    // response is notification serialized as LLSD:
    // ["name"] = notification name
    // ["form"] = LLSD tree that includes form description and any prefilled form data
    // ["response"] = form data filled in by user
    // (including, but not limited to which button they clicked on)
    // ["payload"] = transaction specific data, such as ["source_id"] (originator of notification),
    //              ["item_id"] (attached inventory item), etc.
    // ["substitutions"] = string substitutions used to generate notification message
    // from the template
    // ["time"] = time at which notification was generated;
    // ["expiry"] = time at which notification expires;
    // ["responseFunctor"] = name of registered functor that handles responses to notification;
    LLSD asLLSD(bool excludeTemplateElements = false);

    const LLNotificationFormPtr getForm();
    void updateForm(const LLNotificationFormPtr& form);

    void repost();

    void respond(const LLSD& sd);
    void respondWithDefault();

    void* getResponder() { return mResponderObj; }

    void setResponder(void* responder) { mResponderObj = responder; }

    void setIgnored(bool ignore);

    bool isCancelled() const
    {
        return mCancelled;
    }

    bool isRespondedTo() const
    {
        return mRespondedTo;
    }

    bool isActive() const
    {
        return !isRespondedTo()
            && !isCancelled()
            && !isExpired();
    }

    const LLSD& getResponse() { return mResponse; }

    bool isIgnored() const
    {
        return mIgnored;
    }

    const std::string& getName() const;

    const std::string& getIcon() const;

    bool isPersistent() const;

    const LLUUID& id() const
    {
        return mId;
    }

    const LLSD& getPayload() const
    {
        return mPayload;
    }

    const LLSD& getSubstitutions() const
    {
        return mSubstitutions;
    }

    const LLDate& getDate() const
    {
        return mTimestamp;
    }

    bool getOfferFromAgent() const
    {
        return mOfferFromAgent;
    }

    bool isDND() const
    {
        return mIsDND;
    }

    void setDND(const bool flag)
    {
        mIsDND = flag;
    }

    std::string getType() const;
    std::string getMessage() const;
    std::string getFooter() const;
    std::string getLabel() const;
    std::string getURL() const;
    S32 getURLOption() const;
    S32 getURLOpenExternally() const; //for url responce option
    bool getForceUrlsExternal() const;
    bool canLogToChat() const;
    bool canLogToIM() const;
    bool canShowToast() const;
    bool canFadeToast() const;
    bool hasFormElements() const;
    void playSound();

    typedef enum e_combine_behavior
    {
        REPLACE_WITH_NEW,
        COMBINE_WITH_NEW,
        KEEP_OLD,
        CANCEL_OLD

    } ECombineBehavior;

    ECombineBehavior getCombineBehavior() const;

    const LLDate getExpiration() const
    {
        return mExpiresAt;
    }

    ENotificationPriority getPriority() const
    {
        return mPriority;
    }

    const LLUUID getID() const
    {
        return mId;
    }

    // comparing two notifications normally means comparing them by UUID (so we can look them
    // up quickly this way)
    bool operator<(const LLNotification& rhs) const
    {
        return mId < rhs.mId;
    }

    bool operator==(const LLNotification& rhs) const
    {
        return mId == rhs.mId;
    }

    bool operator!=(const LLNotification& rhs) const
    {
        return !operator==(rhs);
    }

    bool isSameObjectAs(const LLNotification* rhs) const
    {
        return this == rhs;
    }

    // this object has been updated, so tell all our clients
    void update();

    void updateFrom(LLNotificationPtr other);

    // A fuzzy equals comparator.
    // true only if both notifications have the same template and
    //     1) flagged as unique (there can be only one of these) OR
    //     2) all required payload fields of each also exist in the other.
    bool isEquivalentTo(LLNotificationPtr that) const;

    // if the current time is greater than the expiration, the notification is expired
    bool isExpired() const
    {
        if (mExpiresAt.secondsSinceEpoch() == 0)
        {
            return false;
        }

        LLDate rightnow = LLDate::now();
        return rightnow > mExpiresAt;
    }

    std::string summarize() const;

    bool hasUniquenessConstraints() const;

    bool matchesTag(const std::string& tag);

    virtual ~LLNotification() {}
};

std::ostream& operator<<(std::ostream& s, const LLNotification& notification);

namespace LLNotificationFilters
{
    // a sample filter
    bool includeEverything(LLNotificationPtr p);

    typedef enum e_comparison
    {
        EQUAL,
        LESS,
        GREATER,
        LESS_EQUAL,
        GREATER_EQUAL
    } EComparison;

    // generic filter functor that takes method or member variable reference
    template<typename T>
    struct filterBy
    {
        typedef boost::function<T (LLNotificationPtr)>  field_t;
        typedef typename boost::remove_reference<T>::type       value_t;

        filterBy(field_t field, value_t value, EComparison comparison = EQUAL)
            :   mField(field),
                mFilterValue(value),
                mComparison(comparison)
        {
        }

        bool operator()(LLNotificationPtr p)
        {
            switch(mComparison)
            {
            case EQUAL:
                return mField(p) == mFilterValue;
            case LESS:
                return mField(p) < mFilterValue;
            case GREATER:
                return mField(p) > mFilterValue;
            case LESS_EQUAL:
                return mField(p) <= mFilterValue;
            case GREATER_EQUAL:
                return mField(p) >= mFilterValue;
            default:
                return false;
            }
        }

        field_t mField;
        value_t mFilterValue;
        EComparison mComparison;
    };
};

namespace LLNotificationComparators
{
    struct orderByUUID
    {
        bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs) const
        {
            return lhs->id() < rhs->id();
        }
    };
};

typedef boost::function<bool (LLNotificationPtr)> LLNotificationFilter;
typedef std::set<LLNotificationPtr, LLNotificationComparators::orderByUUID> LLNotificationSet;
typedef std::multimap<std::string, LLNotificationPtr> LLNotificationMap;

// ========================================================
// Abstract base class (interface) for a channel; also used for the master container.
// This lets us arrange channels into a call hierarchy.

// We maintain a hierarchy of notification channels; events are always started at the top
// and propagated through the hierarchy only if they pass a filter.
// Any channel can be created with a parent. A null parent (empty string) means it's
// tied to the root of the tree (the LLNotifications class itself).
// The default hierarchy looks like this:
//
// LLNotifications --+-- Expiration --+-- Mute --+-- Ignore --+-- Visible --+-- History
//                                                                          +-- Alerts
//                                                                          +-- Notifications
//
// In general, new channels that want to only see notifications that pass through
// all of the built-in tests should attach to the "Visible" channel
//
class LLNotificationChannelBase :
    public boost::signals2::trackable,
    public LLRefCount
{
    LOG_CLASS(LLNotificationChannelBase);
public:
    LLNotificationChannelBase(LLNotificationFilter filter)
    : mFilter(filter)
    , mItems()
    , mItemsMutex()
    {}

    virtual ~LLNotificationChannelBase()
    {
        // explicit cleanup for easier issue detection
        mChanged.disconnect_all_slots();
        mPassedFilter.disconnect_all_slots();
        mFailedFilter.disconnect_all_slots();
        LLMutexLock lock(&mItemsMutex);
        mItems.clear();
    }
    // you can also connect to a Channel, so you can be notified of
    // changes to this channel
    LLBoundListener connectChanged(const LLEventListener& slot)
    {
        // Call this->connectChangedImpl() to actually connect it.
        return connectChangedImpl(slot);
    }
    LLBoundListener connectAtFrontChanged(const LLEventListener& slot)
    {
        return connectAtFrontChangedImpl(slot);
    }
    LLBoundListener connectPassedFilter(const LLEventListener& slot)
    {
        // see comments in connectChanged()
        return connectPassedFilterImpl(slot);
    }
    LLBoundListener connectFailedFilter(const LLEventListener& slot)
    {
        // see comments in connectChanged()
        return connectFailedFilterImpl(slot);
    }

    // use this when items change or to add a new one
    bool updateItem(const LLSD& payload);
    const LLNotificationFilter& getFilter() { return mFilter; }

protected:
    LLBoundListener connectChangedImpl(const LLEventListener& slot);
    LLBoundListener connectAtFrontChangedImpl(const LLEventListener& slot);
    LLBoundListener connectPassedFilterImpl(const LLEventListener& slot);
    LLBoundListener connectFailedFilterImpl(const LLEventListener& slot);

    LLNotificationSet mItems;
    LLStandardSignal mChanged;
    LLStandardSignal mPassedFilter;
    LLStandardSignal mFailedFilter;
    LLMutex mItemsMutex;

    // these are action methods that subclasses can override to take action
    // on specific types of changes; the management of the mItems list is
    // still handled by the generic handler.
    virtual void onLoad(LLNotificationPtr p) {}
    virtual void onAdd(LLNotificationPtr p) {}
    virtual void onDelete(LLNotificationPtr p) {}
    virtual void onChange(LLNotificationPtr p) {}

    virtual void onFilterPass(LLNotificationPtr p) {}
    virtual void onFilterFail(LLNotificationPtr p) {}

    bool updateItem(const LLSD& payload, LLNotificationPtr pNotification);
    LLNotificationFilter mFilter;
};

// The type of the pointers that we're going to manage in the NotificationQueue system
// Because LLNotifications is a singleton, we don't actually expect to ever
// destroy it, but if it becomes necessary to do so, the shared_ptr model
// will ensure that we don't leak resources.
class LLNotificationChannel;
typedef boost::intrusive_ptr<LLNotificationChannel> LLNotificationChannelPtr;

// manages a list of notifications
// Note that if this is ever copied around, we might find ourselves with multiple copies
// of a queue with notifications being added to different nonequivalent copies. So we
// make it inherit from boost::noncopyable, and then create a map of LLPointer to manage it.
//
class LLNotificationChannel :
    boost::noncopyable,
    public LLNotificationChannelBase,
    public LLInstanceTracker<LLNotificationChannel, std::string>
{
    LOG_CLASS(LLNotificationChannel);

public:
    // Notification Channels have a filter, which determines which notifications
    // will be added to this channel.
    // Channel filters cannot change.
    struct Params : public LLInitParam::Block<Params>
    {
        Mandatory<std::string>              name;
        Optional<LLNotificationFilter>      filter;
        Multiple<std::string>               sources;
    };

    LLNotificationChannel(const Params& p = Params());
    LLNotificationChannel(const std::string& name, const std::string& parent, LLNotificationFilter filter);

    virtual ~LLNotificationChannel();
    typedef LLNotificationSet::iterator Iterator;

    std::string getName() const { return mName; }
    typedef std::vector<std::string>::const_iterator parents_iter;
    boost::iterator_range<parents_iter> getParents() const
    {
        return boost::iterator_range<parents_iter>(mParents);
    }

    bool isEmpty() const;
    S32 size() const;
    size_t size();

    typedef boost::function<void(LLNotificationPtr)> NotificationProcess;
    void forEachNotification(NotificationProcess process);

    std::string summarize();

protected:
    void connectToChannel(const std::string& channel_name);

private:
    std::string mName;
    std::vector<std::string> mParents;
    std::vector<LLBoundListener> mListeners;
};

// An interface class to provide a clean linker seam to the LLNotifications class.
// Extend this interface as needed for your use of LLNotifications.
class LLNotificationsInterface
{
public:
    virtual LLNotificationPtr add(const std::string& name,
                        const LLSD& substitutions,
                        const LLSD& payload,
                        LLNotificationFunctorRegistry::ResponseFunctor functor) = 0;
};

class LLNotifications :
    public LLNotificationsInterface,
    public LLSingleton<LLNotifications>,
    public LLNotificationChannelBase
{
    LLSINGLETON(LLNotifications);
    LOG_CLASS(LLNotifications);
    virtual ~LLNotifications() {}

public:

    // Needed to clear up RefCounted things prior to actual destruction
    // as the singleton nature of the class makes them do "bad things"
    // on at least Mac, if not all 3 platforms
    //
    void clear();

    // load all notification descriptions from file
    // calling more than once will overwrite existing templates
    // but never delete a template
    bool loadTemplates();

    // load visibility rules from file;
    // OK to call more than once because it will reload
    bool loadVisibilityRules();

    // Add a simple notification (from XUI)
    void addFromCallback(const LLSD& name);

    // *NOTE: To add simple notifications, #include "llnotificationsutil.h"
    // and use LLNotificationsUtil::add("MyNote") or add("MyNote", args)
    LLNotificationPtr add(const std::string& name,
                        const LLSD& substitutions,
                        const LLSD& payload);
    LLNotificationPtr add(const std::string& name,
                        const LLSD& substitutions,
                        const LLSD& payload,
                        const std::string& functor_name);
    /* virtual */ LLNotificationPtr add(const std::string& name,
                        const LLSD& substitutions,
                        const LLSD& payload,
                        LLNotificationFunctorRegistry::ResponseFunctor functor) override;
    LLNotificationPtr add(const LLNotification::Params& p);

    void add(const LLNotificationPtr pNotif);
    void load(const LLNotificationPtr pNotif);
    void cancel(LLNotificationPtr pNotif);
    void cancelByName(const std::string& name);
    void cancelByOwner(const LLUUID ownerId);
    void update(const LLNotificationPtr pNotif);

    LLNotificationPtr find(LLUUID uuid);

    // This is all stuff for managing the templates
    // take your template out
    LLNotificationTemplatePtr getTemplate(const std::string& name);

    // get the whole collection
    typedef std::vector<std::string> TemplateNames;
    TemplateNames getTemplateNames() const;  // returns a list of notification names

    typedef std::map<std::string, LLNotificationTemplatePtr> TemplateMap;

    TemplateMap::const_iterator templatesBegin() { return mTemplates.begin(); }
    TemplateMap::const_iterator templatesEnd() { return mTemplates.end(); }

    // test for existence
    bool templateExists(const std::string& name);

    typedef std::list<LLNotificationVisibilityRulePtr> VisibilityRuleList;

    void forceResponse(const LLNotification::Params& params, S32 option);

    void createDefaultChannels();

    LLNotificationChannelPtr getChannel(const std::string& channelName);

    std::string getGlobalString(const std::string& key) const;

    void setIgnoreAllNotifications(bool ignore);
    bool getIgnoreAllNotifications();

    void setIgnored(const std::string& name, bool ignored);
    bool getIgnored(const std::string& name);

    bool isVisibleByRules(LLNotificationPtr pNotification);

private:
    /*virtual*/ void initSingleton() override;
    /*virtual*/ void cleanupSingleton() override;

    void loadPersistentNotifications();

    bool expirationFilter(LLNotificationPtr pNotification);
    bool expirationHandler(const LLSD& payload);
    bool uniqueFilter(LLNotificationPtr pNotification);
    bool uniqueHandler(const LLSD& payload);
    bool failedUniquenessTest(const LLSD& payload);
    LLNotificationChannelPtr pHistoryChannel;
    LLNotificationChannelPtr pExpirationChannel;

    TemplateMap mTemplates;

    VisibilityRuleList mVisibilityRules;

    std::string mFileName;

    LLNotificationMap mUniqueNotifications;

    typedef std::map<std::string, std::string> GlobalStringMap;
    GlobalStringMap mGlobalStrings;

    bool mIgnoreAllNotifications;

    std::unique_ptr<LLNotificationsListener> mListener;

    std::vector<LLNotificationChannelPtr> mDefaultChannels;
};

/**
 * Abstract class for postponed notifications.
 * Provides possibility to add notification after specified by id avatar or group will be
 * received from cache name. The object of this type automatically well be deleted
 * by cleanup method after respond will be received from cache name.
 *
 * To add custom postponed notification to the notification system client should:
 *  1 create class derived from LLPostponedNotification;
 *  2 call LLPostponedNotification::add method;
 */
class LLPostponedNotification : public LLMortician
{
public:
    /**
     * Performs hooking cache name callback which will add notification to notifications system.
     * Type of added notification should be specified by template parameter T
     * and non-private derived from LLPostponedNotification class,
     * otherwise compilation error will occur.
     */
    template<class T>
    static void add(const LLNotification::Params& params,
            const LLUUID& id, bool is_group)
    {
        // upcast T to the base type to restrict T derivation from LLPostponedNotification
        LLPostponedNotification* thiz = new T();
        thiz->mParams = params;

        // Avoid header file dependency on llcachename.h
        thiz->lookupName(id, is_group);
    }

private:
    void lookupName(const LLUUID& id, bool is_group);
    // only used for groups
    void onGroupNameCache(const LLUUID& id, const std::string& full_name, bool is_group);
    // only used for avatars
    void fetchAvatarName(const LLUUID& id);
    void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
    // used for both group and avatar names
    void finalizeName(const std::string& name);

    void cleanup()
    {
        die();
    }

protected:
    LLPostponedNotification()
        : mParams(),
        mName(),
        mAvatarNameCacheConnection()
    {}

    virtual ~LLPostponedNotification()
    {
        if (mAvatarNameCacheConnection.connected())
        {
            mAvatarNameCacheConnection.disconnect();
        }
    }

    /**
     * Abstract method provides possibility to modify notification parameters and
     * will be called after cache name retrieve information about avatar or group
     * and before notification will be added to the notification system.
     */
    virtual void modifyNotificationParams() = 0;

    LLNotification::Params mParams;
    std::string mName;
    boost::signals2::connection mAvatarNameCacheConnection;
};

// Stores only persistent notifications.
// Class users can use connectChanged() to process persistent notifications
// (see LLPersistentNotificationStorage for example).
class LLPersistentNotificationChannel : public LLNotificationChannel
{
    LOG_CLASS(LLPersistentNotificationChannel);
public:
    LLPersistentNotificationChannel()
        :   LLNotificationChannel("Persistent", "Visible", &notificationFilter)
    {}

    virtual ~LLPersistentNotificationChannel()
    {
        mHistory.clear();
    }

    typedef std::vector<LLNotificationPtr> history_list_t;
    history_list_t::iterator beginHistory() { sortHistory(); return mHistory.begin(); }
    history_list_t::iterator endHistory() { return mHistory.end(); }

private:
    struct sortByTime
    {
        S32 operator ()(const LLNotificationPtr& a, const LLNotificationPtr& b)
        {
            return a->getDate() < b->getDate();
        }
    };

    void sortHistory()
    {
        std::sort(mHistory.begin(), mHistory.end(), sortByTime());
    }

    // The channel gets all persistent notifications except those that have been canceled
    static bool notificationFilter(LLNotificationPtr pNotification)
    {
        bool handle_notification = false;

        handle_notification = pNotification->isPersistent()
            && !pNotification->isCancelled();

        return handle_notification;
    }

    void onAdd(LLNotificationPtr p)
    {
        mHistory.push_back(p);
    }

    void onLoad(LLNotificationPtr p)
    {
        mHistory.push_back(p);
    }

    std::vector<LLNotificationPtr> mHistory;
};

#endif//LL_LLNOTIFICATIONS_H