// src/backend/concrete_process_statistics.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_process_statistics.hh"
#include "concrete_thread_statistics.hh"
#include <sgpemv2/simulation.hh>
#include <sgpemv2/history.hh>
#include <sgpemv2/environment.hh>
#include <sgpemv2/schedulable.hh>
#include <sgpemv2/process.hh>
#include <sgpemv2/thread.hh>

#include <iostream>

using namespace std;
using namespace sgpem;

/**
	The constructor executes all calculations examinating or only the Thred object,
	or the whole History
	
	\param core The process whose statistics we are going to calculate
	\param instant The instant statistics will refer to
	\param _threads_stats The statistics of the threads belonging to "core"

*/
ConcreteProcessStatistics::ConcreteProcessStatistics(const Process* core, const int& instant):
		 _core(core)
{
	//retrieve threads statistics necessary to calculate "core"'s statistics
	vector<const Thread*> threads = core->get_threads();
	for (uint i_t = 0; i_t < threads.size(); i_t++)
		_threads_stats.push_back(ConcreteThreadStatistics(threads[i_t], instant));

			
	//inizialize variables		
	_total_inactivity 		= 0;
	_execution_time 		= 0;
	_response_time 			= -1;
	_turn_around			= 0;
	_resource_usage_time	= 0;
	_resource_waitings_time = 0;
	_average_response_time	= 0;
	int min_reponse			= -1;		//useful for _response_time
	int arrived_threads		= 0;		//useful for _average_response_time
	bool someone_began		= false;	//useful for _response_time
	
	//iterate through threads statistics
	vector<ConcreteThreadStatistics>::const_iterator i;
	for(i = _threads_stats.begin(); i != _threads_stats.end(); i++)
	{
		Thread* t = const_cast<Thread*>((*i).get_core());
		if (t->get_process() == *core)
		{							//found a thread belonging to "core"
			
			_execution_time += (*i).get_execution_time();			
			_resource_usage_time += (*i).get_resource_usage_time();			
					
			if ((*i).get_response_time() != -1)
			{
				_average_response_time += (*i).get_response_time();
				arrived_threads++;
			}				
			
			if ((*i).get_response_time() != -1 && someone_began == false)
			{
				someone_began = true;
				min_reponse = (*i).get_response_time();
			}
			
			if(someone_began && (*i).get_response_time() != -1 && (*i).get_response_time() + (*i).get_real_arrival_time() < min_reponse)
				min_reponse = (*i).get_response_time() + (*i).get_real_arrival_time();
			
			if ((*i).get_real_arrival_time() + (*i).get_execution_time() + (*i).get_total_inactivity() > _turn_around)
				_turn_around = (*i).get_real_arrival_time() + (*i).get_execution_time() + (*i).get_total_inactivity();
		}
	}
	
	//******* AAARRRGGHH!!!
	//We have to iterate the whole History ONLY to retreive _resource_waitings_time !!
	// 	
	const History& hist = Simulation::get_instance().get_history();
	for (int time=0; time < instant; time++)
	{		
		const Environment& 		env 	= hist.get_environment_at(time);
		const vector<Process*> 	procs 	= env.get_processes();
		
		for (uint i_p=0; i_p < procs.size(); i_p++)		
			if (*procs[i_p] == *core && procs[i_p]->get_state() == Schedulable::state_blocked)
				_resource_waitings_time++;
	}			
				
				

	//set other variables
	if(core->get_total_cpu_time() != 0)
		_execution_progress = (_execution_time*100) / core->get_total_cpu_time();	
	else
		_execution_progress = 0;
	
	_total_inactivity = _turn_around - _execution_time;
	
	if (_turn_around == 0)
		_efficiency = -1;
	else
		_efficiency = (_execution_time*100)/_turn_around;
	
	_response_time = min_reponse;
	
	if (arrived_threads != 0)
		_average_response_time = (float)((int)((_average_response_time/arrived_threads)*100))/100;
}


ConcreteProcessStatistics::~ConcreteProcessStatistics()
{	
}

int			
ConcreteProcessStatistics::get_execution_time() const
{
	return _execution_time;
}

int	
ConcreteProcessStatistics::get_execution_progress() const
{
	return _execution_progress;
}

int	
ConcreteProcessStatistics::get_total_inactivity() const
{
	return _total_inactivity;
}

int	
ConcreteProcessStatistics::get_response_time() const
{
	return _response_time;
}

int	
ConcreteProcessStatistics::get_turn_around() const
{
	return _turn_around;
}

int
ConcreteProcessStatistics::get_efficiency() const
{
	return _efficiency;	
}

int
ConcreteProcessStatistics::get_resource_usage_time() const
{
	return _resource_usage_time;
}

int
ConcreteProcessStatistics::get_resource_waitings_time() const
{
	return _resource_waitings_time;
}

float
ConcreteProcessStatistics::get_average_response_time() const
{
	return _average_response_time;
}

const Process* 	
ConcreteProcessStatistics::get_core() const
{
	return _core;	
}

/**
	\warning Don't delete these pointers!!
	\warning These pointers are not valid anymore after the destruction of "this"
*/
vector<const ThreadStatistics*>
ConcreteProcessStatistics::get_threads_statistics() const
{
	vector<const ThreadStatistics*> rit;
	for (uint i=0; i < _threads_stats.size(); i++)
		rit.push_back(&_threads_stats[i]);
	return rit;
}
