#ifndef _TCUFORMATUTIL_HPP
#define _TCUFORMATUTIL_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Tester Core
 * ----------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief String format utilities.
 *//*--------------------------------------------------------------------*/

#include "tcuDefs.hpp"
#include "deString.h"

#include <ostream>
#include <string>

namespace tcu
{
namespace Format
{

// Hexadecimal value formatter.
template <size_t NumDigits>
class Hex
{
public:
	Hex (deUint64 value_) : value(value_) {}

	std::ostream& toStream (std::ostream& stream) const
	{
		return stream << this->toString();
	}

	std::string toString (void) const
	{
		DE_STATIC_ASSERT(0 < NumDigits && NumDigits <= 16);

		const char longFmt[]	= {'0', 'x', '%', '0', '0' + NumDigits/10, '0' + NumDigits%10, 'l', 'l', 'x', 0};
		const char shortFmt[]	= {'0', 'x', '%', '0', '0' + NumDigits, 'l', 'l', 'x', 0};

		char buf[sizeof(deUint64)*2 + 3];
		deSprintf(buf, sizeof(buf), NumDigits > 9 ? longFmt : shortFmt, value);

		return std::string(buf);
	}

private:
	deUint64 value;
};

template <size_t NumDigits>
std::ostream& operator<< (std::ostream& stream, tcu::Format::Hex<NumDigits> hex)
{
	return hex.toStream(stream);
}

// Bitfield formatter.

class BitDesc
{
public:
	deUint64	bit;
	const char*	name;

	BitDesc (deUint64 bit_, const char* name_) : bit(bit_), name(name_) {}
};

#define TCU_BIT_DESC(BIT) tcu::Format::BitDesc(BIT, #BIT)

template <size_t BitfieldSize>
class Bitfield
{
public:
	Bitfield (deUint64 value, const BitDesc* begin, const BitDesc* end)
		: m_value	(value)
		, m_begin	(begin)
		, m_end		(end)
	{
	}

	std::ostream& toStream (std::ostream& stream)
	{
		deUint64 bitsLeft = m_value;
		for (const BitDesc* curDesc = m_begin; curDesc != m_end; curDesc++)
		{
			if (curDesc->bit & bitsLeft)
			{
				if (bitsLeft != m_value)
					stream << "|";
				stream << curDesc->name;
				bitsLeft ^= curDesc->bit;
			}
		}

		if (bitsLeft != 0)
		{
			if (bitsLeft != m_value)
				stream << "|";
			stream << Hex<BitfieldSize/4>(bitsLeft);
		}

		return stream;
	}

private:
	deUint64			m_value;
	const BitDesc*		m_begin;
	const BitDesc*		m_end;
};

template <size_t BitfieldSize>
inline std::ostream& operator<< (std::ostream& stream, Bitfield<BitfieldSize> decoder)
{
	return decoder.toStream(stream);
}

// Enum formatter.
// \todo [2012-10-30 pyry] Use template for GetName.
template <typename T, size_t NumBytes = sizeof(T)>
class Enum
{
public:
	typedef const char* (*GetNameFunc) (T value);

	Enum (GetNameFunc getName, T value)
		: m_getName	(getName)
		, m_value (value)
	{
	}

	std::ostream& toStream (std::ostream& stream) const
	{
		const char* name = m_getName(m_value);
		if (name)
			return stream << name;
		else
			return stream << Hex<NumBytes*2>((deUint64)m_value);
	}

	std::string toString (void) const
	{
		const char* name = m_getName(m_value);
		if (name)
			return std::string(name);
		else
			return Hex<NumBytes*2>((deUint64)m_value).toString();
	}

private:
	const GetNameFunc	m_getName;
	const T				m_value;
};

template <typename T, size_t NumBytes>
inline std::ostream& operator<< (std::ostream& stream, const Enum<T, NumBytes>& fmt) { return fmt.toStream(stream); }

// Array formatters.

template <typename Iterator>
class Array
{
public:
	Iterator	begin;
	Iterator	end;

	Array (const Iterator& begin_, const Iterator& end_) : begin(begin_), end(end_) {}
};

template <typename T>
class ArrayPointer
{
public:
	const T*	arr;
	int			size;

	ArrayPointer (const T* arr_, int size_) : arr(arr_), size(size_) {}
};

template <typename Iterator>
std::ostream& operator<< (std::ostream& str, const Array<Iterator>& fmt)
{
	str << "{ ";
	for (Iterator cur = fmt.begin; cur != fmt.end; ++cur)
	{
		if (cur != fmt.begin)
			str << ", ";
		str << *cur;
	}
	str << " }";
	return str;
}

template <typename T>
std::ostream& operator<< (std::ostream& str, const ArrayPointer<T>& fmt)
{
	if (fmt.arr != DE_NULL)
		return str << Array<const T*>(fmt.arr, fmt.arr+fmt.size);
	else
		return str << "(null)";
}

// Hex format iterator (useful for combining with ArrayFormatter).
// \todo [2012-10-30 pyry] Implement more generic format iterator.

template <typename T, typename Iterator = const T*>
class HexIterator
{
public:
										HexIterator			(Iterator iter) : m_iter(iter) {}

	HexIterator<T, Iterator>&			operator++			(void)	{ ++m_iter; return *this;		}
	HexIterator<T, Iterator>			operator++			(int)	{ return HexIterator(m_iter++);	}

	bool								operator==			(const HexIterator<T, Iterator>& other) const { return m_iter == other.m_iter; }
	bool								operator!=			(const HexIterator<T, Iterator>& other) const { return m_iter != other.m_iter; }

#if !defined(__INTELLISENSE__)
	// Intellisense in VS2013 crashes when parsing this.
	Hex<sizeof(T)*2>					operator*			(void) const { return Hex<sizeof(T)*2>(*m_iter);	}
#endif

private:
	Iterator							m_iter;
};

} // Format

template <int Bits>		inline deUint64 makeMask64			(void)				{ return (1ull<<Bits)-1;								}
template <>				inline deUint64 makeMask64<64>		(void)				{ return ~0ull;											}
template <typename T>	inline deUint64	toUint64			(T value)			{ return (deUint64)value & makeMask64<sizeof(T)*8>();	}

/** Format value as hexadecimal number. */
template <size_t NumDigits, typename T>
inline Format::Hex<NumDigits> toHex (T value)
{
	return Format::Hex<NumDigits>(toUint64(value));
}

/** Format value as hexadecimal number. */
template <typename T>
inline Format::Hex<sizeof(T)*2> toHex (T value)
{
	return Format::Hex<sizeof(T)*2>(toUint64(value));
}

/** Decode and format bitfield. */
template <typename T, size_t Size>
inline Format::Bitfield<sizeof(T)*8> formatBitfield (T value, const Format::BitDesc (&desc)[Size])
{
	return Format::Bitfield<sizeof(T)*8>((deUint64)value, &desc[0], &desc[Size]);
}

/** Format array contents. */
template <typename Iterator>
inline Format::Array<Iterator> formatArray (const Iterator& begin, const Iterator& end)
{
	return Format::Array<Iterator>(begin, end);
}

/** Format array contents. */
template <typename T>
inline Format::ArrayPointer<T> formatArray (const T* arr, int size)
{
	return Format::ArrayPointer<T>(arr, size);
}

/** Format array contents. */
template <typename T, int Size>
inline Format::ArrayPointer<T> formatArray (const T (&arr)[Size])
{
	return Format::ArrayPointer<T>(arr, Size);
}

} // tcu

#endif // _TCUFORMATUTIL_HPP