/*
 * Author: Radovan Cerveny
 */

#include "MeasurementEngine.hpp"
#include <stdexcept>

namespace measurements {

MeasurementEngine MeasurementEngine::INSTANCE;
bool MeasurementEngine::OPERATIONAL;

MeasurementEngine::MeasurementEngine ( ) {
	MeasurementEngine::OPERATIONAL = true;

	resetMeasurements ( );
}

MeasurementEngine::~MeasurementEngine ( ) {
	MeasurementEngine::OPERATIONAL = false;
}

void MeasurementEngine::pushMeasurementFrame ( measurements::stealth_string frameName, measurements::Type frameType ) {
	unsigned parentIdx = frameIdxStack.back ( );

	frames.emplace_back ( std::move ( frameName ), frameType, parentIdx );

	unsigned currentIdx = frames.size ( ) - 1;

	frames[parentIdx].subIdxs.push_back ( currentIdx );

	frameIdxStack.push_back ( currentIdx );

	TimeDataFrame::init ( currentIdx, frames );
	MemoryDataFrame::init ( currentIdx, frames );
	CounterDataFrame::init ( currentIdx, frames );
}

void MeasurementEngine::popMeasurementFrame ( ) {
	unsigned currentIdx = frameIdxStack.back ( );

	if ( frames[currentIdx].type == measurements::Type::ROOT )
		throw std::domain_error ( "MeasurementEngine: popMeasurementFrame failed, no measurements started" );

	frameIdxStack.pop_back ( );

	TimeDataFrame::update ( currentIdx, frames );
	MemoryDataFrame::update ( currentIdx, frames );
	CounterDataFrame::update ( currentIdx, frames );
}

void MeasurementEngine::resetMeasurements ( ) {
	frames.clear ( );
	frameIdxStack.clear ( );

	frames.emplace_back ( "Root", measurements::Type::ROOT, 0 );
	frameIdxStack.push_back ( 0 );

	TimeDataFrame::init ( 0, frames );
	MemoryDataFrame::init ( 0, frames );
	CounterDataFrame::init ( 0, frames );
}

MeasurementResults MeasurementEngine::getResults ( ) const {
	return MeasurementResults ( frames );
}

template < typename Hint >
void MeasurementEngine::hint ( Hint arg ) {
	if ( ( frameIdxStack.empty ( ) ) ) return;

	Hint::frame_type::hint ( frameIdxStack.back ( ), frames, std::move ( arg ) );
}

template void MeasurementEngine::hint < MemoryHint > ( MemoryHint );
template void MeasurementEngine::hint < CounterHint > ( CounterHint );
}
