// src/backend/text_simulation.cc - 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#include <sgpemv2/global_preferences.hh>

#include <sgpemv2/string_utils.hh>

#include <sgpemv2/cpu_policies_gatekeeper.hh>
#include <sgpemv2/cpu_policy_manager.hh>
#include <sgpemv2/resource_policy.hh>
#include <sgpemv2/policy_parameters.hh>
#include <sgpemv2/history.hh>
#include <sgpemv2/simulation.hh>
#include <sgpemv2/serializers_gatekeeper.hh>
#include <sgpemv2/serializer.hh>
#include <sgpemv2/process.hh>
#include <sgpemv2/resource.hh>
#include <sgpemv2/resource_policies_gatekeeper.hh>
#include <sgpemv2/resource_policy_manager.hh>
#include <sgpemv2/thread.hh>
#include <sgpemv2/request.hh>
#include <sgpemv2/sub_request.hh>
#include <sgpemv2/ready_queue.hh>

#include <sgpemv2/templates/sequences.tcc>

#include "text_simulation.hh"

#include <glibmm/timer.h>

#include <cassert>
#include <ios>
#include <iomanip>
#include <sstream>


using namespace std;
using namespace sgpem;
using Glib::Thread;
using Glib::ustring;

#include <sgpemv2/templates/smartp.tcc>

namespace sgpem
{
  template <typename T>
  class CommandParameter
  {
  public:
    CommandParameter(const ustring& _description,
                     const T& _low_bound,
                     const T& _up_bound,
                     bool _required,
                     const T& _preset);

    CommandParameter(const PolicyParameters::Parameter<T>& pparam);

    ustring description;
    T low_bound;
    T up_bound;
    bool required;
    T preset;
    T value;
  };

  template <typename T>
  CommandParameter<T>::CommandParameter(const ustring& _description,
                                        const T& _low_bound,
                                        const T& _up_bound,
                                        bool _required,
                                        const T& _preset) :
      description(_description), low_bound(_low_bound),
      up_bound(_up_bound), required(_required), preset(_preset),
      value(_preset)
  {}

  template <typename T>
  CommandParameter<T>::CommandParameter(const PolicyParameters::Parameter<T>& pparam) :
      description(pparam.get_name()),
      low_bound(pparam.get_lower_bound()),
      up_bound(pparam.get_upper_bound()), required(pparam.is_required()),
      preset(pparam.get_default()), value(pparam.get_value())
  {}
}


TextSimulation::TextSimulation() :
    _saved(true)
{}

TextSimulation::~TextSimulation()
{}

bool
TextSimulation::check_arguments_num(const Tokens& arguments, unsigned int num)
{
  if (arguments.size() < num)
  {
    ostringstream oss;
    oss << _("ERROR: this command requires at least ") << num << _(" arguments\n");
    p_stderr(oss.str());

    return false;
  }
  else if (arguments.size() > num)
    p_stderr(_("WARNING: some arguments will be ignored\n"));

  return true;
}

bool
TextSimulation::unsaved_ask_confirm() const
{
  if (!_saved)
  {
    p_stderr(_("WARNING: Simulation was not recently saved. "
               "If you continue some changes to the simulation might be lost.\n"));

    for (bool bad_arg = true; bad_arg;)
    {
      p_stdout(_("Continue? [y/n] "));

      ustring buf = readline();

      Tokens tokens = tokenize(buf);

      if (tokens.size() == 1 && tokens[0].size() == 1)
      {
        if (tokens[0].lowercase() == _("n"))
          return false;
        else if (tokens[0].lowercase() == _("y"))
          bad_arg = false;
      }
    }
  }

  return true;
}

template <typename Container>
void
TextSimulation::show(const Container& entities)
{
  for (unsigned int i = 0; i < entities.size(); ++i)
  {
    ostringstream oss;
    oss << i + 1 << ". " << entities[i]->get_name() << endl;
    p_stdout(oss.str());
  }
}

// Specializations need to go explicitly inside the namespace. why?
namespace sgpem
{
  template <>
  void
  TextSimulation::show<vector<Request*> > (const vector<Request*>& entities)
  {
    for (unsigned int i = 0; i < entities.size(); ++i)
    {
      ostringstream oss;
      oss << i + 1 << ". instant: " << entities[i]->get_instant() << endl;
      p_stdout(oss.str());
    }
  }

  template <>
  void
  TextSimulation::show<vector<SubRequest*> > (const vector<SubRequest*>& entities)
  {
    for (unsigned int i = 0; i < entities.size(); ++i)
    {
      ostringstream oss;

      oss << i + 1 << ". resource: " << entities[i]->get_resource_key() << endl;
      p_stdout(oss.str());
    }
  }

  template <>
  void
  TextSimulation::show<map<int, Resource*> > (const map<int, Resource*>& entities)
  {
    typedef map<int, Resource*>::const_iterator ResourceIt;

    for (ResourceIt it = entities.begin(); it != entities.end(); ++it)
    {
      ostringstream oss;

      oss << it->first << ". " << it->second->get_name();
      oss << "[" << it->second->get_places() << "]" << endl;
      p_stdout(oss.str());
    }
  }

} //~ namespace sgpem

template <typename T>
void
TextSimulation::get_parameter(CommandParameter<T>& parameter)
{
  bool correct;

  do
  {
    correct = true;

    ostringstream buf;

    if (parameter.required)
      buf << "*";

    buf << parameter.description << " (range: [" << parameter.low_bound << ", " <<
    parameter.up_bound << "] current: " << parameter.value << ") : ";

    p_stdout(buf.str());

    ustring input = readline();

    T value;

    if (tokenize(input).size() > 0)
    {
      try
      {
        value = string_to<T>(input);

        if (value > parameter.up_bound || value < parameter.low_bound)
        {
          p_stderr(_("ERROR: Provided value is out of range\n"));
          correct = false;
        }
      }
      catch (domain_error e)
      {
        p_stderr(_("ERROR: Please provide a valid numeric value\n"));
        correct = false;
      }

      if (correct)
        parameter.value = value;
    }
    else if (parameter.required)
    {
      p_stderr(_("ERROR: This is a mandatory attribute; you MUST provide a valid value!\n"));

      correct = false;
    }
  }
  while (!correct);
}

// Specializations need to go explicitly inside the namespace. why?
namespace sgpem
{
  template <>
  void
  TextSimulation::get_parameter<ustring>(CommandParameter<ustring>& parameter)
  {
    bool loop = true;

    while (loop)
    {
      ustring buf;

      if (parameter.required)
        buf += "*";

      p_stdout(buf + parameter.description +
               " (current: \"" + parameter.value + "\") : ");

      buf = readline();

      Tokens tokens = tokenize(buf);

      if (tokens.size() == 0 && parameter.required)
        p_stderr(_("ERROR: This is a mandatory atribute; you MUST provide a valid value!\n"));
      else
      {
        parameter.value = buf;
        loop = false;
      }
    }
  }

  template <>
  void
  TextSimulation::get_parameter<bool>(CommandParameter<bool>& parameter)
  {
    bool correct;

    do
    {
      correct = true;

      ostringstream buf;

      if (parameter.required)
        buf << "*";

      buf << parameter.description <<
      " (current: " << boolalpha << parameter.value << ") : ";

      p_stdout(buf.str());

      ustring str = readline();

      Tokens tokens = tokenize(str);

      if (tokens.size() != 0)
      {
        try
        {
          parameter.value = string_to<bool>(str);
        }
        catch (domain_error e)
        {
          p_stderr(_("ERROR: Please provide a valid boolean value ('true' or 'false')\n"));
          correct = false;
        }
      }
      else if (parameter.required)
      {
        p_stderr(_("ERROR: This is a mandatory atribute; you MUST provide a valid value!\n"));
        correct = false;
      }
    }
    while (!correct);
  }
}

void
TextSimulation::on_run(const Tokens& arguments)
{
  check_arguments_num(arguments, 0);

  // Listen for updates only during scheduling
  Simulation::get_instance().attach(*this);

  try
  {
    Simulation& sim = Simulation::get_instance();
    switch(sim.get_mode())
      {
      case Simulation::mode_step_by_step:
        sim.run();
        break;
      case Simulation::mode_continuous:
        int interval = GlobalPreferences::get_instance().get_speed() * 1000;
        do
          {
            sim.run();
            Glib::usleep(interval);
          }
        while(sim.get_state() != Simulation::state_stopped);
        break;
      }
  }
  catch (const UserInterruptException& e)
  {
    p_stderr(_("ERROR: "));
    p_stderr(e.what());
    p_stderr(_("\nSimulation is now stopped\n"));
  }
  catch (const MalformedPolicyException& e)
  {
    p_stderr(_("ERROR: "));
    p_stderr(e.what());
    p_stderr(_("\nSimulation is now stopped, and "
               "the current policy will be deactivated\n"));
    try
    {
      Simulation::get_instance().set_policy(NULL);
    }
    catch(const CPUPolicyException& f)
    {
      // should never happen
      p_stderr(_("FATAL ERROR: unable to deactivate the policy: "));
      p_stderr(f.what());
      abort();
    }
  }
  catch (const NullPolicyException& e)
  {
    p_stderr(_("ERROR: "));
    p_stderr(e.what());
    p_stderr(_("\nSimulation is now stopped\n"));
  }
  catch (const CPUPolicyException& e)
  {
    p_stderr(_("UNKNOWN ERROR: "));
    p_stderr(e.what());
    p_stderr("\n");
  }

  Simulation::get_instance().detach(*this);
}

void
TextSimulation::on_pause(const Tokens& arguments)
{
  check_arguments_num(arguments, 0);

  Simulation::get_instance().pause();
}


void
TextSimulation::on_jumpto(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 1))
    return;

  // Listen for updates only during scheduling

  ustring _position = arguments[0];

  History::position p;

  try
  {
		int pos = string_to<int>(_position) + 1;
		if (pos < 0)
		{
			p_stderr(_("ERROR: provided instant is out of range.\n"));
			return;
		}
    p = static_cast<unsigned int>(pos);
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided instant is not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the instant is not within range.\n")); // does this make sense?
    return;
  }

  Simulation::get_instance().attach(*this);

  try
  {
    Simulation::get_instance().jump_to(p);
  }
  catch (const UserInterruptException& e)
  {
    p_stderr(_("ERROR: "));
    p_stderr(e.what());
    p_stderr(_("\nSimulation is now stopped\n"));
  }
  catch (const MalformedPolicyException& e)
  {
    p_stderr(_("ERROR: "));
    p_stderr(e.what());
    p_stderr(_("\nSimulation is now stopped, and "
               "the current policy will be deactivated\n"));
    try
    {
      Simulation::get_instance().set_policy(NULL);
    }
    catch(const CPUPolicyException& f)
    {
      // should never happen
      p_stderr(_("FATAL ERROR: unable to deactivate the policy: "));
      p_stderr(f.what());
      abort();
    }
  }
  catch (const NullPolicyException& e)
  {
    p_stderr(_("ERROR: "));
    p_stderr(e.what());
    p_stderr(_("\nSimulation is now stopped\n"));
  }
  catch (const CPUPolicyException& e)
  {
    p_stderr(_("UNKNOWN ERROR: "));
    p_stderr(e.what());
    p_stderr("\n");
  }

  Simulation::get_instance().detach(*this);
}



void
TextSimulation::on_stop(const Tokens& arguments)
{
  check_arguments_num(arguments, 0);

  Simulation::get_instance().stop();
}

template<typename PolicyType>
void
TextSimulation::configure_policy(PolicyType& policy)
{
  PolicyParameters& parameters = policy.get_parameters();

  typedef map<ustring, PolicyParameters::Parameter<int> > IntParams;
  typedef map<ustring, PolicyParameters::Parameter<float> > FloatParams;
  typedef map<ustring, PolicyParameters::Parameter<ustring> > StringParams;

  IntParams int_params = parameters.get_registered_int_parameters();
  FloatParams float_params = parameters.get_registered_float_parameters();
  StringParams string_params = parameters.get_registered_string_parameters();
  
  if(int_params.size() > 0 || float_params.size() > 0 || string_params.size() > 0)
  {
    p_stdout(_("Please provide a value for each attribute:\n"));
    p_stdout(_("Mandatory arguments are marked with an asterisk (*)\n\n"));
  }
  else
    p_stdout(_("Nothing to configure for this policy.\n"));

  if(int_params.size() > 0)
    p_stdout(_("Integer arguments:\n"));

  for (IntParams::iterator it = int_params.begin(); it != int_params.end();)
  {
    PolicyParameters::Parameter<int> &p = it->second;
    CommandParameter<int> cmd_p(p);

    get_parameter(cmd_p);

    parameters.set_int(p.get_name(), cmd_p.value);

    ++it;
  }

  // NOTE this piece code is a verbatim copy of the one above.
  // I tried solving this issue by using templates, but to make
  // it work will require adding to PolicyParameters a member template
  // method with 2 specializations...

  
  if(float_params.size() > 0)
    p_stdout(_("\nFloating-point arguments:\n"));

  for (FloatParams::iterator it = float_params.begin(); it != float_params.end();)
  {
    PolicyParameters::Parameter<float> &p = it->second;

    CommandParameter<float> cmd_p(p);

    get_parameter(cmd_p);

    parameters.set_float(p.get_name(), cmd_p.value);

    ++it;
  }

  if(string_params.size() > 0)
    p_stdout(_("\nString arguments:\n"));

  for (StringParams::iterator it = string_params.begin(); it != string_params.end();)
  {
    PolicyParameters::Parameter<ustring> &p = it->second;

    CommandParameter<ustring> cmd_p(p);

    get_parameter(cmd_p);

    parameters.set_string(p.get_name(), cmd_p.value);

    ++it;
  }

}

void
TextSimulation::on_configure(const Tokens& arguments)
{
  if(!check_arguments_num(arguments, 1))
    return;

  ustring what = arguments[0];

  if(what == "cpu-policy")
  {
    CPUPolicy* policy = Simulation::get_instance().get_policy();

    if (policy == NULL)
      p_stderr(_("ERROR: No CPU policy actually selected for the simulation\n"));
    else
     configure_policy(*policy); 
  }
  else if(what == "resource-policy")  
  {
    ResourcePolicy* policy = Simulation::get_instance().get_resource_policy();

    if (policy == NULL)
      p_stderr(_("ERROR: No resource policy actually selected for the simulation\n"));
    else
     configure_policy(*policy); 
  }
  else
    p_stderr(ustring(_("ERROR: Nothing to configure for ")) + what + "\n");
}

void
TextSimulation::on_help(const Tokens& arguments)
{
  ustring command;

  if (arguments.size() > 0)
  {
    command = arguments[0].uppercase();
    // print warning if necessary
    check_arguments_num(arguments, 1);
  }

  if (command.size() == 0)
    p_stdout(_("Avaiable commands:\nRUN\nSTOP\nPAUSE\n"
               "CONFIGURE\nHELP\nGET\nSET\nSHOW\nADD\n"
               "REMOVE\nSAVE\nLOAD\nQUIT\n\n"
               "HELP followed by a command name shows help about it.\n"
               "ex. `HELP RUN` shows help about the command RUN\n"));
  else if (command == "RUN")
    p_stdout(_("-- RUN COMMAND --\nStarts the simulation. It can be "
               "continuous or step-by-step depending on the mode configured with "
               "SET CONTINUOUS (default=true).\n\n"
               "The output of RUN is a snapshot of the state of the simulation at each "
               "instant.\n"
               "The instant 0 represents the initial state, "
               "during which no process is running. The scheduler "
               "activity begins at instant 1.\n"));
  else if (command == "STOP")
    p_stdout(_("-- STOP COMMAND --\nStops the simulation. The next call to RUN will "
               "bring the simulation to the first instant and start it.\n"));
  else if (command == "PAUSE")
    p_stdout(_("-- PAUSE COMMAND --\nPauses the simulation. The next call to RUN will "
               "continue it.\n"));
  else if (command == "JUMPTO")
    p_stdout(_("-- JUMPTO COMMAND --\nPauses the simulation and jumps to the specified instant.\n"));
  else if (command == "CONFIGURE")
    p_stdout(_("-- CONFIGURE COMMAND --\nConfigures a configurable entity.\n\n"
	       "Syntax: CONFIGURE <entity>\n"
	       "\twhere <entity> may be cpu-policy or resource-policy.\n"
               "This is currently the only way to control the behaviour of "
               "policies without modifying their source code.\n"));
  else if (command == "HELP")
    p_stdout(_("-- HELP COMMAND --\nThe help you're reading.\n"));
  else if (command == "GET")
    p_stdout(_("-- GET COMMAND --\nSyntax: GET <attr_name>\n"
               "\twhere <attr_name> may be simulation-tick or continuous.\n"));
  else if (command == "SET")
    p_stdout(_("-- SET COMMAND --\nSyntax: SET <attr_name> [=] <value>\n"
               "\twhere <attr_name> may be simulation-tick, continuous, cpu-policy or resource-policy.\n"));
  else if (command == "SHOW")
    p_stderr(_("-- SHOW COMMAND --\nDisplays the name of the entities (if available) "
               "and other informations prefixed by its numeric identifier.\n\n"
               "Syntax depends from entities being displayed:\n"
               "`SHOW processes|resources|cpu-policies|resource-policies`\n"
               "`SHOW threads <process_id>` with <process_id> being the numeric identifier of "
               "the parent process\n"
               "`SHOW requests <process_id> <thread_id>` with <thread_id> being the numeric "
               "identifier of the thread child of process identified by <process_id>\n"
               "`SHOW subrequests <process_id> <thread_id> <request_id>` where the numeric ids "
               "follow the same logic of the previous commands\n"));
  else if (command == "ADD")
    p_stderr(_("-- ADD COMMAND --\nAdds an entity by using a questionary-like approach.\n\n"
               "Syntax depends from entity being added:\n"
               "`ADD process|resource`\n"
               "`ADD thread <process_id>` with <process_id> being the numeric identifier of "
               "the parent process\n"
               "`ADD request <process_id> <thread_id>` with <thread_id> being the numeric "
               "identifier of the thread child of process identified by <process_id>\n"
               "`ADD subrequest <process_id> <thread_id> <request_id>` where the numeric ids "
               "follow the same logic of the previous commands\n"));
  else if (command == "REMOVE")
    p_stderr(_("-- REMOVE COMMAND --\nRemoves an entity.\n\n"
               "Syntax depends from entity being removed:\n"
               "`REMOVE process|resource <id>` where <id> is the process or resource identifier\n"
               "`REMOVE thread <process_id> <thread_id>` with <process_id> being the identifier of "
               "the parent process, and <thread_id> the id of the thread to be removed\n"
               "`REMOVE request <process_id> <thread_id> <request_id>` where the "
               "numeric ids follow the same logic of the previous commands\n"
               "`REMOVE subrequest <process_id> <thread_id> <request_id> <subrequest_id>` where the "
               "numeric ids follow the same logic of the previous commands\n"));
  else if (command == "SAVE")
    p_stderr(_("-- SAVE COMMAND --\nSaves the simulation.\n\n"
               "Syntax: SAVE <filename>\n"));
  else if (command == "LOAD")
    p_stderr(_("-- LOAD COMMAND --\nLoads the simulation.\n\n"
               "Syntax: LOAD <filename>\n"));
  else if (command == "QUIT")
    p_stderr(_("-- QUIT COMMAND --\nGently closes the program.\n"));
  else
    p_stderr(_("ERROR: Sorry, no help available for this command.\n"));
}

void
TextSimulation::on_quit(const Tokens& arguments)
{
  check_arguments_num(arguments, 0);

  if (!unsaved_ask_confirm())
    return;

  p_stdout(_("\nBye.\n\n"));

  // Exit the program with a correct return code.
  exit(0);
}

void
TextSimulation::on_get(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 1))
    return;

  ustring attr = arguments[0].uppercase();

  if (attr == "SIMULATION-TICK")
  {
    ostringstream oss;
    oss << "simulation-tick = " << GlobalPreferences::get_instance().get_speed() << "ms" << endl;
    p_stdout(oss.str());
  }
  else if (attr == "CONTINUOUS")
  {
    ostringstream oss;
    oss << "continuous = " << boolalpha << Simulation::get_instance().get_mode() << endl;
    p_stdout(oss.str());
  }
  else
    p_stderr(_("ERROR: invalid attribute name.\n"));
}

template <typename GatekeeperType>
typename GatekeeperType::Manager::Policy&
policy_from_index(const Glib::ustring& value) throw(domain_error)
{
  int index = string_to<int>(value) - 1;
      
  if (index < 0)
    throw domain_error("");

  typedef typename GatekeeperType::Managers ManagerVec;
  typedef typename GatekeeperType::Manager::Policies PolicyVec;
  typedef typename PolicyVec::iterator CPUPolicyIt;

  ManagerVec managers = GatekeeperType::get_instance().get_registered();

  for (typename ManagerVec::iterator mit = managers.begin(); mit != managers.end(); ++mit)
  {
    PolicyVec policies = (*mit)->get_avail_policies();
    for (CPUPolicyIt pit = policies.begin(); pit != policies.end(); ++pit)
    {
      if (index == 0)
	return *(*pit);

      --index;
    }
  }

  throw domain_error("");
}

void
TextSimulation::on_set(const Tokens& arguments)
{
  // handle the optional "=' (I knew that I was buying myself a problem when I
  // decided to support the assigment operator!)
  if (arguments.size() >= 3)
    check_arguments_num(arguments, 3);
  else if (!check_arguments_num(arguments, 2))
    return;

  ustring attr = arguments[0].uppercase();

  ustring value;

  if (arguments[1] == "=")
    value = arguments[2];
  else
    value = arguments[1];

  if (attr == "SIMULATION-TICK")
  {
    try
    {
      int timer = string_to<int>(value);

      if (timer < 0)
        throw domain_error("");

      GlobalPreferences::get_instance().set_speed(timer);
    }
    catch (domain_error e)
    {
      p_stderr(_("ERROR: you must provide a valid unsigned integer value\n"));
    }

  }
  else if (attr == "CPU-POLICY")
  {
    try
    {
      CPUPolicy& p = policy_from_index<CPUPoliciesGatekeeper>(value);

      Simulation::get_instance().set_policy(&p);
      p_stdout("\n");
      p_stdout(p.get_name() + _(" scheduling policy selected.\n"));

      //ResourcePolicyManager & rpm = *ResourcePoliciesGatekeeper::get_instance().get_registered().at(0);
      //Simulation::get_instance().set_resource_policy(rpm.get_avail_policies().at(0));
    }
    catch (domain_error e)
    {
      p_stderr(_("ERROR: invalid unsigned integer or not a valid scheduling policy index\n"));
    }
    catch (const CPUPolicyException& e)
    {
      p_stderr(_("ERROR: "));
      p_stderr(e.what());
      p_stderr("\n");
    }
      
  }
  else if (attr == "RESOURCE-POLICY")
  {
    try
    {
      ResourcePolicy& p = policy_from_index<ResourcePoliciesGatekeeper>(value);

      Simulation::get_instance().set_resource_policy(&p);
      p_stdout("\n");
      p_stdout(p.get_name() + _(" resource policy selected.\n"));
    }
    catch (domain_error e)
    {
      p_stderr(_("ERROR: invalid unsigned integer or not a valid resource policy index\n"));
    }
    catch (const CPUPolicyException& e)
    {
      p_stderr(_("ERROR: "));
      p_stderr(e.what());
      p_stderr("\n");
    }
  }
  else if (attr == "CONTINUOUS")
  {
    try
    {
      if(string_to<bool>(value) == true)
        Simulation::get_instance().set_mode(Simulation::mode_continuous);
      else
        Simulation::get_instance().set_mode(Simulation::mode_step_by_step);
    }
    catch (domain_error e)
    {
      p_stderr(_("ERROR: you must provide a valid boolean value ('true' or 'false')\n"));
    }
  }
  else
    p_stderr(_("ERROR: invalid attribute name.\n"));
}

void
TextSimulation::on_show(const Tokens& arguments)
{
  if (arguments.size() < 1)
  {
    //print error
    check_arguments_num(arguments, 1);
    return;
  }

  //make a local copy which we'll probably modify
  Tokens args = arguments;

  ustring entities = args[0].uppercase();
  args.erase(args.begin());

  typedef void (TextSimulation::*f_ptr)(const Tokens&);
  map<ustring, f_ptr> entities_handlers;

  entities_handlers["PROCESSES"] = &TextSimulation::on_show_processes;
  entities_handlers["RESOURCES"] = &TextSimulation::on_show_resources;
  entities_handlers["THREADS"] = &TextSimulation::on_show_threads;
  entities_handlers["REQUESTS"] = &TextSimulation::on_show_requests;
  entities_handlers["SUBREQUESTS"] = &TextSimulation::on_show_subrequests;
  entities_handlers["CPU-POLICIES"] = &TextSimulation::on_show_cpu_policies;
  entities_handlers["RESOURCE-POLICIES"] = &TextSimulation::on_show_resource_policies;

  if (entities_handlers.find(entities) == entities_handlers.end())
    p_stderr(_("ERROR: invalid argument\n"));
  else
    (this->*(entities_handlers[entities]))(args);
}

void
TextSimulation::on_show_processes(const Tokens& arguments)
{
  check_arguments_num(arguments, 0);

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();

  show(processes);
}

void
TextSimulation::on_show_resources(const Tokens& arguments)
{
  check_arguments_num(arguments, 0);

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Resources& resources = env.get_resources();

  show(resources);
}

void
TextSimulation::on_show_threads(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 1))
    return;

  ustring process = arguments[0];

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();

  vector<Thread*> threads;

  try
  {
    int pid = string_to<int>(process) - 1;
    threads = processes.at(pid)->get_threads();
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided process identifier is not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: this process identifier does not belong to an existing process\n"));
    return;
  }

  show(threads);
}


void
TextSimulation::on_show_requests(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 2))
    return;

  ustring process = arguments[0];
  ustring thread = arguments[1];

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();
  vector<Request*> requests;

  try
  {
    int pid = string_to<int>(process) - 1;
    int tid = string_to<int>(thread) - 1;

    vector<Thread*> threads = processes.at(pid)->get_threads();
    requests = threads.at(tid)->get_requests();
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
    return;
  }

  show(requests);
}

void
TextSimulation::on_show_subrequests(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 3))
    return;

  ustring process = arguments[0];
  ustring thread = arguments[1];
  ustring request = arguments[2];

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();
  vector<SubRequest*> subrequests;

  try
  {
    int pid = string_to<int>(process) - 1;
    int tid = string_to<int>(thread) - 1;
    int rid = string_to<int>(request) - 1;

    vector<Thread*> threads = processes.at(pid)->get_threads();
    vector<Request*> requests = threads.at(tid)->get_requests();
    subrequests = requests.at(rid)->get_subrequests();
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
    return;
  }

  show(subrequests);
}

template <typename GatekeeperType>
void
TextSimulation::show_policies(const Tokens& arguments)
{
  typedef typename GatekeeperType::Managers ManagerVec;
  typedef typename GatekeeperType::Manager::Policies PolicyVec;
  typedef typename PolicyVec::iterator PolicyIt;

  check_arguments_num(arguments, 0);

  ManagerVec managers = GatekeeperType::get_instance().get_registered();

  unsigned int index = 1;
  for (typename ManagerVec::iterator mit = managers.begin(); mit != managers.end(); ++mit)
  {
    PolicyVec policies = (*mit)->get_avail_policies();
    for (PolicyIt pit = policies.begin(); pit != policies.end(); ++pit)
    {
      ostringstream oss;
      oss << endl << index << ". " << (*pit)->get_name() << endl;
      oss << "\t" << (*pit)->get_description() << endl;

      p_stdout(oss.str());
      ++index;
    }
  }
}

void
TextSimulation::on_show_cpu_policies(const Tokens& arguments)
{
  show_policies<CPUPoliciesGatekeeper>(arguments);
}

void
TextSimulation::on_show_resource_policies(const Tokens& arguments)
{
  show_policies<ResourcePoliciesGatekeeper>(arguments);
}

void
TextSimulation::on_add(const Tokens& arguments)
{
  if (arguments.size() < 1)
  {
    //print error
    check_arguments_num(arguments, 1);
    return;
  }

  if (Simulation::get_instance().get_state() != Simulation::state_stopped)
  {
    p_stderr(_("WARNING: Simulation is not stopped, it will be automatically stopped\n"));
    Simulation::get_instance().stop();
  }

  //make a local copy which we'll probably modify
  Tokens args = arguments;

  ustring entity = args[0].uppercase();
  args.erase(args.begin());

  typedef void (TextSimulation::*f_ptr)(const Tokens&);
  map<ustring, f_ptr> entity_handlers;

  entity_handlers["PROCESS"] = &TextSimulation::on_add_process;
  entity_handlers["RESOURCE"] = &TextSimulation::on_add_resource;
  entity_handlers["THREAD"] = &TextSimulation::on_add_thread;
  entity_handlers["REQUEST"] = &TextSimulation::on_add_request;
  entity_handlers["SUBREQUEST"] = &TextSimulation::on_add_subrequest;

  if (entity_handlers.find(entity) == entity_handlers.end())
    p_stderr(_("ERROR: invalid argument\n"));
  else
    (this->*(entity_handlers[entity]))(args);
}

void
TextSimulation::on_add_process(const Tokens& arguments)
{
  check_arguments_num(arguments, 0);

  CommandParameter<ustring> name(_("name"), "", "", false, "");
  CommandParameter<int> arrival_time(_("arrival time"), 0, INT_MAX, false, 0);
  CommandParameter<int> base_priority(_("priority"), 0, INT_MAX, false, 0);

  get_parameter(name);
  get_parameter(arrival_time);
  get_parameter(base_priority);

  History& h = Simulation::get_instance().get_history();

  h.add_process(name.value, arrival_time.value, base_priority.value);
}

void
TextSimulation::on_add_resource(const Tokens& arguments)
{
  check_arguments_num(arguments, 0);

  CommandParameter<ustring> name(_("name"), "", "", false, "");
  CommandParameter<int> places(_("places"), 0, INT_MAX, false, 1);

  get_parameter(name);
  get_parameter(places);

  History& h = Simulation::get_instance().get_history();

  h.add_resource(name.value, false, places.value);
}

void
TextSimulation::on_add_thread(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 1))
    return;

  ustring process = arguments[0];

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();

  Process* p;

  try
  {
    p = processes.at(string_to<int>(process) - 1);
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
    return;
  }

  CommandParameter<ustring> name(_("name"), "", "", false, "");
  CommandParameter<int> cpu_time(_("cpu time"), 1, INT_MAX, true, 0);
  CommandParameter<int> arrival_time(_("arrival time"), 0, INT_MAX, false, 0);
  CommandParameter<int> base_priority(_("base priority"), 0, INT_MAX, false, 0);

  get_parameter(name);
  get_parameter(cpu_time);
  get_parameter(arrival_time);
  get_parameter(base_priority);

  History& h = Simulation::get_instance().get_history();

  h.add_thread(name.value, *p, cpu_time.value, arrival_time.value,
               base_priority.value);
}

void
TextSimulation::on_add_request(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 2))
    return;

  ustring process = arguments[0];
  ustring thread = arguments[1];

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();

  Thread* t;

  try
  {
    int pid = string_to<int>(process) - 1;
    int tid = string_to<int>(thread) - 1;

    vector<Thread*> threads = processes.at(pid)->get_threads();
    t = threads.at(tid);
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
    return;
  }

  CommandParameter<int> instant(_("instant"), 0, INT_MAX, true, 0);

  get_parameter(instant);

  History& h = Simulation::get_instance().get_history();

  h.add_request(*t, instant.value);
}

void
TextSimulation::on_add_subrequest(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 3))
    return;

  ustring process = arguments[0];
  ustring thread = arguments[1];
  ustring request = arguments[2];

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();

  Request* r;

  try
  {
    int pid = string_to<int>(process) - 1;
    int tid = string_to<int>(thread) - 1;
    int rid = string_to<int>(request) - 1;

    vector<Thread*> threads = processes.at(pid)->get_threads();
    vector<Request*> requests = threads.at(tid)->get_requests();
    r = requests.at(rid);

  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
    return;
  }

  CommandParameter<int> resource_key(_("resource key"), 0, INT_MAX, true, 0);
  CommandParameter<int> duration(_("duration"), 0, INT_MAX, true, 0);

  const Environment::Resources& resources = env.get_resources();

  do
  {
    get_parameter(resource_key);

    if (resources.find(resource_key.value) == resources.end())
      p_stderr(_("ERROR: invalid resource identifier\n"));
  }
  while (resources.find(resource_key.value) == resources.end());

  get_parameter(duration);

  History& h = Simulation::get_instance().get_history();

  h.add_subrequest(*r, resource_key.value, duration.value);
}

void
TextSimulation::on_remove(const Tokens& arguments)
{
  if (arguments.size() < 1)
  {
    //print error
    check_arguments_num(arguments, 1);
    return;
  }

  if (Simulation::get_instance().get_state() != Simulation::state_stopped)
    p_stderr(_("WARNING: Simulation is not stopped, it will be automatically stopped"));

  //make a local copy which we'll probably modify
  Tokens args = arguments;

  ustring entity = args[0].uppercase();
  args.erase(args.begin());

  typedef void (TextSimulation::*f_ptr)(const Tokens&);
  map<ustring, f_ptr> entity_handlers;

  entity_handlers["PROCESS"] = &TextSimulation::on_remove_process;
  entity_handlers["RESOURCE"] = &TextSimulation::on_remove_resource;
  entity_handlers["THREAD"] = &TextSimulation::on_remove_thread;
  entity_handlers["REQUEST"] = &TextSimulation::on_remove_request;
  entity_handlers["SUBREQUEST"] = &TextSimulation::on_remove_subrequest;

  if (entity_handlers.find(entity) == entity_handlers.end())
    p_stderr(_("ERROR: invalid argument\n"));
  else
    (this->*(entity_handlers[entity]))(args);
}

void
TextSimulation::on_remove_process(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 1))
    return;

  ustring process = arguments[0];

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();

  Process* p;

  try
  {
    p = processes.at(string_to<int>(process) - 1);
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
    return;
  }

  History& h = Simulation::get_instance().get_history();
  h.remove(*p);
}

void
TextSimulation::on_remove_resource(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 1))
    return;

  ustring resource = arguments[0];

  History& h = Simulation::get_instance().get_history();

  History::resource_key_t rid;

  try
  {
    rid = string_to<int>(resource);

    const Environment::Resources& resources =
      h.get_environment_at(0).get_resources();

    if (resources.find(rid) == resources.end())
      throw out_of_range(_("invalid resource id"));
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
    return;
  }

  h.remove(rid);
}

void
TextSimulation::on_remove_thread(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 2))
    return;

  ustring process = arguments[0];
  ustring thread = arguments[1];

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();

  Thread* t;

  try
  {
    int pid = string_to<int>(process) - 1;
    int tid = string_to<int>(thread) - 1;

    vector<Thread*> threads = processes.at(pid)->get_threads();
    t = threads.at(tid);
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
    return;
  }

  History& h = Simulation::get_instance().get_history();
  h.remove(*t);
}

void
TextSimulation::on_remove_request(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 3))
    return;

  ustring process = arguments[0];
  ustring thread = arguments[1];
  ustring request = arguments[2];

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();

  Request* r;

  try
  {
    int pid = string_to<int>(process) - 1;
    int tid = string_to<int>(thread) - 1;
    int rid = string_to<int>(request) - 1;

    vector<Thread*> threads = processes.at(pid)->get_threads();
    vector<Request*> requests = threads.at(tid)->get_requests();
    r = requests.at(rid);
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
    return;
  }

  History& h = Simulation::get_instance().get_history();
  h.remove(*r);
}

void
TextSimulation::on_remove_subrequest(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 4))
    return;

  ustring process = arguments[0];
  ustring thread = arguments[1];
  ustring request = arguments[2];
  ustring subrequest = arguments[3];

  const Environment& env = Simulation::get_instance().get_history().get_environment_at(0);
  const Environment::Processes& processes = env.get_processes();

  SubRequest* r;

  try
  {
    int pid = string_to<int>(process) - 1;
    int tid = string_to<int>(thread) - 1;
    int rid = string_to<int>(request) - 1;
    int srid = string_to<int>(subrequest) - 1;

    vector<Thread*> threads = processes.at(pid)->get_threads();
    vector<Request*> requests = threads.at(tid)->get_requests();
    vector<SubRequest*> subrequests = requests.at(rid)->get_subrequests();
    r = subrequests.at(srid);
  }
  catch (domain_error e)
  {
    p_stderr(_("ERROR: provided identifier(s) not a valid integer\n"));
    return;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: the identifier(s) do not belong to an existing entity\n"));
    return;
  }

  History& h = Simulation::get_instance().get_history();
  h.remove(*r);
}

void
TextSimulation::on_save(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 1))
    return;

  ustring filename = arguments[0];

  try
  {
    vector<Serializer*> serializers =
      SerializersGatekeeper::get_instance().get_registered();

    // FIXME using the first serializer available, this
    // will need to be changed when multiple serializers will
    // be made available
    Serializer& serializer = *serializers.at(0);

    const History& history = Simulation::get_instance().get_history();

    serializer.save_snapshot(filename, history);

    _saved = true;
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: No registered serializer available\n"));
  }
  catch (SerializerError e)
  {
    string msg = _("ERROR: ");

    p_stderr(msg + e.what() + "\n");
  }
}

void
TextSimulation::on_load(const Tokens& arguments)
{
  if (!check_arguments_num(arguments, 1))
    return;

  ustring filename = arguments[0];

  if (!unsaved_ask_confirm())
    return;

  try
  {
    vector<Serializer*> serializers =
      SerializersGatekeeper::get_instance().get_registered();

    // FIXME using the first serializer available, this
    // will need to be changed when multiple serializers will
    // be made available
    Serializer& serializer = *serializers.at(0);

    History& history = Simulation::get_instance().get_history();

    serializer.restore_snapshot(filename, history);
  }
  catch (out_of_range e)
  {
    p_stderr(_("ERROR: No registered serializer available\n"));
  }
  catch (SerializerError e)
  {
    string msg = _("ERROR: ");

    p_stderr(msg + e.what() + "\n");
  }
}

void
TextSimulation::p_stdout(const ustring& str)
{
  cout << str;
}

void
TextSimulation::p_stderr(const ustring& str)
{
  cerr << str;
}

ustring
TextSimulation::readline()
{
  // TODO throw an exception when the stream returned by
  // getline() has the fail, bad or eof bit set, because
  // in this case the application should quit
  string str;
  getline(cin, str);
  return str;
}

void
TextSimulation::parse_command(TextSimulation& sim, const ustring& str)
{
  typedef void (TextSimulation::*f_ptr)(const Tokens&);
  map<ustring, f_ptr> command_handlers;

  command_handlers["RUN"] = &TextSimulation::on_run;
  command_handlers["STOP"] = &TextSimulation::on_stop;
  command_handlers["PAUSE"] = &TextSimulation::on_pause;
  command_handlers["JUMPTO"] = &TextSimulation::on_jumpto;
  command_handlers["CONFIGURE"] = &TextSimulation::on_configure;
  command_handlers["HELP"] = &TextSimulation::on_help;
  command_handlers["GET"] = &TextSimulation::on_get;
  command_handlers["SET"] = &TextSimulation::on_set;
  command_handlers["SHOW"] = &TextSimulation::on_show;
  command_handlers["ADD"] = &TextSimulation::on_add;
  command_handlers["REMOVE"] = &TextSimulation::on_remove;
  command_handlers["SAVE"] = &TextSimulation::on_save;
  command_handlers["LOAD"] = &TextSimulation::on_load;
  command_handlers["QUIT"] = &TextSimulation::on_quit;

  Tokens arguments = tokenize(str);

  // Ignore empty lines
  if (arguments.size() == 0)
    return;
  
  // Allow also comments into batch scripts (w/ echo):
  if(arguments[0].at(0) == '#')
  {
    cout << str;
    return;
  }

  ustring key = arguments[0].uppercase();

  if (command_handlers.find(key) == command_handlers.end())
  {
    p_stderr(_("ERROR: command not supported\n"));
    return;
  }

  arguments.erase(arguments.begin());

  (sim.*(command_handlers[key]))(arguments);

  if (key == "ADD" || key == "REMOVE")
    sim._saved = false;
}

static ostream&
operator<<(ostream& os, Schedulable::state state)
{
  switch (state)
  {
  case Schedulable::state_running:
    os << _(">> RUNNING <<");
    break;
  case Schedulable::state_ready:
    os << _("READY");
    break;
  case Schedulable::state_blocked:
    os << _("BLOCKED");
    break;
  case Schedulable::state_future:
    os << _("FUTURE");
    break;
  case Schedulable::state_terminated:
    os << _("TERMINATED");
    break;
  default:
    os.setstate(ios_base::failbit);
  }

  return os;
}

static ostream&
operator<<(ostream& os, Request::state state)
{
  switch (state)
  {
  case Request::state_unallocable:
    os << _("UNALLOCABLE");
    break;
  case Request::state_allocated:
    os << _("ALLOCATED");
    break;
  case Request::state_future:
    os << _("FUTURE");
    break;
  case Request::state_exhausted:
    os << _("EXHAUSTED");
    break;
  case Request::state_allocable:
    os << _("ALLOCABLE");
    break;
  default:
    os.setstate(ios_base::failbit);
  }

  return os;
}

void
TextSimulation::update(const Simulation& changed_simulation)
{
  ostringstream oss;
  int printed_instant;
  static const std::string tab = "   ";


  // Print header for each instant:
  printed_instant = static_cast<int>(changed_simulation.get_history().get_front()) - 1;

  oss << endl << ">>>> " << printed_instant;


  // Print ready queue
  oss << endl << _("READY QUEUE: { ");

  const Environment& env = changed_simulation.get_history().get_last_environment();
  const ReadyQueue& q = env.get_sorted_queue();

  for (unsigned int i = 0; i < q.size(); ++i)
  {
    const Thread& t = q.get_item_at(i);
    oss << t.get_name() + _(" ~ ");
  }

  oss << _("}") << endl;


  // Flush buffer to screen
  p_stdout(oss.str());
  oss.str(string());


  const Environment::Resources& resources = env.get_resources();
  const Environment::Processes& processes = env.get_processes();
  typedef Environment::Resources::const_iterator ResourceIt;


  // Write the queue of requests for each resource
  oss << _("RESOURCES:") << endl;
  for (ResourceIt it = resources.begin(); it != resources.end(); ++it)
  {
    const Resource& r = *it->second;
    Environment::resource_key_t key = it->first;

    oss << right << setw(3) << key << left << ". " << r.get_name() << _(", with ");
    oss << r.get_places() << _(" places") << endl;

    const Environment::SubRequestQueue& req_queue =
      env.get_request_queue(it->first);

    oss << setw(tab.size()*3) << ' ' << _("queue: { ");

    for (unsigned int i = 0; i < req_queue.size(); ++i)
    {
      if (i == r.get_places())
        oss << " || ";
      else
        if (i != 0)
          oss << " ~ ";

      if (req_queue[i]->get_state() == Request::state_allocated)
        oss << "[" << req_queue[i]->get_request().get_thread().get_name() << "]";
      else
        oss << req_queue[i]->get_request().get_thread().get_name();
    }

    oss << _(" }") << endl;
  }

  // Flush buffer to screen
  p_stdout(oss.str());
  oss.str(string());

 
  // Set new format fillers
  static const unsigned int fill0 = 25;
  static const unsigned int fill1 = 15;

  // Minimum fill0 for the usage we make of it below:
  assert(fill0 > tab.size()*2 + 6);

	oss << endl;

  oss << left;
  oss << setw(fill0) << _("PROCESSES:");
	oss << right;
  oss << setw(fill1) << _("state")
      << setw(fill1) << _("arrival") 
      << setw(fill1) << _("requiring") 
      << setw(fill1) << _("elapsed") 
      << setw(fill1) << _("priority") 
      << setw(fill1) << _("resource_id") 
      << endl;

  for (unsigned int pi = 0; pi < processes.size(); ++pi)
  {
    Process& p = *processes[pi];
    oss << setw(2) << right << (pi + 1) << left << ". " 
        << setw(fill0 - 4) << p.get_name().substr(0, fill0 - 5);
    oss << right;
    oss << setw(fill1) << p.get_state();
    oss << setw(fill1) << p.get_arrival_time();
    oss << setw(fill1) << p.get_total_cpu_time();
    oss << setw(fill1) << p.get_elapsed_time();
    oss << setw(fill1) << p.get_current_priority();
    oss << endl;

    p_stdout(oss.str());
    oss.str(string());

    vector<Thread*> threads = p.get_threads();

    for (unsigned int ti = 0; ti < threads.size(); ++ti)
    {
      Thread& t = *threads[ti];

      oss << right << setw(tab.size() + 2) << ti + 1 << left << ". " 
          << setw(fill0 - 4 - tab.size()) << t.get_name().substr(0, fill0 - 5 - tab.size());
      oss << right;
      oss << setw(fill1) << t.get_state();
      oss << setw(fill1) << t.get_arrival_time();
      oss << setw(fill1) << t.get_total_cpu_time();
      oss << setw(fill1) << t.get_elapsed_time();
      oss << setw(fill1) << t.get_current_priority();
      
      // Temporary debug code. Remove me when done.
      // oss << setw(fill1 / 2) << t.get_last_acquisition();
      // oss << setw(fill1 / 2) << t.get_last_release();

      oss << endl;

      p_stdout(oss.str());
      oss.str(string());

      vector<Request*> requests = t.get_requests();

      for (unsigned int ri = 0; ri < requests.size(); ++ri)
      {
        Request& r = *requests[ri];

        vector<SubRequest*> subrequests = r.get_subrequests();

        for (unsigned int sri = 0; sri < subrequests.size(); ++sri)
        {
          SubRequest& sr = *subrequests[sri];
          ResourceIt point = resources.find(sr.get_resource_key());

					oss << setw(2 + tab.size()*2) << ri + 1 << left << "." << setw(2) << sri + 1
              << setw(fill0 - 5 - tab.size()*2) << (point->second)->get_name().substr(0, fill0 - 6 - tab.size()*2);
          oss << right;
          oss << setw(fill1) << sr.get_state();
					oss << setw(fill1) << r.get_instant();
      		oss << setw(fill1) << sr.get_length();
		      oss << setw(fill1) << sr.get_length() - sr.get_remaining_time();
          oss << setw(fill1*2) << sr.get_resource_key();
          oss << endl;

          p_stdout(oss.str());
          oss.str(string());
        }
      }

    }
  }
}

