// src/backend/dynamic_process.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 "dynamic_process.hh"
#include "static_process.hh"
#include "dynamic_thread.hh"
#include <sgpemv2/serialize_visitor.hh>

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

#include <algorithm>
#include <functional>
#include <iostream>
#include <cassert>

using namespace sgpem;
using namespace std;

typedef std::vector<DynamicThread*>::const_iterator  ConstThreadIt;
typedef std::vector<DynamicThread*>::iterator        ThreadIt;


DynamicProcess::DynamicProcess(StaticProcess* core) :
    DynamicSchedulable(), _core(core)
{
  assert(core != NULL);
}

DynamicProcess::DynamicProcess(const DynamicProcess &other) :
    Schedulable(), DynamicSchedulable(other), Process(),
    _core(other._core)
{  
  for (Iseq<ConstThreadIt> seq = iseq(other._dynamic_threads); seq; ++seq)
    new DynamicThread(*(*seq), this);
}

DynamicProcess::~DynamicProcess()
{
  for_each(_dynamic_threads.begin(), _dynamic_threads.end(),
           memory::deletor<DynamicThread>());
}

std::vector<Thread*>
DynamicProcess::get_threads()
{
  return vector<Thread*>(_dynamic_threads.begin(), _dynamic_threads.end());
}

std::vector<const Thread*>
DynamicProcess::get_threads() const
{
  return vector<const Thread*>(_dynamic_threads.begin(), _dynamic_threads.end());
}

Schedulable::state
DynamicProcess::get_state() const
{
  static const int uninitialized = -1;

  state result = state_terminated;
  int next_thread_starts_at = uninitialized;

  // This is the logic behind the code:
  // If there is at least one running thread, the result is
  // running. If not, it may be either blocked, ready, future or terminated.
  
  // We have these cases (a state takes precedence over some other one):
  // (a) if a thread is running, return immediately state_running
  // (b) if a thread is ready, puts unconditionally result as state_ready,
  //   and continue iterating (to see if there's a running thread)
  // (c) if a thread is blocked, and result is not state_ready, result
  //   becomes state_blocked, and continue iterating (to see if there are
  //   ready or running threads)
  // (d) if a thread is future, and result is not state_ready or
  //   state_blocked, put result as state_future, and remember
  //   when the next thread will start (d1) (see at the end of this
  //   method for the rationale (d2)). Then continue iterating.
  // (e) else (if all threads are state_terminated) put result as
  //   state_terminated.

  
  for(Iseq<ConstThreadIt> seq = iseq(_dynamic_threads); seq; ++seq)
  {
    state thread_state = (*seq)->get_state();

    switch(thread_state)
    {
    case state_running: // (a)
      return state_running;
    case state_ready:   // (b)
      result = state_ready;
      continue;
    case state_blocked: // (c)
      if((result & state_ready) == 0)
        result = state_blocked;
      continue;
    case state_future:  // (d)
      if((result & (state_ready|state_blocked)) == 0)
        {
          result = state_future;
          int thread_starts_at = (*seq)->get_arrival_time();
          if(next_thread_starts_at == uninitialized) // (d1)
            next_thread_starts_at = thread_starts_at;
          else
            next_thread_starts_at = std::min(thread_starts_at, next_thread_starts_at);
        }
      continue;
    case state_terminated: // (e)
      // already put into terminated state as the default value
      continue;
    }
  } //~ "for" iterating over threads

  // (d2) Now check if a "hole" happens: if all other threads are terminated
  // the next thread to start, e.g. the one with the least arrival_time,
  // has start time greater than the current process elapsed time, then
  // pass from state_future to state_terminated:
  if (result == state_future && next_thread_starts_at > static_cast<int>(get_elapsed_time()))
    return state_terminated;

  return result;
}


void
DynamicProcess::serialize(SerializeVisitor& translator) const
{
  translator.from_process(*this);
}

StaticProcess&
DynamicProcess::get_core()
{
  return *_core;
}


const StaticProcess&
DynamicProcess::get_core() const
{
  return *_core;
}


std::vector<DynamicThread*>&
DynamicProcess::get_dynamic_threads()
{
  return _dynamic_threads;
}

unsigned int
DynamicProcess::get_elapsed_time() const
{
  unsigned int result = 0;
  for (Iseq<ConstThreadIt> seq = iseq(_dynamic_threads); seq; ++seq)
  {
    result += (*seq)->get_elapsed_time();
  }
  return result;
}

int
DynamicProcess::get_last_acquisition() const
{
  int result = -1;
  for (Iseq<ConstThreadIt> seq = iseq(_dynamic_threads); seq; ++seq)
  {
    int acq = (*seq)->get_last_acquisition();
    if (result < acq)
      result = acq;
  }
  return result;
}

int
DynamicProcess::get_last_release() const
{
  int result = -1;
  for (Iseq<ConstThreadIt> seq = iseq(_dynamic_threads); seq; ++seq)
  {
    int acq = (*seq)->get_last_release();
    if (result < acq)
      result = acq;
  }
  return result;
}

