// src/backend/sgpemv2/history.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 HISTORY_HH
#define HISTORY_HH 1

#include <sgpemv2/sgpemv2-visibility.hh>

#include <sgpemv2/environment.hh>

#include <glibmm/ustring.h>

#include <stdexcept>
#include <utility>
#include <vector>


namespace sgpem
{
  /** \brief Manages the history of the simulation

  Manages the history of the simulation from the instant 0 to the current time,
  i.e. permits to know the state of each schedulable object inside this time interval.
  In particoular it's possible to know which entity was running at a precise moment.

  In a future iteration it will be possible to revert the entire simulation to a state
  present in the history ("undo operation")
  */
  class History;

  // Forward declarations:
  class HistoryObserver;
  class Resource;
  class Process;
  class Thread;
  class Request;
  class SubRequest;

  class SG_DLLEXPORT History
  {
  public:
    // Forward declaration
    class LockNotify;
    friend class History::LockNotify;

    typedef unsigned int                     size_t;
    typedef unsigned int                     time_t;
    typedef unsigned int                     position;
    typedef int                              prio_t;

    typedef Environment::resource_key_t      resource_key_t;
    typedef const std::pair<resource_key_t, Resource*>  ResourcePair;

    History();
    virtual ~History() = 0;

    virtual       size_t       get_size() const = 0;
    virtual const Environment& get_last_environment() const = 0;
    virtual const Environment& get_environment_at(position index) const = 0;

    /**
    \brief Removes a Resource from the whole History.
    This normally means that users themselves have asked for a particular
    ::Resource to be removed by the whole simulation they're editing.
    Before removal, the History should always be reset().

    \param resource_key The key of the resource to remove.
    */
    virtual void  remove(resource_key_t resource_key)  = 0;

    /**
    \brief Removes a Process from the whole History.
    This normally means that users themselves have asked for a
    particular ::Process to be removed by the whole simulation they're editing.

    \param process The process to remove.
    */
    virtual void  remove(Process&    process)          = 0;

    /**
    \brief Removes a thread from the whole History.
    This normally means that users themselves have asked for a
    particular ::Thread to be removed by the whole simulation they're editing.

    \param thread The thread to remove.
    */
    virtual void  remove(Thread&     thread)           = 0;

    /**
    \brief Removes a request from the whole History.
    This normally means that users themselves have asked for a
    particular ::Request to be removed by the whole simulation
    they're editing.

    \param request The request to remove.
    */
    virtual void  remove(Request&    request)          = 0;

    /**
    \brief Removes a subrequest from the whole History.
    This normally means that users themselves have asked for a
    particular ::SubRequest to be removed by the whole simulation
    they're editing.

    \param subrequest The subrequest to remove.
    */
    virtual void  remove(SubRequest& subrequest)       = 0;

    /**
      \brief Clears the whole history
     */
    virtual void clear() = 0;

    /**
      \brief Add a Resource to the simulation.

      \param name The name of the new resource.
      \param preemptable True if the resource is preemptable.
      \param places Number of places of the resource.
      \param availability The instant from wich the resource is available.
      \return a pair with unique key and pointer to the resource created.
     */
    virtual ResourcePair add_resource(const Glib::ustring& name,
                                      bool   preemptable  = false,
                                      size_t places       = 1,
                                      size_t availability = 0) = 0;

    /**
      \brief Change data of an existing resource.

      \param resource Reference tot the editing resource.
      \param name The name of the new resource.
      \param preemptable True if the resource is preemptable.
      \param places Number of places of the resource.
      \param availability The instant from wich the resource is available.
     */
    virtual void        edit_resource(Resource& resource,
                                      const Glib::ustring& name,
                                      bool   preemptable  = false,
                                      size_t places       = 1,
                                      size_t availability = 0) = 0;

    /**
      \brief Add a Process to the simulation.

      \param name The name of the new process.
      \param arrival_time Instant when the process starts.
      \param base_priority Process priority at start up time
        (scheduler can change it during execution).
      \return The newly created process.
     */
    virtual Process&     add_process(const Glib::ustring& name,
                                     time_t arrival_time,
                                     prio_t base_priority = 0) = 0;

    /**
      \brief Edit an exixting Process in the simulation.

      \param process Reference to the editing process.
      \param name The name of the new process.
      \param arrival_time Instant when the process starts.
      \param base_priority Process priority at start up time
        (scheduler can change it during execution).
     */
    virtual void        edit_process(Process& process,
                                     const Glib::ustring& name,
                                     time_t arrival_time,
                                     prio_t base_priority = 0) = 0;


    /**
      \brief Add a Thread to an exixting process.

      \param name The name of the new thread.
      \param parent Reference to the process wich this thread belongs to.
      \param cpu_time Total time requested by thread complete execution.
      \param arrival_time Instant when the thread starts.
      \param base_priority Thread priority at start up time
        (scheduler can change it during execution).
      \return The newly created thread.
     */
    virtual Thread&      add_thread(const Glib::ustring& name,
                                    Process& parent,
                                    time_t cpu_time,
                                    time_t arrival_time = 0,
                                    prio_t base_priority = 0) = 0;

    /**
      \brief Edit an exixting Thread in the simulation.

      \param thread Reference to the editing thread.
      \param name New name for the thread.
      \param cpu_time Total time requested by thread complete execution.
      \param arrival_time Instant when the thread starts.
      \param base_priority Thread priority at start up time
        (scheduler can change it during execution).
     */
    virtual void         edit_thread(Thread& thread,
                                     const Glib::ustring& name,
                                     time_t cpu_time,
                                     time_t arrival_time = 0,
                                     prio_t base_priority = 0) = 0;

    /**
      \brief Add a Request to an existing thread.

      \param owner Reference to the requiring thread.
      \param instant When the request arrives.
      \return The newly created request.
     */
    virtual Request&     add_request(Thread& owner,
                                     time_t instant) = 0;

    /**
      \brief Edit a Request.

      \param request Reference to editing request.
      \param instant When the request arrives.
     */
    virtual void         edit_request(Request& request,
                                      time_t instant) = 0;


    /**
      \brief Add a SubRequest to an existing Request.

      \param owner Reference to the requiring thread.
      \param resource_key Key of the requested resource.
      \param duration Total time of request usage.
      \return The newly created subrequest.
     */
    virtual SubRequest&  add_subrequest(Request& request,
                                        resource_key_t resource_key,
                                        time_t  duration) = 0;

    /**
      \brief Edit an existing SubRequest.

      \param subrequest Reference of the request to edit.
      \param resource_key Key of the requested resource.
      \param duration Total time of request usage.
     */
    virtual void         edit_subrequest(SubRequest& subrequest,
                                        resource_key_t resource_key,
                                        time_t  duration) = 0;
    /**
     \return actual instant (current time in simulation)
     */
    virtual position get_front() const;

    /**
     */
    virtual bool     is_sealed() const = 0;

    /**
    \brief Attach a new observer object for this History.
    When someone wants to be notified when this object changes,
    it will register itself to using this method. \b TODO: in next milestones,
    pass a bitmap to this method so that you can specify exactly for what you
    want to be notified, e.g. only when resources change but not when requests
    do, etc.
    */
    virtual void  attach(HistoryObserver& observer);

    /**
    \brief Detach an observer object for this History.
    */
    virtual void  detach(const HistoryObserver& observer);

    /**
    \brief Brings History to initial state.
    */
    virtual void reset() = 0;

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

    /**
    \brief Notify all registered ::HistoryObserver that the History has changed.
    Calls HistoryObserver.update(this) for each registered observer.
    */
    void notify_change();

        position _front;

  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);

    /**
     \brief Flag indicating notification to observers enabled/disbled.
     */
    bool _notify;

  }; //~ class History

  /** \brief Disables notifications to History during the life of this object
   *
   * This class is useful if you've to do a lot of sequential operations on
   * History 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 History::LockNotify
  {
  public:
    LockNotify(History& history);
    ~LockNotify();

  private:
    History&  _h;
    bool      _was_enabled;

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


}//~ namespace sgpem

#endif //~ HISTORY_HH
