#ifndef _TCUTESTLOG_HPP
#define _TCUTESTLOG_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 Test Log C++ Wrapper.
 *//*--------------------------------------------------------------------*/

#include "tcuDefs.hpp"
#include "qpTestLog.h"
#include "tcuTexture.hpp"

#include <sstream>

namespace tcu
{

class Surface;
class MessageBuilder;
class LogImageSet;
class LogImage;
class LogSection;
class LogShaderProgram;
class LogShader;
class LogKernelSource;
class LogSampleList;
class LogValueInfo;
class SampleBuilder;
template<typename T> class LogNumber;

/*--------------------------------------------------------------------*//*!
 * \brief Test log
 *
 * TestLog provides convinient C++ API for logging. The API has been designed
 * around stream operators much like STL iostream library. The following
 * examples demonstrate how to use TestLog.
 *
 * \code
 * TestLog& log = m_testCtx.getLog();
 *
 * // Write message to log.
 * log << TestLog::Message << "Hello, World!" << TestLog::EndMessage;
 * int myNumber = 3;
 * log << TestLog::Message << "Diff is " << myNumber << TestLog::EndMessage;
 *
 * // Write image
 * Surface myImage(256, 256);
 * log << TestLog::Image("TestImage", "My test image", myImage);
 *
 * // Multiple commands can be combined:
 * log << TestLog::Section("Details", "Test case details")
 *     << TestLog::Message << "Here be dragons" << TestLog::EndMessage
 *     << TestLog::ImageSet("Result", "Result images")
 *     << TestLog::Image("ImageA", "Image A", imageA)
 *     << TestLog::Image("ImageB", "Image B", imageB)
 *     << TestLog::EndImageSet << TestLog::EndSection;
 * \endcode
 *//*--------------------------------------------------------------------*/
class TestLog
{
public:
	// Tokens
	static const class BeginMessageToken {}			Message;
	static const class EndMessageToken {}			EndMessage;
	static const class EndImageSetToken {}			EndImageSet;
	static const class EndSectionToken {}			EndSection;
	static const class EndShaderProgramToken {}		EndShaderProgram;
	static const class SampleInfoToken {}			SampleInfo;
	static const class EndSampleInfoToken {}		EndSampleInfo;
	static const class BeginSampleToken {}			Sample;
	static const class EndSampleToken {}			EndSample;
	static const class EndSampleListToken {}		EndSampleList;

	// Typedefs.
	typedef LogImageSet			ImageSet;
	typedef LogImage			Image;
	typedef LogSection			Section;
	typedef LogShaderProgram	ShaderProgram;
	typedef LogShader			Shader;
	typedef LogKernelSource		KernelSource;
	typedef LogSampleList		SampleList;
	typedef LogValueInfo		ValueInfo;
	typedef LogNumber<float>	Float;
	typedef LogNumber<deInt64>	Integer;

	explicit			TestLog					(const char* fileName, deUint32 flags = 0);
						~TestLog				(void);

	MessageBuilder		operator<<				(const BeginMessageToken&);
	MessageBuilder		message					(void);

	TestLog&			operator<<				(const ImageSet& imageSet);
	TestLog&			operator<<				(const Image& image);
	TestLog&			operator<<				(const EndImageSetToken&);

	TestLog&			operator<<				(const Section& section);
	TestLog&			operator<<				(const EndSectionToken&);

	TestLog&			operator<<				(const ShaderProgram& shaderProgram);
	TestLog&			operator<<				(const EndShaderProgramToken&);
	TestLog&			operator<<				(const Shader& shader);

	TestLog&			operator<<				(const KernelSource& kernelSrc);

	template<typename T>
	TestLog&			operator<<				(const LogNumber<T>& number);

	TestLog&			operator<<				(const SampleList& sampleList);
	TestLog&			operator<<				(const SampleInfoToken&);
	TestLog&			operator<<				(const ValueInfo& valueInfo);
	TestLog&			operator<<				(const EndSampleInfoToken&);
	SampleBuilder		operator<<				(const BeginSampleToken&);
	TestLog&			operator<<				(const EndSampleListToken&);

	// Raw api
	void				writeMessage			(const char* message);

	void				startImageSet			(const char* name, const char* description);
	void				endImageSet				(void);
	void				writeImage				(const char* name, const char* description, const ConstPixelBufferAccess& surface, const Vec4& scale, const Vec4& bias, qpImageCompressionMode compressionMode = QP_IMAGE_COMPRESSION_MODE_BEST);
	void				writeImage				(const char* name, const char* description, qpImageCompressionMode compressionMode, qpImageFormat format, int width, int height, int stride, const void* data);

	void				startSection			(const char* name, const char* description);
	void				endSection				(void);

	void				startShaderProgram		(bool linkOk, const char* linkInfoLog);
	void				endShaderProgram		(void);
	void				writeShader				(qpShaderType type, const char* source, bool compileOk, const char* infoLog);

	void				writeKernelSource		(const char* source);
	void				writeCompileInfo		(const char* name, const char* description, bool compileOk, const char* infoLog);

	void				writeFloat				(const char* name, const char* description, const char* unit, qpKeyValueTag tag, float value);
	void				writeInteger			(const char* name, const char* description, const char* unit, qpKeyValueTag tag, deInt64 value);

	void				startEglConfigSet		(const char* name, const char* description);
	void				writeEglConfig			(const qpEglConfigInfo* config);
	void				endEglConfigSet			(void);

	void				startCase				(const char* testCasePath, qpTestCaseType testCaseType);
	void				endCase					(qpTestResult result, const char* description);
	void				terminateCase			(qpTestResult result);

	void				startSampleList			(const std::string& name, const std::string& description);
	void				startSampleInfo			(void);
	void				writeValueInfo			(const std::string& name, const std::string& description, const std::string& unit, qpSampleValueTag tag);
	void				endSampleInfo			(void);
	void				startSample				(void);
	void				writeSampleValue		(double value);
	void				writeSampleValue		(deInt64 value);
	void				endSample				(void);
	void				endSampleList			(void);

private:
						TestLog					(const TestLog& other); // Not allowed!
	TestLog&			operator=				(const TestLog& other); // Not allowed!

	qpTestLog*			m_log;
};

class MessageBuilder
{
public:
	explicit				MessageBuilder		(TestLog* log) : m_log(log) {}
							~MessageBuilder		(void) {}

	std::string				toString			(void) const { return m_str.str(); }

	TestLog&				operator<<			(const TestLog::EndMessageToken&);

	template <typename T>
	MessageBuilder&			operator<<			(const T& value);

							MessageBuilder		(const MessageBuilder& other);
	MessageBuilder&			operator=			(const MessageBuilder& other);

private:
	TestLog*				m_log;
	std::ostringstream		m_str;
};

class SampleBuilder
{
public:
							SampleBuilder		(TestLog* log) : m_log(log) {}

	SampleBuilder&			operator<<			(int v)		{ m_values.push_back(Value((deInt64)v));	return *this; }
	SampleBuilder&			operator<<			(deInt64 v)	{ m_values.push_back(Value(v));				return *this; }
	SampleBuilder&			operator<<			(float v)	{ m_values.push_back(Value((double)v));		return *this; }
	SampleBuilder&			operator<<			(double v)	{ m_values.push_back(Value(v));				return *this; }

	TestLog&				operator<<			(const TestLog::EndSampleToken&);

private:
	struct Value
	{
		enum Type { TYPE_INT64 = 0, TYPE_FLOAT64, TYPE_LAST };

		Type	type;
		union
		{
			deInt64		int64;
			double		float64;
		} value;

		Value (void)		: type(TYPE_LAST)		{ value.int64 = 0;		}
		Value (double v)	: type(TYPE_FLOAT64)	{ value.float64 = v;	}
		Value (deInt64 v)	: type(TYPE_INT64)		{ value.int64 = v;		}
	};

	TestLog*				m_log;
	std::vector<Value>		m_values;
};

class LogImageSet
{
public:
	LogImageSet (const std::string& name, const std::string& description)
		: m_name		(name)
		, m_description	(description)
	{
	}

	void write (TestLog& log) const;

private:
	std::string		m_name;
	std::string		m_description;
};

// \note Doesn't take copy of surface contents
class LogImage
{
public:
	LogImage (const std::string& name, const std::string& description, const Surface& surface, qpImageCompressionMode compression = QP_IMAGE_COMPRESSION_MODE_BEST);

	LogImage (const std::string& name, const std::string& description, const ConstPixelBufferAccess& access, qpImageCompressionMode compression = QP_IMAGE_COMPRESSION_MODE_BEST);

	LogImage (const std::string& name, const std::string& description, const ConstPixelBufferAccess& access, const Vec4& scale, const Vec4& bias, qpImageCompressionMode compression = QP_IMAGE_COMPRESSION_MODE_BEST)
		: m_name		(name)
		, m_description	(description)
		, m_access		(access)
		, m_scale		(scale)
		, m_bias		(bias)
		, m_compression	(compression)
	{
	}

	void write (TestLog& log) const;

private:
	std::string				m_name;
	std::string				m_description;
	ConstPixelBufferAccess	m_access;
	Vec4					m_scale;
	Vec4					m_bias;
	qpImageCompressionMode	m_compression;
};

class LogSection
{
public:
	LogSection (const std::string& name, const std::string& description)
		: m_name		(name)
		, m_description	(description)
	{
	}

	void write (TestLog& log) const;

private:
	std::string		m_name;
	std::string		m_description;
};

class LogShaderProgram
{
public:
	LogShaderProgram (bool linkOk, const std::string& linkInfoLog)
		: m_linkOk		(linkOk)
		, m_linkInfoLog	(linkInfoLog)
	{
	}

	void write (TestLog& log) const;

private:
	bool			m_linkOk;
	std::string		m_linkInfoLog;
};

class LogShader
{
public:
	LogShader (qpShaderType type, const std::string& source, bool compileOk, const std::string& infoLog)
		: m_type		(type)
		, m_source		(source)
		, m_compileOk	(compileOk)
		, m_infoLog		(infoLog)
	{
	}

	void write (TestLog& log) const;

private:
	qpShaderType	m_type;
	std::string		m_source;
	bool			m_compileOk;
	std::string		m_infoLog;
};

class LogKernelSource
{
public:
	explicit LogKernelSource (const std::string& source)
		: m_source(source)
	{
	}

	void write (TestLog& log) const;

private:
	std::string	m_source;
};

class LogSampleList
{
public:
	LogSampleList (const std::string& name, const std::string& description)
		: m_name		(name)
		, m_description	(description)
	{
	}

	void write (TestLog& log) const;

private:
	std::string		m_name;
	std::string		m_description;
};

class LogValueInfo
{
public:
	LogValueInfo (const std::string& name, const std::string& description, const std::string& unit, qpSampleValueTag tag)
		: m_name		(name)
		, m_description	(description)
		, m_unit		(unit)
		, m_tag			(tag)
	{
	}

	void write (TestLog& log) const;

private:
	std::string			m_name;
	std::string			m_description;
	std::string			m_unit;
	qpSampleValueTag	m_tag;
};

template<typename T>
class LogNumber
{
public:
	LogNumber (const std::string& name, const std::string& desc, const std::string& unit, qpKeyValueTag tag, T value)
		: m_name	(name)
		, m_desc	(desc)
		, m_unit	(unit)
		, m_tag		(tag)
		, m_value	(value)
	{
	}

	void write (TestLog& log) const;

private:
	std::string		m_name;
	std::string		m_desc;
	std::string		m_unit;
	qpKeyValueTag	m_tag;
	T				m_value;
};

// Section helper that closes section when leaving scope.
class ScopedLogSection
{
public:
	ScopedLogSection (TestLog& log, const std::string& name, const std::string& description)
		: m_log(log)
	{
		m_log << TestLog::Section(name, description);
	}

	~ScopedLogSection (void)
	{
		m_log << TestLog::EndSection;
	}

private:
	TestLog& m_log;
};

// TestLog stream operators.

inline TestLog& TestLog::operator<< (const ImageSet& imageSet)			{ imageSet.write(*this);	return *this;	}
inline TestLog& TestLog::operator<< (const Image& image)				{ image.write(*this);		return *this;	}
inline TestLog& TestLog::operator<< (const EndImageSetToken&)			{ endImageSet();			return *this;	}
inline TestLog& TestLog::operator<< (const Section& section)			{ section.write(*this);		return *this;	}
inline TestLog& TestLog::operator<< (const EndSectionToken&)			{ endSection();				return *this;	}
inline TestLog& TestLog::operator<< (const ShaderProgram& shaderProg)	{ shaderProg.write(*this);	return *this;	}
inline TestLog& TestLog::operator<< (const EndShaderProgramToken&)		{ endShaderProgram();		return *this;	}
inline TestLog& TestLog::operator<< (const Shader& shader)				{ shader.write(*this);		return *this;	}
inline TestLog& TestLog::operator<< (const KernelSource& kernelSrc)		{ kernelSrc.write(*this);	return *this;	}
inline TestLog&	TestLog::operator<<	(const SampleList& sampleList)		{ sampleList.write(*this);	return *this;	}
inline TestLog&	TestLog::operator<<	(const SampleInfoToken&)			{ startSampleInfo();		return *this;	}
inline TestLog&	TestLog::operator<<	(const ValueInfo& valueInfo)		{ valueInfo.write(*this);	return *this;	}
inline TestLog&	TestLog::operator<<	(const EndSampleInfoToken&)			{ endSampleInfo();			return *this;	}
inline TestLog&	TestLog::operator<<	(const EndSampleListToken&)			{ endSampleList();			return *this;	}

template<typename T>
inline TestLog& TestLog::operator<< (const LogNumber<T>& number)
{
	number.write(*this);
	return *this;
}

inline TestLog& operator<< (TestLog& log, const std::exception& e)
{
	// \todo [2012-10-18 pyry] Print type info?
	return log << TestLog::Message << e.what() << TestLog::EndMessage;
}

// Utility class inline implementations.

template <typename T>
inline MessageBuilder& MessageBuilder::operator<< (const T& value)
{
	// Overload stream operator to implement custom format
	m_str << value;
	return *this;
}

inline MessageBuilder TestLog::operator<< (const BeginMessageToken&)
{
	return MessageBuilder(this);
}

inline MessageBuilder TestLog::message (void)
{
	return MessageBuilder(this);
}

inline SampleBuilder TestLog::operator<< (const BeginSampleToken&)
{
	return SampleBuilder(this);
}

inline void LogImageSet::write (TestLog& log) const
{
	log.startImageSet(m_name.c_str(), m_description.c_str());
}

inline void LogImage::write (TestLog& log) const
{
	log.writeImage(m_name.c_str(), m_description.c_str(), m_access, m_scale, m_bias, m_compression);
}

inline void LogSection::write (TestLog& log) const
{
	log.startSection(m_name.c_str(), m_description.c_str());
}

inline void LogShaderProgram::write (TestLog& log) const
{
	log.startShaderProgram(m_linkOk, m_linkInfoLog.c_str());
}

inline void LogShader::write (TestLog& log) const
{
	log.writeShader(m_type, m_source.c_str(), m_compileOk, m_infoLog.c_str());
}

inline void LogKernelSource::write (TestLog& log) const
{
	log.writeKernelSource(m_source.c_str());
}

inline void LogSampleList::write (TestLog& log) const
{
	log.startSampleList(m_name, m_description);
}

inline void LogValueInfo::write (TestLog& log) const
{
	log.writeValueInfo(m_name, m_description, m_unit, m_tag);
}

template<>
inline void LogNumber<float>::write (TestLog& log) const
{
	log.writeFloat(m_name.c_str(), m_desc.c_str(), m_unit.c_str(), m_tag, m_value);
}

template<>
inline void LogNumber<deInt64>::write (TestLog& log) const
{
	log.writeInteger(m_name.c_str(), m_desc.c_str(), m_unit.c_str(), m_tag, m_value);
}

} // tcu

#endif // _TCUTESTLOG_HPP