/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "script/script_profiler.h"
		#include "platform.h"
		#include "log/log.h"

		using namespace GS::Script;


//------------------------------------------------------------------------------
Profiler::FunctionProfile *Profiler::GetFunctionProfile(const char *source, const char *func, int line, FunctionProfile *caller)
{
	// Check for profiled function.
	FunctionProfile	*profile = NULL;
	ListForeachPtr(FunctionProfile *, f_profile, function_map)
		if	(
				(f_profile->func == func) &&
				(f_profile->caller_function == caller)
			)
		{
			profile = f_profile;
			break;
		}

	// Create a new function profile.
	if	(!profile)
	{
		profile = new FunctionProfile(source, func, line);
		if	(!profile)
			__LOG_E__ << "Failed to allocate a new function profile.\n";

		// Register caller.
		profile->caller_function = caller;

		// Add the newly created function as a child of the caller function profile.
		if	(caller)
			caller->callee_function.Add(profile);

		function_map.Add(profile);
	}
	return profile;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Profiler::Start(const char *session_id)
{
	id = session_id;
	function_map.Clear();
	function_list.Clear();

	current_function_profile = NULL;

	profiler_clock = Platform::Get().GetClock();
	total_clock = 0;

	profiling = true;
}
int					Profiler::GetChildTotalClock(FunctionProfile *profile)
{
	int child_total_clock = 0;
	ListForeachPtr(FunctionProfile *, c_profile, profile->callee_function)
		child_total_clock += GetChildTotalClock(c_profile);
	return profile->self_clock + child_total_clock;
}
void				Profiler::End()
{
	ListForeachPtr(FunctionProfile *, f_profile, function_map)
		f_profile->total_clock = GetChildTotalClock(f_profile);

	// Build the function list from the call graph function map.
	ListForeachPtr(FunctionProfile *, f_profile, function_map)
	{
		// Check list if function is already present.
		FunctionProfile	*profile = NULL;
		ListForeachPtr(FunctionProfile *, l_profile, function_list)
			if	(l_profile->func == f_profile->func)
			{
				profile = l_profile;
				break;
			}

		// If not, create a new list entry.
		if	(!profile)
		{
			profile = new FunctionProfile(f_profile->source.c_str(), f_profile->func.c_str(), f_profile->line);
			function_list.Add(profile);
		}

		// Merge this function contribution.
		profile->hit_count += f_profile->hit_count;
		profile->self_clock += f_profile->self_clock;
		profile->total_clock += f_profile->total_clock;
	}

	profiling = false;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Profiler::Update(int type, FunctionProfile *profile)
{
	// Update clock.
	int current_clock = Platform::Get().GetClock(),
		dt_clock = current_clock - profiler_clock;
	profiler_clock = current_clock;

	// Update current profile.
	if	(current_function_profile)
	{
		current_function_profile->Update(dt_clock, 0);
		total_clock += dt_clock;
	}

	// Get profile.
	switch (type)
	{
		case 'c':
			current_function_profile = profile;
			current_function_profile->Hit();
			break;

		case 'r':
			current_function_profile = NULL;
			break;
		case 'l':
			break;
	}
}
//------------------------------------------------------------------------------

Profiler::Profiler() : profiling(false), current_function_profile(NULL) {}
