/**
 * STATS - small C++ code statistic library (code events counter)
 * version 0.3 (29 V 2011)
 * by Piotr Beling
 *
 * STATS is provided under the terms of the GNU Library Public License,
 * Version 2 with exceptions that allow for static linking.
 *
 * This file includes whole library, to use it:
 * - include it in Your project (compilation unit): #include <stats.hpp>
 * - compile Your project (compilation unit) with STATS symbol (-DSTATS in gcc)
 * Note:
 *  if project (compilation unit) is compiled without STATS symbol all statistics are disable
 *  (all provided macros are empty) and program binary code is exacly like without this library
 *
 * How to use (minimal example):
 * #include <stats.hpp>
 * ...
 * //declare events (this declare some objects):
 * STATS_DECL_EVENT(MyEvent, std::cout)			//root event, print raport to std::cout
 * STATS_DECL_SUBEVENT(MyEvent, SubEvt1) 		//MyEvent sub-event
 * STATS_DECL_SUBEVENT(MyEvent, SubEvt2)		//MyEvent sub-event
 * STATS_DECL_SUBEVENT(SubEvt2, SubEvt2SubEvt)	//SubEvt2 sub-event
 * ...
 * //in code just show where events/sub-events starts:
 * STATS_EVENT(MyEvent); //MyEvent starts hare
 * while (...) { //loop with MyEvent sub-events:
 *    if (...) {
 *      STATS_EVENT(SubEvt1); //SubEvt1 starts hare
 *      ...
 *    } else {
 *      STATS_EVENT(SubEvt2); //SubEvt2 starts hare
 *      ...
 *      while (...) { //loop with SubEvt2 sub-events:
 *         STATS_EVENT(SubEvt2SubEvt);
 *         ...
 *      }
 *    }
 * }
 *
 * Version history:
 * v0.3 (29 V 2011): fix bug (too small local varible type when printing)
 * v0.2 (12 VII 2008): added sub-events with table of values
 * v0.1 (20 IV 2008): first public relase
 */

#ifndef ___QWAK___STATS__HPP_
#define ___QWAK___STATS__HPP_

#ifndef STATS	//interface:

/**
 * Declere (root) event object with given name and output.
 * @param name event name
 * @param output output stream object (std::ostream&)
 */
#define STATS_DECL_EVENT(name, output)

/**
 * Declere subevent object with given name.
 * @param parentname parent (over-event) event/subevent name
 * @param name this subevent name
 * @param ... optionals: maximum number of values (1 by default) and first values array index (0 by default)
 */
#define STATS_DECL_SUBEVENT(parentname, name, ...)

/**
 * Use (import) event/subevent from other compilation unit.
 * @name event/subevent name to use
 */
#define STATS_DECL_EXTERN(name)

/**
 * Increase event counter by 1.
 * Eventualy print raport if name is root event.
 * @param name event/subevent name
 * @param ... one optional parameter: index in table
 */
#define STATS_EVENT(name, ...)

/**
 * Increase event counter by 1 but only if mimum one subevent was registered.
 * Eventualy print raport if name is root event.
 * @param name event/subevent name
 */
#define STATS_EVENT_NONEMPTY(name)

/**
 * Increase event counter by given value.
 * Eventualy print raport for root.
 * @param name event/subevent name
 * @param howmuny value
 */
#define STATS_EVENTS(name, howmuny)

/**
 * Do next instruction iff condition.
 * @param condition
 * Example: <code>STATS_IF(...) STATS_EVENT(EVENTNAME);</code>
 */
#define STATS_IF(condition)

/**
 * Set event/subevent and all his subevents (subtree) counters to zero
 * @param name event/subevent name
 */
#define STATS_RESET(name)

#else					//implementation:

#include <vector>
#include <ostream>
#include <cassert>
#include <stdint.h>

struct __qwak__stats__eventcounter {
	__qwak__stats__eventcounter* parent;	//parent in events tree
	const char* name;						//event name
	std::ostream& output;					//stream to print
	typedef uint64_t value_t;
	value_t* values;					//values
	value_t* accvaluebase;			//accumulate values
	typedef std::vector<__qwak__stats__eventcounter*> ecvec;
	ecvec subevents;						//sub-events

	int table_first_index;					//if has values table first index in this table, or 0
	int table_size;
	//typedef unsigned char flags_t;

	void reserve_values_mem() {
		values = new value_t[table_size];
		accvaluebase = new value_t[table_size];
		for (int i = 0; i < table_size; ++i) values[i] = accvaluebase[i] = 0;
	}

	//constructor for root
	__qwak__stats__eventcounter(const char* name, std::ostream& output, int table_size = 1, int table_first_index = 0)
		: parent(0), name(name), output(output),
		  table_first_index(table_first_index), table_size(table_size)
	{
		reserve_values_mem();
	}

	//constructor for sub-events
	__qwak__stats__eventcounter(const char* name, __qwak__stats__eventcounter& parent, int table_size = 1, int table_first_index = 0)
	: parent(&parent), name(name), output(parent.output),
	  table_first_index(table_first_index), table_size(table_size) {
		reserve_values_mem();
		assert(this->parent->table_size == 1 && "Table events can't have subevents.");
		this->parent->subevents.push_back(this);
	}

	//destructor, first and only first node deleted in tree print all the tree
	~__qwak__stats__eventcounter() {
		root()->print_and_reset();
		delete[] values; delete[] accvaluebase;
	}

	//tree root
	__qwak__stats__eventcounter* root() { return is_root() ? this : parent->root(); }

	//print all the tree and reset
	void print_and_reset() {
		if (is_root() && *values != 0 /*and there was main event*/) {
			print_childrens();
			reset();
		}
	}

	value_t accvalue(int index = 0) const { return accvaluebase[index] + values[index]; }

	void printv(const value_t* accbase, const value_t* value, const value_t parentvalue,
				const int how_muny, bool full_print = true) {
		if (how_muny == 1) {
			value_t v = accbase ? *accbase + *value : *value;
			output << v;
			if (full_print) {
				output << " / " <<  parentvalue;
				if (parentvalue != 0) output << " = " << double(v)/double(parentvalue);
			}
		} else {
			output << "[ ";
			value_t sum = 0;
			for (int i = 0; i < table_size; ++i) {
				value_t v = (accbase) ? values[i] + accbase[i] : values[i];
				output << v << ' ';
				sum += v;
			}
			output << "] " << sum;
			if (full_print) {
				output << " / " << parentvalue;
				if (parentvalue != 0) {
					output << " = [ ";
					const double parent_vd = double(parentvalue);
					if (accbase)
						for (int i = 0; i < table_size; ++i) output << double(values[i] + accbase[i])/parent_vd << ' ';
					else
						for (int i = 0; i < table_size; ++i) output << double(values[i])/parent_vd << ' ';
					output << "] " << double(sum)/parent_vd;
				}
			}
		}
	}

	bool is_root() const { return parent == 0; }

	void print_this() {
		if (is_root()) return; //do not print root
		output << name << " / " << parent->name << ' ';
		printv(0, values, *parent->values, table_size, !parent->is_root());
		output << " ACC: ";
		printv(accvaluebase, values, parent->accvalue(), table_size);
		output << std::endl;
	}

	void print_childrens() {
		for (ecvec::iterator i = subevents.begin(); i != subevents.end(); ++i)
			(*i)->print();
	}

	void print() { print_this(); print_childrens(); }

	void reset_childrens() {
		for (ecvec::iterator i = subevents.begin(); i != subevents.end(); ++i) (*i)->reset();
	}

	void reset() {	//zero value and subvalues (but not accumulate subvalues)
		reset_childrens();
		for (int i = 0; i < table_size; ++i) {
			accvaluebase[i] += values[i];
			values[i] = 0;
		}
	}

	void event_end() { print_and_reset(); }

	void events(value_t howmuny) {
		event_end();	//previous event end
		*values += howmuny;
	}

	//true <=> all values are 0
	bool all_zeros() const {
		for (int i = 0; i < table_size; ++i)
			if (values[i] != 0) return false;
		return true;
	}

	void event_if_non_empty() {
		for (ecvec::iterator i = subevents.begin(); i != subevents.end(); ++i)
			if (!(*i)->all_zeros()) { event(); return; }
	}

	void event() {	//increase value
		event_end();	//previous event end
		++*values;
	}

	bool check_index(int nr) const { return table_first_index <= nr && nr < table_first_index + table_size; }

	void event(int nr) {
		assert(check_index(nr) && "table sub-event index out of bounds");
		//event_end();//we don't must call this because we are children
		++values[nr - table_first_index];
	}
};



#define STATS_DECL_EVENT(name, output) __qwak__stats__eventcounter __qwak__stats__var_ ## name(#name, output);
#define STATS_DECL_SUBEVENT(parentname, name, ...) __qwak__stats__eventcounter __qwak__stats__var_ ## name(#name, __qwak__stats__var_ ## parentname, ##__VA_ARGS__);
#define STATS_DECL_EXTERN(name) extern __qwak__stats__eventcounter __qwak__stats__var_ ## name;
#define STATS_EVENT(name, ...) (__qwak__stats__var_ ## name).event(__VA_ARGS__)
#define STATS_EVENT_NONEMPTY(name) (__qwak__stats__var_ ## name).event_if_non_empty()
#define STATS_EVENTS(name, howmuny) (__qwak__stats__var_ ## name).events(howmuny)
#define STATS_IF(condition) if (condition)
#define STATS_RESET(name) (__qwak__stats__var_ ## name).reset()

//You can optional call this on event end:
//#define STATS_EVENT_END(name) (name).event_end()

#endif	//STATS

#endif	//___QWAK___STATS__HPP_

