// src/backend/sgpemv2/simulation.hh - Copyright 2005, 2006, University
//               of Padova, dept. of Pure and Applied
//               Mathematics
//
// This file is part of SGPEMv2.
//
// This is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// SGPEMv2 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with SGPEMv2. If not, see http://www.gnu.org/licenses/.


#ifndef SIMULATION_HH
#define SIMULATION_HH 1

namespace sgpem
{
  class ConcreteSimulation;
  class CPUPolicy;
  class ResourcePolicy;
	class SimulationObserver;
}

#include <sgpemv2/sgpemv2-visibility.hh>
#include <sgpemv2/history.hh>
#include <sgpemv2/templates/singleton.hh>
#include <sgpemv2/cpu_policy_exception.hh>
#include <sgpemv2/user_interrupt_exception.hh>
#include <sgpemv2/null_policy_exception.hh>
#include <sgpemv2/malformed_policy_exception.hh>
#include <stdexcept>
#include <utility>
#include <vector>

namespace sgpem
{
  class Simulation;

  /**
    \brief Manages a single simulation instance.

    Starting from a base state, simulates the progress of a scheduling system.
    Given the discrete nature of the implementation, once a scheduling algorithm
    and its parameters are fixed, the state of the system is a function of time.
    This doesn't mean the behaviour of the policy is assumed to be deterministic,
    since this will restrict the range of available policies (we won't be able 
    to uselottery scheduling, for example).

    It provides methods to edit the state of the simulation, to check the 
    characteristics of the simulation (advancement speed, advancement mode) and to
    check which schedulng algorithm is currently in use.

    \remarks Implements the Observer pattern: observes classes \ref History, \ref Scheduler,
    \ref CPUPolicy.
    \remarks Implements the Controller pattern: ensures Low Coupling between the Frontend and
    the Backend layers.
   */

  class SG_DLLEXPORT Simulation : public Singleton<ConcreteSimulation>
  {
  public:
    class LockNotify;
    friend class Simulation::LockNotify;

    enum state
    {
      state_running = 0xdeafd0d0,
      state_paused  = 0xbaddcafe,
      state_stopped = 0xdeadbeef
    };

    enum mode
    {
      mode_step_by_step = 0,
      mode_continuous   = 1
    };

    virtual ~Simulation() = 0;

    /**
    \brief Runs the simulation.

    Advances the simulation by one or more steps, depending on the
    actual state and on the value set with set_mode().
    */
    virtual void  run() = 0;

    /**
      \brief Pauses a running simulation.

      It is obviously useful only when the advancement mode is continue.
      Calling again run() will cause the simulation to start from the current
      simulation step.
     */
    virtual void  pause() = 0;

    /**
      \brief Stops the simulation.

      Behaves in the same way as pause(), except that the next call to run() 
      will cause the simulation to start from the beginning.
     */
    virtual void  stop() = 0;



    /**
      \brief Jumps the simulation to the specified instant
			
			Pauses the simulation and jumps to the specified instant
     */
    virtual void  jump_to(History::position p) = 0;


    /**
      \brief This methods allows to change the way the simulation progresses.

      If the input value is 0 (false), the simulation will advance a single time 
      step for each call to run().
      If the input value is 1 (true), the simulation will advance contiuosly, 
      waiting the time defined with set_timer() between each step, until all 
      processes have terminated, or some error happens. 
     */
    virtual void  set_mode(mode) = 0;

    /**
      \return The simulation advancement mode: 0 if step-to-step, 1 if
      continue.
     */
    virtual mode  get_mode() const = 0;

    /** 
      \return The curent simulation state.
      \see state
     */
    virtual state get_state() const = 0;

    /**
      \brief Setup the CPU policy to be used by the system.
     */
    virtual void set_policy(CPUPolicy*) = 0;

    /**
      \brief Setup the resource policy to be used by the system.
     */
    virtual void set_resource_policy(ResourcePolicy*) = 0;

    /**
      \return The CPU policy currently in use.
     */
    virtual CPUPolicy* get_policy() = 0;

    /**
      \return The resource policy currently in use.
     */
    virtual ResourcePolicy* get_resource_policy() = 0;

    /**
      \return A reference to the History associated with this simulation.
     */
    virtual History& get_history() = 0;

    /**
      \return A const reference to the History associated with this simulation.
     */
    virtual const History& get_history() const = 0;

    /**
     * Small kludge to avoid the need for declaration of ConcreteSimulation
     * by the calling code of Simulation::get_instance()
     */
    static Simulation& get_instance();
 
    /**
     \brief Tie a new observer to this simulation.

     \param observer The observer to attach.
     */
    virtual void  attach(SimulationObserver& observer);

    /**
     \brief Removed an attached observer from this simulation.

     \param observer The observer to detach.
     */
    virtual void  detach(const SimulationObserver& observer);

  protected:
    typedef std::vector<SimulationObserver*> RegisteredObservers;
    RegisteredObservers _observers;

    Simulation(); // Constructor

    void notify_change();

  private:
    /** \brief Enable/disable notifications to registered observers
     *
     * This is quite useful to disable momentarily notification while you
     * do a bunch of insertions and/or deletions in one go, in order to 
     * speed up things.
     *
     * \return The old value
     */
    bool  set_notify_enabled(bool enabled = true);

    bool _notify;
  }; //~ class Simulation

    /** \brief Disables notifications to Simulation during the life of this object
   *
   * This class is useful if you've to do a lot of sequential operations on
   * Simulation that would reset it / notify its observers. For example, when loading
   * from a file. In this case, create an object of this type on the stack.
   * The destructor will take care of re-enabling notifications.
   *
   * Locks can be taken recursively without fear.
   */
  class SG_DLLEXPORT Simulation::LockNotify
  {
  public:
    LockNotify(Simulation& simulation);
    ~LockNotify();

  private:
    Simulation&  _s;
    bool         _was_enabled;

    LockNotify(const LockNotify&);
    LockNotify& operator=(const LockNotify&);
  }; //~ class Simulation::LockNotify

}


#endif

