/*
 * StatisticsCollector.cc
 *
 *  Created on: 21.08.2013
 *      Author: lrade
 */

#include "ResultCollector.hpp"

#include <iostream>
#include <fstream>
#include <cmath>
#include <numeric>
#include <algorithm>

#include <vector>

#include "comm/InjectionPointHopsMessage.pb.h"

#include <proc/readproc.h>

using std::hex;
using std::dec;

char separator = ' ';

extern std::ofstream g_cp_ofstream;

extern fail::Logger LOG;

void
ResultCollector::setProtoOStream(ProtoOStream* protoOStream)
{
	ps = protoOStream;
}

unsigned int calculate_costs(std::vector<result_tuple >& res)
{
	std::vector<result_tuple>::iterator it = res.begin();
	std::vector<result_tuple>::iterator it_ahead = res.begin();
	it_ahead++;

	// Cost COST_CHANGE for first hop
	// Costs of all following hops are calculated only on basis of
	// difference between two consecutive hop_positions
	unsigned int costs = COST_CHANGE;

	while (it != res.end() && it_ahead != res.end()) {
		if (it->first == it_ahead->first) {
			costs += COST_NO_CHANGE;
		} else {
			costs += COST_CHANGE;
		}
		it_ahead++;
		it++;
	}

	return costs;
}

void
ResultCollector::addResult(std::vector<result_tuple >& res, unsigned int costs)
{
	m_result_size++;

	// Costs not calculated in hop calculator (e.g. dijkstra)? => Do it here
	if (costs == 0) {
		costs = calculate_costs(res);
	}

	if (m_output_mode == OUTPUT_COSTS) {
		m_ostream << m_res_count++ << separator << costs << '\n';
	} else if (m_output_mode == OUTPUT_RESULT) {
		if (ps) {
			InjectionPointMessage hc;
			hc.set_costs(costs);
			hc.set_target_trace_position(res.back().second);
			// If checkpoint at beginning of hop-chain, add its id to InjectionPointMessage
			std::vector<result_tuple >::iterator it_hop = res.begin();
			if (it_hop != res.end() && it_hop->first.second == ACCESS_CHECKPOINT) {
				hc.set_checkpoint_id(it_hop->first.first);
				it_hop++;
			}

			for (; it_hop != res.end();
				it_hop++) {
				InjectionPointMessage_Hops *hop = hc.add_hops();
				hop->set_address(it_hop->first.first);

				enum InjectionPointMessage_Hops_AccessType at;
				switch (it_hop->first.second) {
				case ACCESS_NONE:
					at = InjectionPointMessage_Hops_AccessType_EXECUTE;
					break;
				case ACCESS_READ:
					at = InjectionPointMessage_Hops_AccessType_READ;
					break;
				case ACCESS_WRITE:
					at = InjectionPointMessage_Hops_AccessType_WRITE;
					break;
				case ACCESS_READORWRITE:
					LOG << "ReadOrWrite memory access event not yet"
					" covered" << std::endl;
					exit(-1);
					break;
				case ACCESS_CHECKPOINT:
					LOG << "Checkpoint not allowed after beginning of hop chain" << std::endl;
					exit(-1);
				}
				hop->set_accesstype(at);
			}
			ps->writeMessage(&hc);
		} else {
			for (std::vector<result_tuple >::iterator it_hop = res.begin();
					it_hop != res.end();
					it_hop++) {
				address_t add = it_hop->first.first;
				mem_access_type_e mem_acc_type = it_hop->first.second;
				std::string prefix = (mem_acc_type == ACCESS_READ)?"R":((mem_acc_type == ACCESS_WRITE)?"W":((mem_acc_type == ACCESS_NONE)?"X":((mem_acc_type == ACCESS_CHECKPOINT)?"C":"")));
				m_ostream << prefix << hex << add << dec << separator;
			}

			if (res.size() > 0)
				m_ostream << '\n';
		}
	} else if (m_output_mode == OUTPUT_STATISTICS) {
		// Calculate mean
		// http://www.heikohoffmann.de/htmlthesis/node134.html
		// c(t+1) = c(t) + (1/(t+1))*(x - c(t))
		m_it_mean_costs = m_it_mean_costs + (1/(double)m_result_size)*((double)costs-m_it_mean_costs);

	}
}

void
ResultCollector::addCheckpoint(unsigned int pos)
{
	g_cp_ofstream << m_checkpoint_count++ << separator << pos << '\n';
}

void
ResultCollector::startTimer()
{
	m_timer.startTimer();
}

void
ResultCollector::stopTimer()
{
	m_timer.stopTimer();
}

void
ResultCollector::setMaxMemUsage()
{
	struct proc_t usage;
	look_up_our_self(&usage);
	m_mem_usage = usage.vsize;
}

void
ResultCollector::finish()
{
	// Print results if buffered

	// Print statistics
	if (m_output_mode == OUTPUT_STATISTICS) {

		m_ostream << m_result_size << separator << m_timer.getRuntimeAsDouble() << separator
				<< m_mem_usage << separator << m_it_mean_costs << separator << m_checkpoint_count << '\n';
	}
}
