/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/
#ifndef itkEventObject_h
#define itkEventObject_h

#include "itkIndent.h"

namespace itk
{
/** \class EventObject
 * \brief Abstraction of the Events used to communicating among filters
    and with GUIs.
 *
 * EventObject provides a standard coding for sending and receiving messages
 * indicating things like the initiation of processes, end of processes,
 * modification of filters.
 *
 * EventObjects form a hierarchy similar to the itk::ExceptionObject allowing
 * to factorize common events in a tree-like structure. Higher detail can
 * be assigned by users by subclassing existing itk::EventObjects.
 *
 * EventObjects are used by itk::Command and itk::Object for implementing the
 * Observer/Subject design pattern. Observers register their interest in
 * particular kinds of events produced by a specific itk::Object. This
 * mechanism decouples classes among them.
 *
 * As opposed to itk::Exception, itk::EventObject does not represent error
 * states, but simply flow of information allowing to trigger actions
 * as a consequence of changes occurring in state on some itk::Objects.
 *
 * itk::EventObject carries information in its own type, it relies on the
 * appropriate use of the RTTI (Run Time Type Information).
 *
 * A set of standard EventObjects is defined near the end of itkEventObject.h.
 *
 * \sa itk::Command
 * \sa itk::ExceptionObject
 *
 * \ingroup ITKSystemObjects
 * \ingroup ITKCommon
 */
class ITKCommon_EXPORT EventObject
{
public:
  /** Constructor and copy constructor.  Note that these functions will be
   * called when children are instantiated. */
  EventObject() = default;

  EventObject(const EventObject &) = default;

  EventObject &
  operator=(const EventObject &) = delete;

  /** Virtual destructor needed  */
  virtual ~EventObject() = default;

  /**  Create an Event of this type This method work as a Factory for
   *  creating events of each particular type. */
  virtual EventObject *
  MakeObject() const = 0;

  /** Print Event information.  This method can be overridden by
   * specific Event subtypes.  The default is to print out the type of
   * the event. */
  virtual void
  Print(std::ostream & os) const;

  /** Return the StringName associated with the event. */
  virtual const char *
  GetEventName() const = 0;

  /** Check if given event matches or derives from this event. */
  virtual bool
  CheckEvent(const EventObject *) const = 0;

protected:
  /** Methods invoked by Print() to print information about the object
   * including superclasses. Typically not called by the user (use Print()
   * instead) but used in the hierarchical print process to combine the
   * output of several classes.  */
  virtual void
  PrintSelf(std::ostream & os, Indent indent) const;

  virtual void
  PrintHeader(std::ostream & os, Indent indent) const;

  virtual void
  PrintTrailer(std::ostream & os, Indent indent) const;
};

/** Generic inserter operator for EventObject and its subclasses. */
inline std::ostream &
operator<<(std::ostream & os, const EventObject & e)
{
  (&e)->Print(os);
  return os;
}


#define ITKEvent_EXPORT ITKCommon_EXPORT

/**
 *  Macros for creating new Events
 */

#define itkEventMacroDeclaration(classname, super)                                                                     \
  /** \class classname */                                                                                              \
  class ITKEvent_EXPORT classname : public super                                                                       \
  {                                                                                                                    \
  public:                                                                                                              \
    using Self = classname;                                                                                            \
    using Superclass = super;                                                                                          \
    classname();                                                                                                       \
    classname(const Self & s);                                                                                         \
    virtual ~classname();                                                                                              \
    virtual const char *                                                                                               \
    GetEventName() const;                                                                                              \
    virtual bool                                                                                                       \
    CheckEvent(const ::itk::EventObject * e) const;                                                                    \
    virtual ::itk::EventObject *                                                                                       \
    MakeObject() const;                                                                                                \
                                                                                                                       \
  private:                                                                                                             \
    void                                                                                                               \
    operator=(const Self &);                                                                                           \
  };

#define itkEventMacroDefinition(classname, super)                                                                      \
  classname::classname() {}                                                                                            \
  classname::classname(const classname & s)                                                                            \
    : super(s){};                                                                                                      \
  classname::~classname() {}                                                                                           \
  const char * classname::GetEventName() const { return #classname; }                                                  \
  bool         classname::CheckEvent(const ::itk::EventObject * e) const                                               \
  {                                                                                                                    \
    return (dynamic_cast<const classname *>(e) != nullptr);                                                            \
  }                                                                                                                    \
  ::itk::EventObject * classname::MakeObject() const { return new classname; }

//
// This macro duplicates some of the declaration and definition
// macro code. The purpose is to provide a backward compatibility API
// for ITK applications.
// NOTE: New applications should use itkEventMacroDeclaration (in a
// .h file) and itkEventMacroDefinition (in a compiled .cxx
// file). This new approach guarantees that only one copy of the
// implementation will be present.
//
#define itkEventMacro(classname, super)                                                                                \
  /** \class classname */                                                                                              \
  class ITKEvent_EXPORT classname : public super                                                                       \
  {                                                                                                                    \
  public:                                                                                                              \
    using Self = classname;                                                                                            \
    using Superclass = super;                                                                                          \
    classname() {}                                                                                                     \
    virtual ~classname() {}                                                                                            \
    virtual const char *                                                                                               \
    GetEventName() const                                                                                               \
    {                                                                                                                  \
      return #classname;                                                                                               \
    }                                                                                                                  \
    virtual bool                                                                                                       \
    CheckEvent(const ::itk::EventObject * e) const                                                                     \
    {                                                                                                                  \
      return (dynamic_cast<const Self *>(e) != nullptr);                                                               \
    }                                                                                                                  \
    virtual ::itk::EventObject *                                                                                       \
    MakeObject() const                                                                                                 \
    {                                                                                                                  \
      return new Self;                                                                                                 \
    }                                                                                                                  \
    classname(const Self & s)                                                                                          \
      : super(s){};                                                                                                    \
                                                                                                                       \
  private:                                                                                                             \
    void                                                                                                               \
    operator=(const Self &);                                                                                           \
  };

/**
 *      Delclare some common ITK events
 */
itkEventMacroDeclaration(NoEvent, EventObject) itkEventMacroDeclaration(AnyEvent, EventObject)
  itkEventMacroDeclaration(DeleteEvent, AnyEvent) itkEventMacroDeclaration(StartEvent, AnyEvent)
    itkEventMacroDeclaration(EndEvent, AnyEvent) itkEventMacroDeclaration(ProgressEvent, AnyEvent)
      itkEventMacroDeclaration(ExitEvent, AnyEvent) itkEventMacroDeclaration(AbortEvent, AnyEvent)
        itkEventMacroDeclaration(ModifiedEvent, AnyEvent) itkEventMacroDeclaration(InitializeEvent, AnyEvent)
          itkEventMacroDeclaration(IterationEvent, AnyEvent)
            itkEventMacroDeclaration(MultiResolutionIterationEvent, IterationEvent)
              itkEventMacroDeclaration(PickEvent, AnyEvent) itkEventMacroDeclaration(StartPickEvent, PickEvent)
                itkEventMacroDeclaration(EndPickEvent, PickEvent) itkEventMacroDeclaration(AbortCheckEvent, PickEvent)
                  itkEventMacroDeclaration(FunctionEvaluationIterationEvent, IterationEvent)
                    itkEventMacroDeclaration(GradientEvaluationIterationEvent, IterationEvent)
                      itkEventMacroDeclaration(FunctionAndGradientEvaluationIterationEvent, IterationEvent)
                        itkEventMacroDeclaration(UserEvent, AnyEvent)

#undef ITKEvent_EXPORT
#define ITKEvent_EXPORT ITK_ABI_EXPORT

} // end namespace itk

#endif
