/*
 * \brief  Interface for textual output
 * \author Norman Feske
 * \date   2016-05-03
 */

/*
 * Copyright (C) 2016-2017 Genode Labs GmbH
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU Affero General Public License version 3.
 */

#ifndef _INCLUDE__BASE__OUTPUT_H_
#define _INCLUDE__BASE__OUTPUT_H_

#include <base/stdint.h>
#include <base/error.h>
#include <util/interface.h>

namespace Genode { struct Output; }


struct Genode::Output : Interface
{
	/**
	 * Output single character
	 */
	virtual void out_char(char) = 0;

	/**
	 * Output string
	 *
	 * \param n  maximum number of characters to output
	 *
	 * The output stops on the first occurrence of a null character in the
	 * string or after 'n' characters.
	 *
	 * The default implementation uses 'out_char'. This method may be
	 * overridden by the backend for improving efficiency.
	 */
	virtual void out_string(char const *str, size_t n = ~0UL);

	/**
	 * Helper for the sequential output of a variable list of arguments
	 */
	static void out_args(Output &output, auto && head, auto &&... tail)
	{
		print(output, head);
		out_args(output, tail...);
	}

	static void out_args(Output &output, auto && last) { print(output, last); }
};


namespace Genode {

	/**
	 * Print null-terminated string
	 */
	void print(Output &output, char const *);

	/**
	 * Disallow printing non-const character buffers
	 *
	 * For 'char *' types, it is unclear whether the argument should be printed
	 * as a pointer or a string. The call must resolve this ambiguity by either
	 * casting the argument to 'void *' or wrapping it in a 'Cstring' object.
	 */
	void print(Output &, char *) = delete;

	/**
	 * Print pointer value
	 */
	void print(Output &output, void const *);

	/**
	 * Print arbitrary pointer types
	 *
	 * This function template takes precedence over the one that takes a
	 * constant object reference as argument.
	 */
	static inline void print(Output &output, auto *ptr)
	{
		print(output, (void const *)ptr);
	}

	/**
	 * Print unsigned long value
	 */
	void print(Output &output, unsigned long);

	/**
	 * Print unsigned long long value
	 */
	void print(Output &output, unsigned long long);

	/*
	 * Overloads for unsigned integer types
	 */
	static inline void print(Output &o, unsigned char  v) { print(o, (unsigned long)v); }
	static inline void print(Output &o, unsigned short v) { print(o, (unsigned long)v); }
	static inline void print(Output &o, unsigned int   v) { print(o, (unsigned long)v); }

	/**
	 * Print signed long value
	 */
	void print(Output &output, long);

	/**
	 * Print signed long long value
	 */
	void print(Output &output, long long);

	/*
	 * Overloads for signed integer types
	 */
	static inline void print(Output &o, char  v) { print(o, (long)v); }
	static inline void print(Output &o, short v) { print(o, (long)v); }
	static inline void print(Output &o, int   v) { print(o, (long)v); }

	/**
	 * Print bool value
	 */
	static inline void print(Output &output, bool value)
	{
		print(output, (int)value);
	}

	/**
	 * Print single-precision float
	 */
	void print(Output &output, float);

	/**
	 * Print double-precision float
	 */
	void print(Output &output, double);

	/**
	 * Helper for the hexadecimal output of integer values
	 *
	 * To output an integer value as hexadecimal number, the value can be
	 * wrapped into an 'Hex' object, thereby selecting the corresponding
	 * overloaded 'print' function below.
	 */
	class Hex
	{
		public:

			enum Prefix { PREFIX, OMIT_PREFIX };
			enum Pad    { PAD,    NO_PAD };

		private:

			unsigned long long const _value;
			size_t             const _digits;
			Prefix             const _prefix;
			Pad                const _pad;

		public:

			/**
			 * Constructor
			 *
			 * \param prefix  by default, the value is prepended with the prefix
			 *                '0x'. The prefix can be suppressed by specifying
			 *                'OMIT_PREFIX' as argument.
			 * \param pad     by default, leading zeros are stripped from the
			 *                output. If set to 'PAD', the leading zeros will be
			 *                printed.
			 */
			explicit Hex(auto value, Prefix prefix = PREFIX, Pad pad = NO_PAD)
			: _value(value), _digits(2*sizeof(value)), _prefix(prefix), _pad(pad) { }

			void print(Output &output) const;
	};

	/**
	 * Print range as hexadecimal format
	 *
	 * This helper is intended for the output for memory-address ranges. For
	 * brevity, it omits the '0x' prefix from the numbers. The numbers are
	 * padded with leading zeros to foster the horizontal alignment of
	 * consecutive outputs (like a table of address ranges).
	 */
	template <typename T>
	struct Hex_range
	{
		T const base;
		size_t const len;

		Hex_range(T base, size_t len) : base(base), len(len) { }

		void print(Output &out) const;
	};

	/**
	 * Helper for the output of an individual character
	 *
	 * When printing a 'char' value, it appears as an integral number. By
	 * wrapping the value in a 'Char' object, it appears as character instead.
	 */
	struct Char
	{
		char const c;

		explicit Char(char c) : c(c) { }

		void print(Output &output) const { output.out_char(c); }
	};

	/**
	 * Print error codes defined at base/error.h
	 */
	void print(Output &, Alloc_error);

	struct Ok;

	/**
	 * Print success of 'Attempt'
	 */
	static inline void print(Output &out, Ok const &) { print(out, "ok"); }

	/**
	 * Print information about object 'obj'
	 *
	 * The object type must provide a const 'print(Output &)' method that
	 * produces the textual representation of the object.
	 *
	 * In contrast to overloads of the 'Genode::print' function, the 'T::print'
	 * method is able to access object-internal state, which can thereby be
	 * incorporated into the textual output.
	 */
	static inline void print(Output &output, auto const &obj)
	{
		obj.print(output);
	}

	/**
	 * Print a variable number of arguments
	 */
	static inline void print(Output &output, auto const &head, auto &&... tail)
	{
		Output::out_args(output, head, tail...);
	}

	static inline size_t num_printed_bytes(auto &&... printable_args)
	{
		struct Counted : Output
		{
			size_t num_bytes = 0;

			void out_char  (char)                   override { num_bytes++; }
			void out_string(char const *, size_t n) override { num_bytes += n; }
		};

		Counted counted { };

		print(counted, printable_args...);

		return counted.num_bytes;
	}
}


template <typename T>
void Genode::Hex_range<T>::print(Output &out) const
{
	using Genode::print;

	Hex const from(base, Hex::OMIT_PREFIX, Hex::PAD);

	T const end = (T)(base + len);

	/* if end at integer limit, use ']' as closing delimiter */
	if (base && end == 0) {
		Hex const inclusive_to((T)(end - 1), Hex::OMIT_PREFIX, Hex::PAD);
		print(out, "[", from, ",", inclusive_to, "]");
		return;
	}

	/* use exclusive upper limit for ordinary ranges  */
	print(out, "[", from, ",", Hex(end, Hex::OMIT_PREFIX, Hex::PAD), ")");

	/* output warning on integer-overflowing upper limit or empty range */
	if (base && end < base) print(out, " (overflow!)");
	if (len == 0)           print(out, " (empty!)");
}

#endif /* _INCLUDE__BASE__OUTPUT_H_ */
