#ifndef _TCUEITHER_HPP
#define _TCUEITHER_HPP
/*-------------------------------------------------------------------------
 * drawElements Quality Program Tester Core
 * ----------------------------------------
 *
 * Copyright 2015 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 Template class that is either type of First or Second.
 *//*--------------------------------------------------------------------*/

#include "tcuDefs.hpp"

namespace tcu
{

/*--------------------------------------------------------------------*//*!
 * \brief Object containing Either First or Second type of object
 *
 * \note Type First and Second are always aligned to same alignment as
 * 		 deUint64.
 * \note This type always uses at least sizeof(bool) + max(sizeof(First*),
 * 		 sizeof(Second*)) + sizeof(deUint64) of memory.
 *//*--------------------------------------------------------------------*/
template<typename First, typename Second>
class Either
{
public:
					Either		(const First& first);
					Either		(const Second& second);
					~Either		(void);

					Either		(const Either<First, Second>& other);
	Either&			operator=	(const Either<First, Second>& other);

	Either&			operator=	(const First& first);
	Either&			operator=	(const Second& second);

	bool			isFirst		(void) const;
	bool			isSecond	(void) const;

	const First&	getFirst	(void) const;
	const Second&	getSecond	(void) const;

	template<typename Type>
	const Type&		get			(void) const;

	template<typename Type>
	bool			is			(void) const;

private:
	void			release		(void);

	bool			m_isFirst;

	union
	{
		First*		m_first;
		Second*		m_second;
	};

	union
	{
		deUint8		m_data[sizeof(First) > sizeof(Second) ? sizeof(First) : sizeof(Second)];
		deUint64	m_align;
	};
} DE_WARN_UNUSED_TYPE;

namespace EitherDetail
{

template<typename Type, typename First, typename Second>
struct Get;

template<typename First, typename Second>
struct Get<First, First, Second>
{
	static const First& get (const Either<First, Second>& either)
	{
		return either.getFirst();
	}
};

template<typename First, typename Second>
struct Get<Second, First, Second>
{
	static const Second& get (const Either<First, Second>& either)
	{
		return either.getSecond();
	}
};

template<typename Type, typename First, typename Second>
const Type& get (const Either<First, Second>& either)
{
	return Get<Type, First, Second>::get(either);
}

template<typename Type, typename First, typename Second>
struct Is;

template<typename First, typename Second>
struct Is<First, First, Second>
{
	static bool is (const Either<First, Second>& either)
	{
		return either.isFirst();
	}
};

template<typename First, typename Second>
struct Is<Second, First, Second>
{
	static bool is (const Either<First, Second>& either)
	{
		return either.isSecond();
	}
};

template<typename Type, typename First, typename Second>
bool is (const Either<First, Second>& either)
{
	return Is<Type, First, Second>::is(either);
}

} // EitherDetail

template<typename First, typename Second>
void Either<First, Second>::release (void)
{
	if (m_isFirst)
		m_first->~First();
	else
		m_second->~Second();

	m_isFirst	= true;
	m_first		= DE_NULL;
}

template<typename First, typename Second>
Either<First, Second>::Either (const First& first)
	: m_isFirst	(true)
{
	m_first = new(m_data)First(first);
}

template<typename First, typename Second>
Either<First, Second>::Either (const Second& second)
	: m_isFirst (false)
{
	m_second = new(m_data)Second(second);
}

template<typename First, typename Second>
Either<First, Second>::~Either (void)
{
	release();
}

template<typename First, typename Second>
Either<First, Second>::Either (const Either<First, Second>& other)
	: m_isFirst	(other.m_isFirst)
{
	if (m_isFirst)
		m_first = new(m_data)First(*other.m_first);
	else
		m_second = new(m_data)Second(*other.m_second);
}

template<typename First, typename Second>
Either<First, Second>& Either<First, Second>::operator= (const Either<First, Second>& other)
{
	if (this == &other)
		return *this;

	release();

	m_isFirst = other.m_isFirst;

	if (m_isFirst)
		m_first = new(m_data)First(*other.m_first);
	else
		m_second = new(m_data)Second(*other.m_second);

	return *this;
}

template<typename First, typename Second>
Either<First, Second>& Either<First, Second>::operator= (const First& first)
{
	release();

	m_isFirst = true;
	m_first = new(m_data)First(first);

	return *this;
}

template<typename First, typename Second>
Either<First, Second>& Either<First, Second>::operator= (const Second& second)
{
	release();

	m_isFirst = false;
	m_second = new(m_data)Second(second);

	return *this;
}

template<typename First, typename Second>
bool Either<First, Second>::isFirst (void) const
{
	return m_isFirst;
}

template<typename First, typename Second>
bool Either<First, Second>::isSecond (void) const
{
	return !m_isFirst;
}

template<typename First, typename Second>
const First& Either<First, Second>::getFirst (void) const
{
	DE_ASSERT(isFirst());
	return *m_first;
}

template<typename First, typename Second>
const Second& Either<First, Second>::getSecond (void) const
{
	DE_ASSERT(isSecond());
	return *m_second;
}

template<typename First, typename Second>
template<typename Type>
const Type& Either<First, Second>::get (void) const
{
	return EitherDetail::get<Type, First, Second>(*this);
}

template<typename First, typename Second>
template<typename Type>
bool Either<First, Second>::is (void) const
{
	return EitherDetail::is<Type, First, Second>(*this);
}

void Either_selfTest (void);

} // tcu

#endif // _TCUEITHER_HPP