/* -*- C++ -*-
 * src/lemon/notifier.h - Part of LEMON, a generic C++ optimization library
 *
 * Copyright (C) 2005 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
 * (Egervary Combinatorial Optimization Research Group, EGRES).
 *
 * Permission to use, modify and distribute this software is granted
 * provided that this copyright notice appears in all copies. For
 * precise terms see the accompanying LICENSE file.
 *
 * This software is provided "AS IS" with no warranty of any kind,
 * express or implied, and with no claim as to its suitability for any
 * purpose.
 *
 */

#ifndef LEMON_ALTERATION_OBSERVER_REGISTRY_H
#define LEMON_ALTERATION_OBSERVER_REGISTRY_H

#include <vector>
#include <algorithm>

///\ingroup graphmaps
///\file
///\brief Observer registry for graph alteration observers.

using namespace std;

namespace lemon {

  /// \addtogroup graphmaps
  /// @{

  /// Registry class to register objects observes alterations in the graph.

  /// This class is a registry for the objects which observe the
  /// alterations in a container. The alteration observers can be attached
  /// to and detached from the registry. The observers have to inherit
  /// from the \ref AlterationNotifier::ObserverBase and override
  /// the virtual functions in that.
  ///
  /// The most important application of the alteration observing is the
  /// dynamic map implementation when the observers are observing the
  /// alterations in the graph.
  ///
  /// \param _Item The item type what the observers are observing, usually
  /// edge or node.
  ///
  /// \author Balazs Dezso

  template <typename _Item>
  class AlterationNotifier {
  public:
    typedef _Item Item;

    /// ObserverBase is the base class for the observers.
	
    /// ObserverBase is the abstract base class for the observers.
    /// It will be notified about an item was inserted into or
    /// erased from the graph.
    ///
    /// The observer interface contains some pure virtual functions
    /// to override. The add() and erase() functions are
    /// to notify the oberver when one item is added or
    /// erased.
    ///
    /// The build() and clear() members are to notify the observer
    /// about the container is builded from an empty container or
    /// is cleared to an empty container. 
    /// 
    /// \author Balazs Dezso

    class ObserverBase {
    protected:
      typedef AlterationNotifier Registry;

      friend class AlterationNotifier;

      /// Default constructor.

      /// Default constructor for ObserverBase.
      /// 
      ObserverBase() : registry(0) {}

      virtual ~ObserverBase() {}

      /// Attaches the observer into an AlterationNotifier.

      /// This member attaches the observer into an AlterationNotifier.
      ///
      void attach(AlterationNotifier& r) {
	registry = &r;
	registry->attach(*this);
      }

      /// Detaches the observer into an AlterationNotifier.

      /// This member detaches the observer from an AlterationNotifier.
      ///
      void detach() {
	if (registry) {
	  registry->detach(*this);
	}
      }
	

      /// Gives back a pointer to the registry what the map attached into.

      /// This function gives back a pointer to the registry what the map
      /// attached into.
      ///
      Registry* getRegistry() const { return const_cast<Registry*>(registry); }
      
      /// Gives back true when the observer is attached into a registry.
      bool attached() const { return registry != 0; }
	
    private:

      ObserverBase(const ObserverBase& copy);
      ObserverBase& operator=(const ObserverBase& copy);

    protected:
      
      Registry* registry;
      int registry_index;

    public:

      /// \brief The member function to notificate the observer about an
      /// item is added to the container.
      ///
      /// The add() member function notificates the observer about an item
      /// is added to the container. It have to be overrided in the
      /// subclasses.
	
      virtual void add(const Item&) = 0;	


      /// \brief The member function to notificate the observer about an
      /// item is erased from the container.
      ///
      /// The erase() member function notificates the observer about an
      /// item is erased from the container. It have to be overrided in
      /// the subclasses.
	
      virtual void erase(const Item&) = 0;

      /// \brief The member function to notificate the observer about the
      /// container is builded.
      ///
      /// The build() member function notificates the observer about the
      /// container is builded from an empty container. It have to be
      /// overrided in the subclasses.

      virtual void build() = 0;

      /// \brief The member function to notificate the observer about all
      /// items are erased from the container.
      ///
      /// The clear() member function notificates the observer about all
      /// items are erased from the container. It have to be overrided in
      /// the subclasses.
      
      virtual void clear() = 0;

    };
	
  protected:
	

    typedef std::vector<ObserverBase*> Container; 

    Container container;

		
  public:

    /// Default constructor.
	
    ///
    /// The default constructor of the AlterationNotifier. 
    /// It creates an empty registry.
    AlterationNotifier() {}

    /// Copy Constructor of the AlterationNotifier. 
	
    /// Copy constructor of the AlterationNotifier. 
    /// It creates only an empty registry because the copiable
    /// registry's observers have to be registered still into that registry.
    AlterationNotifier(const AlterationNotifier&) {}

    /// Assign operator.
		
    /// Assign operator for the AlterationNotifier. 
    /// It makes the notifier only empty because the copiable
    /// notifier's observers have to be registered still into that registry.
    AlterationNotifier& operator=(const AlterationNotifier&) {
      typename Container::iterator it;
      for (it = container.begin(); it != container.end(); ++it) {
	(*it)->registry = 0;
      }
    }

    /// Destructor.
				
    /// Destructor of the AlterationNotifier.
    ///
    ~AlterationNotifier() {
      typename Container::iterator it;
      for (it = container.begin(); it != container.end(); ++it) {
	(*it)->registry = 0;
      }
    }
	
	
  protected:

    void attach(ObserverBase& observer) {
      container.push_back(&observer);
      observer.registry = this;
      observer.registry_index = container.size()-1;
    } 

    void detach(ObserverBase& base) {
      container.back()->registry_index = base.registry_index; 
      container[base.registry_index] = container.back();
      container.pop_back();
      base.registry = 0;
    }

  public:
	
    /// Notifies all the registered observers about an Item added to the container.
		
    /// It notifies all the registered observers about an Item added to the container.
    /// 
    void add(const Item& key) {
      typename Container::iterator it;
      for (it = container.begin(); it != container.end(); ++it) {
	(*it)->add(key);
      }
    }	

    /// Notifies all the registered observers about an Item erased from the container.
		
    /// It notifies all the registered observers about an Item erased from the container.
    /// 
    void erase(const Item& key) {
      typename Container::iterator it;
      for (it = container.begin(); it != container.end(); ++it) {
	(*it)->erase(key);
      }
    }
    

    /// Notifies all the registered observers about the container is builded.
		
    /// Notifies all the registered observers about the container is builded
    /// from an empty container.
    void build() {
      typename Container::iterator it;
      for (it = container.begin(); it != container.end(); ++it) {
	(*it)->build();
      }
    }


    /// Notifies all the registered observers about all Items are erased.

    /// Notifies all the registered observers about all Items are erased
    /// from the container.
    void clear() {
      typename Container::iterator it;
      for (it = container.begin(); it != container.end(); ++it) {
	(*it)->clear();
      }
    }
  };


  /// \brief Class to extend a graph with the functionality of alteration
  /// observing.
  ///
  /// AlterableGraphExtender extends the _Base graphs functionality with
  /// the possibility of alteration observing. It defines two observer
  /// registrys for the nodes and mapes.
  /// 
  /// \todo Document what "alteration observing" is. And probably find a
  /// better (shorter) name.
  ///
  /// \param _Base is the base class to extend.
  ///
  /// \pre _Base is conform to the BaseGraphComponent concept.
  ///
  /// \post AlterableGraphExtender<_Base> is conform to the
  /// AlterableGraphComponent concept.
  ///
  /// \author Balazs Dezso

  template <typename _Base> 
  class AlterableGraphExtender : public _Base {
  public:

    typedef AlterableGraphExtender Graph;
    typedef _Base Parent;

    typedef typename Parent::Node Node;
    typedef typename Parent::Edge Edge;

    /// The edge observer registry.
    typedef AlterationNotifier<Edge> EdgeNotifier;
    /// The node observer registry.
    typedef AlterationNotifier<Node> NodeNotifier;


  protected:

    mutable EdgeNotifier edge_notifier;

    mutable NodeNotifier node_notifier;

  public:

    /// \brief Gives back the edge alteration notifier.
    ///
    /// Gives back the edge alteration notifier.
    EdgeNotifier& getNotifier(Edge) const {
      return edge_notifier;
    }

    /// \brief Gives back the node alteration notifier.
    ///
    /// Gives back the node alteration notifier.
    NodeNotifier& getNotifier(Node) const {
      return node_notifier;
    }

    ~AlterableGraphExtender() {
      node_notifier.clear();
      edge_notifier.clear();
    }
    
  };

  /// \brief Class to extend an undirected graph with the functionality of
  /// alteration observing.
  ///
  /// \todo Document.
  ///
  /// \sa AlterableGraphExtender
  ///
  /// \bug This should be done some other way. Possibilities: template
  /// specialization (not very easy, if at all possible); some kind of
  /// enable_if boost technique?

  template <typename _Base> 
  class AlterableUndirGraphExtender
    : public AlterableGraphExtender<_Base> {
  public:

    typedef AlterableUndirGraphExtender Graph;
    typedef AlterableGraphExtender<_Base> Parent;

    typedef typename Parent::UndirEdge UndirEdge;

    /// The edge observer registry.
    typedef AlterationNotifier<UndirEdge> UndirEdgeNotifier;

  protected:

    mutable UndirEdgeNotifier undir_edge_notifier;

  public:

    using Parent::getNotifier;
    UndirEdgeNotifier& getNotifier(UndirEdge) const {
      return undir_edge_notifier;
    }

    ~AlterableUndirGraphExtender() {
      undir_edge_notifier.clear();
    }
  };
  
/// @}
  

}

#endif
