#ifndef _TCUTHREADUTIL_HPP
#define _TCUTHREADUTIL_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 Thread test utilities
 *//*--------------------------------------------------------------------*/

#include "tcuDefs.hpp"
#include "deSharedPtr.hpp"
#include "deMutex.hpp"
#include "deSemaphore.hpp"
#include "deThread.hpp"
#include "deRandom.hpp"

#include <vector>
#include <sstream>

namespace tcu
{
namespace ThreadUtil
{
// Event object for synchronizing threads
class Event
{
public:
	enum Result
	{
		RESULT_NOT_READY = 0,
		RESULT_OK,
		RESULT_FAILED
	};

					Event 		(void);
					~Event		(void);
	void			setResult	(Result result);
	Result			waitReady	(void);
	Result			getResult	(void) const { return m_result; }

private:
	volatile Result	m_result;
	volatile int	m_waiterCount;
	de::Semaphore	m_waiters;
	de::Mutex		m_lock;

	// Disabled
					Event		(const Event&);
	Event&			operator=	(const Event&);
};

// Base class for objects which modifications should be tracked between threads
class Object
{
public:
										Object		(const char* type, de::SharedPtr<Event> createEvent);
	virtual								~Object		(void);
	const char*							getType		(void) const { return m_type; }

	// Used by class Operation only
	void								read		(de::SharedPtr<Event> event, std::vector<de::SharedPtr<Event> >& deps);
	void								modify		(de::SharedPtr<Event> event, std::vector<de::SharedPtr<Event> >& deps);

private:
	const char*							m_type;
	de::SharedPtr<Event>				m_modify;
	std::vector<de::SharedPtr<Event> >	m_reads;

	// Disabled
										Object		(const Object&);
	Object&								operator=	(const Object&);
};

class Thread;

class MessageBuilder
{
public:
						MessageBuilder		(Thread& thread) : m_thread(thread) {}
						MessageBuilder		(const MessageBuilder& other) : m_thread(other.m_thread), m_stream(other.m_stream.str()) {}
	template<class T>
	MessageBuilder&		operator<<			(const T& t) { m_stream << t; return *this; }

	class EndToken
	{
	public:
						EndToken			(void) {}
	};

	void 				operator<<			(const EndToken&);

private:
	Thread&				m_thread;
	std::stringstream	m_stream;
};

class Message
{
public:
						Message		(deUint64 time, const char* message) : m_time(time), m_message(message) {}

	deUint64			getTime		(void) const { return m_time; }
	const std::string&	getMessage	(void) const { return m_message; }

	static const MessageBuilder::EndToken End;

private:
	deUint64			m_time;
	std::string			m_message;
};

// Base class for operations executed by threads
class Operation
{
public:
											Operation		(const char* name);
	virtual									~Operation		(void);

	const char*								getName			(void) const { return m_name; }
	de::SharedPtr<Event>					getEvent		(void) { return m_event; }

	void									readObject		(de::SharedPtr<Object> object) { object->read(m_event, m_deps); }
	void									modifyObject	(de::SharedPtr<Object> object) { object->modify(m_event, m_deps); }

	virtual void							exec			(Thread& thread) = 0;	//!< Overwritten by inherited class to perform actual operation
	virtual void							execute			(Thread& thread);		//!< May Be overwritten by inherited class to change how syncronization is done

protected:
	const char*								m_name;
	std::vector<de::SharedPtr<Event> >		m_deps;
	de::SharedPtr<Event>					m_event;

											Operation		(const Operation&);
	Operation&								operator=		(const Operation&);
};

class Thread : public de::Thread
{
public:
	enum ThreadStatus
	{
		THREADSTATUS_NOT_STARTED = 0,
		THREADSTATUS_INIT_FAILED,
		THREADSTATUS_RUNNING,
		THREADSTATUS_READY,
		THREADSTATUS_FAILED,
		THREADSTATUS_NOT_SUPPORTED
	};
							Thread				(deUint32 seed);
							~Thread				(void);

	virtual void			init				(void) {}	//!< Called first before any Operation

	// \todo [mika] Should the result of execution be passed to deinit?
	virtual void			deinit				(void) {}	//!< Called after after operation

	void					addOperation		(Operation* operation);

	void					exec				(void);

	deUint8*				getDummyData		(size_t size);	//!< Return data pointer that contains at least size bytes. Valid until next call

	ThreadStatus			getStatus			(void) const { return m_status; }

	MessageBuilder			newMessage			(void) { return MessageBuilder(*this); }
	de::Random&				getRandom			(void) { return m_random; }

	// Used to by test case to read log messages
	int						getMessageCount		(void) const;
	Message					getMessage			(int index) const;

	// Used by message builder
	void					pushMessage			(const std::string& str);

private:
	virtual void			run					(void);

	std::vector<Operation*>	m_operations;
	de::Random				m_random;

	mutable de::Mutex		m_messageLock;
	std::vector<Message>	m_messages;
	ThreadStatus			m_status;
	std::vector<deUint8>	m_dummyData;

	// Disabled
							Thread				(const Thread&);
	Thread					operator=			(const Thread&);
};

class DataBlock : public Object
{
public:
					DataBlock	(de::SharedPtr<Event> event);

	void			setData		(size_t size, const void* data);
	const deUint8*	getData		(void) const { return &(m_data[0]); }
	size_t			getSize		(void) const { return m_data.size(); }

private:
	std::vector<deUint8> m_data;
};


class CompareData : public Operation
{
public:
			CompareData	(de::SharedPtr<DataBlock> a, de::SharedPtr<DataBlock> b);
	void	exec		(Thread& thread);

private:
	de::SharedPtr<DataBlock>	m_a;
	de::SharedPtr<DataBlock>	m_b;
};

} // ThreadUtil
} // tcu

#endif // _TCUTHREADUTIL_HPP