// src/backend/concrete_environment.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 "concrete_environment.hh"
#include "dynamic_process.hh"
#include "dynamic_resource.hh"
#include "dynamic_sub_request.hh"
#include <sgpemv2/sub_request.hh>
#include <sgpemv2/thread.hh>

#include <sgpemv2/templates/deletor.tcc>
#include <sgpemv2/templates/sequences.tcc>

#include <algorithm>
#include <cassert>
#include <functional>
#include <iterator>

using namespace sgpem;
using namespace std;

ConcreteEnvironment::ConcreteEnvironment()
{
  // Nothing to do here. Really.
}


ConcreteEnvironment::ConcreteEnvironment(const ConcreteEnvironment& ce) :
    Environment(ce), _resources(ce._resources), _processes(), _sched_queue(), _sreq_queues(ce._sreq_queues)
{
  // The ReadyQueue won't be copied. Pointers to objects contained into
  // the ready queue _will_ have changed in the new one. The ready queue
  // needs to be reset: it is Scheduler that builds it again from time to time.

  // Update resource pointers in a way you won't like :-)
  {
    for (Resources::iterator it = _resources.begin(); it != _resources.end(); it++)
      it->second = new DynamicResource(dynamic_cast<const DynamicResource&>(*it->second));
  }

  // DynamicProcess object need to be copied.
  // The deep copy is guaranteed by the DynamicProcess copy constructor
  {
    const Processes& ce_proc = ce._processes;
    insert_iterator<Processes> dest(_processes, _processes.begin());
    for (Iseq<Processes::const_iterator> orig = iseq(ce_proc); orig; orig++)
      *dest++ = new DynamicProcess(dynamic_cast<const DynamicProcess&>(**orig));
  }



  // Update the subrequest queues.

  // for each subrequest
  typedef Processes::const_iterator  it1_t;

  typedef std::vector<Thread*>        v2_t;
  typedef v2_t::const_iterator       it2_t;

  typedef std::vector<Request*>       v3_t;
  typedef v3_t::const_iterator       it3_t;

  typedef std::vector<SubRequest*>    v4_t;
  typedef v4_t::const_iterator       it4_t;

  typedef SubRequestQueue::iterator  it5_t;

  for (it1_t it1 = _processes.begin(); it1 != _processes.end(); it1++)
  {
    const v2_t& threads = (*it1)->get_threads();
    for (it2_t it2 = threads.begin(); it2 != threads.end(); it2++)
    {
      const v3_t& reqs = (*it2)->get_requests();
      for (it3_t it3 = reqs.begin(); it3 != reqs.end(); it3++)
      {
        // an optimization here: there is no reason in iterating through
        // future or exausted requests. (Do you know why?)
        const v4_t& subr = (*it3)->get_subrequests();
        for (it4_t it4 = subr.begin(); it4 != subr.end(); it4++)
        {
          SubRequest::state curr_state = (*it4)->get_state();
          if (curr_state != Request::state_future && curr_state != Request::state_exhausted)
          {
            // the subrequest is the following queue:
            SubRequestQueue & queue = get_request_queue((*it4)->get_resource_key());
            // we must replace the old pointer:
            bool found = false;
            for (it5_t it5 = queue.begin(); !found && it5 != queue.end(); it5++)
            {
              DynamicSubRequest& _old = dynamic_cast<DynamicSubRequest&>(**it5);
              DynamicSubRequest& _new = dynamic_cast<DynamicSubRequest&>(**it4);
              if (&_old.get_core() == &_new.get_core())
              {
                found = true;
                *it5 = *it4;
              }
            }
          }
        }
      }
    }
  }


}



const Environment::Processes&
ConcreteEnvironment::get_processes() const
{
  return _processes;
}



ConcreteEnvironment::Processes&
ConcreteEnvironment::get_processes()
{
  return _processes;
}



const Environment::Resources&
ConcreteEnvironment::get_resources() const
{
  return _resources;
}



ConcreteEnvironment::Resources&
ConcreteEnvironment::get_resources()
{
  return _resources;
}



const Environment::SubRequestQueue&
ConcreteEnvironment::get_request_queue(resource_key_t resource_key) const
{
  // Should always return something...
  return _sreq_queues.find(resource_key)->second;
}


Environment::SubRequestQueue&
ConcreteEnvironment::get_request_queue(resource_key_t resource_key)
{
  // Inserts a new element in none is there!
  return _sreq_queues[resource_key];
}

ConcreteEnvironment::SubRequestQueues&
ConcreteEnvironment::get_subrequest_queues()
{
  return _sreq_queues;
}



const ReadyQueue&
ConcreteEnvironment::get_sorted_queue() const
{
  return _sched_queue;
}



ReadyQueue&
ConcreteEnvironment::get_sorted_queue()
{
  return _sched_queue;
}



ConcreteEnvironment::~ConcreteEnvironment()
{
  // This call will invoke the DynamicProcess virtual destructor
  // Which will delete on cascade all DynamicThreads and so on.
  for_each(_processes.begin(), _processes.end(),
           memory::deletor<Process>());

  // We do the same with Resources.
  for (Resources::iterator it = _resources.begin(); it != _resources.end(); it++)
    delete it->second;

  // After this, the destructor of _sched_queue is invoked (only invalid pointers)
  // After that, the destructor of _processes is invoked (only invalid pointers)
  // After that, the destructor of _resources is invoked (only invalid pointers)
}




// -------------------------------- TO BE FIXED ----------------

