/*******************************************************************************
 *
 * File:     System.h
 *
 * Contents: Declaration of System base-class.
 *
 * Copyright 2017
 *
 * Author: David Juhasz (david.juhasz@tuwien.ac.at)
 *
 ******************************************************************************/

#ifndef ROSA_CORE_SYSTEM_H
#define ROSA_CORE_SYSTEM_H

#include <atomic>
#include <memory>
#include <string>

namespace rosa {

// Forward declarations.
class Unit;

// Base-class for actual agent-systems, the class provides facitlities to keep
// track of Units of the system.
// NOTE: This class and any subclasses are supposed to provide thread-safe
// interfaces.
class System {
public:
  // Signature of creator functions for Units.
  using UnitCreator = Unit *(*)(const size_t, const std::string&, System&);

  // Returns an object implementing the interface defined by the class.
  static std::unique_ptr<System> createSystem(const std::string &Name) noexcept;

protected:
  // Protected ctor, only subclasses can instantiate.
  System(const std::string &Name) noexcept;

  // No copy and move.
  System(const System&) = delete;
  System(System&&) = delete;
  System &operator=(const System&) = delete;
  System &operator=(System&&) = delete;

public:
  // Dtor. Only a cleared System can be destroyed.
  // PRE: SystemCleaned
  virtual ~System(void);

protected:

  // Sets SystemCleaned flag. Can be called only once and the System must not
  // have any live Units.
  // PRE: !SystemCleaned && empty()
  // POST: SystemCleaned
  void markCleaned(void) noexcept;

  // Creates a Unit instance with the given UnitCreator, using the given Name
  // if any, then registers the new Unit instance by calling the virtual
  // member function registerUnit.
  // NOTE: This function handles the member field CountUnits.
  // PRE: !SystemCleaned
  Unit &createUnit(UnitCreator C,
                   const std::string &Name = std::string()) noexcept;

  // Registers the given Unit instance to the System.
  // PRE: !isUnitRegistered(U)
  // POST: isUnitRegistered(U)
  virtual void registerUnit(Unit &U) noexcept = 0;

  // Unregisters and destroys the given Unit.
  // PRE: isUnitRegistered(U)
  // POST: !isUnitRegistered(U) && 'U is destroyed'
  virtual void destroyUnit(Unit &U) noexcept = 0;

  // Returns if the given Unit is registered in the System.
  virtual bool isUnitRegistered(const Unit &U) const noexcept = 0;

public:
  // The textual name of the System.
  const std::string Name;

private:
  // Number of Units constructed in the system.
  // NOTE: Should never be decremented!
  std::atomic<size_t> CountUnits;

  // Indicates that the System has been cleaned and is ready for destruction.
  // The field is initialized as 'false' and can be set by the member function
  // markCleaned.
  // Subclasses must set the flag upon destroying the System instance, which
  // indicates to the destructor of the base-class that all the managed
  // resources has been properly released.
  std::atomic<bool> SystemCleaned;

public:

  // Returns the number of Units constructed in the System so far,
  // including those being already destroyed.
  size_t numberOfConstructedUnits(void) const noexcept;

  // Returns the number of live Units, that have been constructed and not
  // destroyed yet.
  virtual size_t numberOfLiveUnits(void) const noexcept = 0;

  // Returns if the System has no live Units.
  virtual bool empty(void) const noexcept = 0;
};

} // End namespace rosa

#endif // ROSA_CORE_SYSTEM_H

