#ifndef _XETESTCASE_HPP
#define _XETESTCASE_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Test Executor
 * ------------------------------------------
 *
 * 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 case.
 *//*--------------------------------------------------------------------*/

#include "xeDefs.hpp"

#include <string>
#include <vector>
#include <set>
#include <map>

namespace xe
{

enum TestCaseType
{
	TESTCASETYPE_SELF_VALIDATE,
	TESTCASETYPE_CAPABILITY,
	TESTCASETYPE_ACCURACY,
	TESTCASETYPE_PERFORMANCE,

	TESTCASETYPE_LAST
};

const char* getTestCaseTypeName (TestCaseType caseType);

enum TestNodeType
{
	TESTNODETYPE_ROOT,
	TESTNODETYPE_GROUP,
	TESTNODETYPE_TEST_CASE,

	TESTNODETYPE_LAST
};

class TestGroup;
class TestCase;

class TestNode
{
public:
	virtual				~TestNode			(void) {}

	TestNodeType		getNodeType			(void) const { return m_nodeType;		}
	const char*			getName				(void) const { return m_name.c_str();	}
	const TestGroup*	getParent			(void) const { return m_parent;			}

	void				getFullPath			(std::string& path) const;
	std::string			getFullPath			(void) const { std::string str; getFullPath(str); return str; }

	const TestNode*		find				(const char* path) const;
	TestNode*			find				(const char* path);

protected:
						TestNode			(TestGroup* parent, TestNodeType nodeType, const char* name, const char* desc);

private:
						TestNode			(const TestNode& other);
	TestNode&			operator=			(const TestNode& other);

	TestGroup*			m_parent;
	TestNodeType		m_nodeType;
	std::string			m_name;
	std::string			m_description;
};

class TestGroup : public TestNode
{
public:
								~TestGroup			(void);

	int							getNumChildren		(void) const	{ return (int)m_children.size();	}
	TestNode*					getChild			(int ndx)		{ return m_children[ndx];			}
	const TestNode*				getChild			(int ndx) const	{ return m_children[ndx];			}

	TestNode*					findChildNode		(const char* path);
	const TestNode*				findChildNode		(const char* path) const;

	TestGroup*					createGroup			(const char* name, const char* description);
	TestCase*					createCase			(TestCaseType caseType, const char* name, const char* description);

protected:
								TestGroup			(TestGroup* parent, TestNodeType nodeType, const char* name, const char* description);

private:
	std::vector<TestNode*>		m_children;
	std::set<std::string>		m_childNames;		//!< Used for checking for duplicate test case names.

	// For adding TestCase to m_children. \todo [2012-06-15 pyry] Is the API broken perhaps?
	friend class TestNode;
};

class TestRoot : public TestGroup
{
public:
								TestRoot			(void);
};

class TestCase : public TestNode
{
public:
								~TestCase			(void);

	TestCaseType				getCaseType			(void) const { return m_caseType; }

	static TestCase*			createAsChild		(TestGroup* parent, TestCaseType caseType, const char* name, const char* description);

protected:
								TestCase			(TestGroup* parent, TestCaseType caseType, const char* name, const char* description);

private:
	TestCaseType				m_caseType;
};

// Helper class for efficiently constructing TestCase hierarchy from test case list.
class TestHierarchyBuilder
{
public:
										TestHierarchyBuilder		(TestRoot* root);
										~TestHierarchyBuilder		(void);

	TestCase*							createCase					(const char* path, TestCaseType caseType);

private:
										TestHierarchyBuilder		(const TestHierarchyBuilder& other);
	TestHierarchyBuilder&				operator=					(const TestHierarchyBuilder& other);

	TestRoot*							m_root;
	std::map<std::string, TestGroup*>	m_groupMap;
};

// Helper class for computing and iterating test sets.
class TestSet
{
public:
							TestSet			(void) {}
							~TestSet		(void) {}

	bool					empty			(void) const { return m_set.empty(); }

	void					add				(const TestNode* node);
	void					addCase			(const TestCase* testCase);
	void					addGroup		(const TestGroup* testGroup);

	void					remove			(const TestNode* node);
	void					removeCase		(const TestCase* testCase);
	void					removeGroup		(const TestGroup* testGroup);

	bool					hasNode			(const TestNode* node) const { return m_set.find(node) != m_set.end(); }

private:
	std::set<const TestNode*> m_set;
};

class ConstTestNodeIterator
{
public:
	static ConstTestNodeIterator	begin					(const TestNode* root);
	static ConstTestNodeIterator	end						(const TestNode* root);

	ConstTestNodeIterator&			operator++				(void);
	ConstTestNodeIterator			operator++				(int);

	const TestNode*					operator*				(void) const;

	bool							operator!=				(const ConstTestNodeIterator& other) const;

protected:
									ConstTestNodeIterator	(const TestNode* root);

private:
	struct GroupState
	{
		GroupState (const TestGroup* group_) : group(group_), childNdx(0) {}

		const TestGroup*	group;
		int					childNdx;

		bool operator!= (const GroupState& other) const
		{
			return group != other.group || childNdx != other.childNdx;
		}

		bool operator== (const GroupState& other) const
		{
			return group == other.group && childNdx == other.childNdx;
		}
	};

	const TestNode*					m_root;
	std::vector<GroupState>			m_iterStack;
};

// \todo [2012-06-19 pyry] Implement following iterators:
//  - TestNodeIterator
//  - ConstTestSetIterator
//  - TestSetIterator

} // xe

#endif // _XETESTCASE_HPP