/**
 * @file format_output.h
 * outputting format for symbol lists
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Read the file COPYING
 *
 * @author Philippe Elie
 * @author John Levon
 */

#ifndef FORMAT_OUTPUT_H
#define FORMAT_OUTPUT_H

#include "config.h"

#include <string>
#include <map>
#include <iosfwd>

#include "format_flags.h"
#include "symbol.h"
#include "string_filter.h"
#include "xml_output.h"

class symbol_entry;
class sample_entry;
class callgraph_container;
class profile_container;
class diff_container;
class extra_images;
class op_bfd;

struct profile_classes;
// FIXME: should be passed to the derived class formatter ctor
extern profile_classes classes;

namespace format_output {

/// base class for formatter, handle common options to formatter
class formatter {
public:
	formatter(extra_images const & extra);
	virtual ~formatter();

	/// add a given column
	void add_format(format_flags flag);

	/// set the need_header boolean to false
	void show_header(bool);
	/// format for 64 bit wide VMAs
	void vma_format_64bit(bool);
	/// show long (full path) filenames
	void show_long_filenames(bool);
	/// use global count rather symbol count for details percent
	void show_global_percent(bool);

	/**
	 * Set the number of collected profile classes. Each class
	 * will output sample count and percentage in extra columns.
	 *
	 * This class assumes that the profile information has been
	 * populated with the right number of classes.
	 */
	void set_nr_classes(size_t nr_classes);

	/// output table header, implemented by calling the virtual function
	/// output_header_field()
	void output_header(std::ostream & out);

protected:
	struct counts_t {
		/// total sample count
		count_array_t total;
		/// samples so far
		count_array_t cumulated_samples;
		/// percentage so far
		count_array_t cumulated_percent;
		/// detailed percentage so far
		count_array_t cumulated_percent_details;
	};

	/// data passed for output
	struct field_datum {
		field_datum(symbol_entry const & sym,
		            sample_entry const & s,
			    size_t pc, counts_t & c,
			    extra_images const & extra, double d = 0.0)
			: symbol(sym), sample(s), pclass(pc),
			  counts(c), extra(extra), diff(d) {}
		symbol_entry const & symbol;
		sample_entry const & sample;
		size_t pclass;
		counts_t & counts;
		extra_images const & extra;
		double diff;
	};
 
	/// format callback type
	typedef std::string (formatter::*fct_format)(field_datum const &);
 
	/** @name format functions.
	 * The set of formatting functions, used internally by output().
	 */
	//@{
	std::string format_vma(field_datum const &);
	std::string format_symb_name(field_datum const &);
	std::string format_image_name(field_datum const &);
	std::string format_app_name(field_datum const &);
	std::string format_linenr_info(field_datum const &);
	std::string format_nr_samples(field_datum const &);
	std::string format_nr_cumulated_samples(field_datum const &);
	std::string format_percent(field_datum const &);
	std::string format_cumulated_percent(field_datum const &);
	std::string format_percent_details(field_datum const &);
	std::string format_cumulated_percent_details(field_datum const &);
	std::string format_diff(field_datum const &);
	//@}
 
	/// decribe one field of the colummned output.
	struct field_description {
		field_description() {}
		field_description(std::size_t w, std::string h,
				  fct_format f)
			: width(w), header_name(h), formatter(f) {}
 
		std::size_t width;
		std::string header_name;
		fct_format formatter;
	};
 
	typedef std::map<format_flags, field_description> format_map_t;

	/// actually do output
	void do_output(std::ostream & out, symbol_entry const & symbol,
		      sample_entry const & sample, counts_t & c,
	              diff_array_t const & = diff_array_t(),
	              bool hide_immutable_field = false);
 
	/// returns the nr of char needed to pad this field
	size_t output_header_field(std::ostream & out, format_flags fl,
	                           size_t padding);

	/// returns the nr of char needed to pad this field
	size_t output_field(std::ostream & out, field_datum const & datum,
			   format_flags fl, size_t padding,
			   bool hide_immutable);
 
	/// stores functors for doing actual formatting
	format_map_t format_map;

	/// number of profile classes
	size_t nr_classes;

	/// total counts
	counts_t counts;

	/// formatting flags set
	format_flags flags;
	/// true if we need to format as 64 bits quantities
	bool vma_64;
	/// false if we use basename(filename) in output rather filename
	bool long_filenames;
	/// true if we need to show header before the first output
	bool need_header;
	/// bool if details percentage are relative to total count rather to
	/// symbol count
	bool global_percent;

	/// To retrieve the real image location, usefull when acting on
	/// an archive and for 2.6 kernel modules
	extra_images const & extra_found_images;
};
 

/// class to output in a columned format symbols and associated samples
class opreport_formatter : public formatter {
public:
	/// build a ready to use formatter
	opreport_formatter(profile_container const & profile);

	/** output a vector of symbols to out according to the output format
	 * specifier previously set by call(s) to add_format() */
	void output(std::ostream & out, symbol_collection const & syms);

	/// set the output_details boolean
	void show_details(bool);

private:
 
	/** output one symbol symb to out according to the output format
	 * specifier previously set by call(s) to add_format() */
	void output(std::ostream & out, symbol_entry const * symb);

	/// output details for the symbol
	void output_details(std::ostream & out, symbol_entry const * symb);
 
	/// container we work from
	profile_container const & profile;
 
	/// true if we need to show details for each symbols
	bool need_details;
};


/// class to output in a columned format caller/callee and associated samples
class cg_formatter : public formatter {
public:
	/// build a ready to use formatter
	cg_formatter(callgraph_container const & profile);

	/** output callgraph information according to the previously format
	 * specifier set by call(s) to add_format() */
	void output(std::ostream & out, symbol_collection const & syms);
};

/// class to output a columned format symbols plus diff values
class diff_formatter : public formatter {
public:
	/// build a ready to use formatter
	diff_formatter(diff_container const & profile,
		       extra_images const & extra);

	/**
	 * Output a vector of symbols to out according to the output
	 * format specifier previously set by call(s) to add_format()
	 */
	void output(std::ostream & out, diff_collection const & syms);

private:
	/// output a single symbol
	void output(std::ostream & out, diff_symbol const & sym);

};


/// class to output in XML format
class xml_formatter : public formatter {
public:
	/// build a ready to use formatter
	xml_formatter(profile_container const * profile,
		      symbol_collection & symbols, extra_images const & extra,
		      string_filter const & symbol_filter);

	// output body of XML output
	void output(std::ostream & out);

	/** output one symbol symb to out according to the output format
	 * specifier previously set by call(s) to add_format() */
	virtual void output_symbol(std::ostream & out,
		symbol_entry const * symb, size_t lo, size_t hi,
		bool is_module);

	/// output details for the symbol
	std::string output_symbol_details(symbol_entry const * symb,
		size_t & detail_index, size_t const lo, size_t const hi);

	/// set the output_details boolean
	void show_details(bool);

	// output SymbolData XML elements
	void output_symbol_data(std::ostream & out);

private:
	/// container we work from
	profile_container const * profile;
 
	// ordered collection of symbols associated with this profile
	symbol_collection & symbols;

	/// true if we need to show details for each symbols
	bool need_details;

	// count of DetailData items output so far
	size_t detail_count;

	/// with --details we need to reopen the bfd object for each symb to
	/// get it's contents, hence we store the filter used by the bfd ctor.
	string_filter const & symbol_filter;

	void output_sample_data(std::ostream & out,
		sample_entry const & sample, size_t count);

	/// output attribute in XML
	void output_attribute(std::ostream & out, field_datum const & datum,
			      format_flags fl, tag_t tag);

	/// Retrieve a bfd object for this symbol, reopening a new bfd object
	/// only if necessary
	bool get_bfd_object(symbol_entry const * symb, op_bfd * & abfd) const;

	void output_the_symbol_data(std::ostream & out,
		symbol_entry const * symb, op_bfd * & abfd);

	void output_cg_children(std::ostream & out,
		cg_symbol::children const cg_symb, op_bfd * & abfd);
};

// callgraph XML output version
class xml_cg_formatter : public xml_formatter {
public:
	/// build a ready to use formatter
	xml_cg_formatter(callgraph_container const & callgraph,
		symbol_collection & symbols, string_filter const & sf);

	/** output one symbol symb to out according to the output format
	 * specifier previously set by call(s) to add_format() */
	virtual void output_symbol(std::ostream & out,
		symbol_entry const * symb, size_t lo, size_t hi, bool is_module);

private:
	/// container we work from
	callgraph_container const & callgraph;

	void output_symbol_core(std::ostream & out,
		cg_symbol::children const cg_symb,
		std::string const selfname, std::string const qname,
		size_t lo, size_t hi, bool is_module, tag_t tag);
};

} // namespace format_output 


#endif /* !FORMAT_OUTPUT_H */