#pragma once

#include <alib/set>
#include <alib/type_traits>

namespace abstraction {

class TypeQualifiers {
public:
	enum class TypeQualifierSet {
		NONE = 0x0,
		CONST = 0x1,
		LREF = 0x2,
		RREF = 0x4,
	};

	friend constexpr TypeQualifierSet operator | ( TypeQualifierSet first, TypeQualifierSet second ) {
		return static_cast < TypeQualifierSet > ( static_cast < unsigned > ( first ) | static_cast < unsigned > ( second ) );
	}

	friend constexpr TypeQualifierSet operator & ( TypeQualifierSet first, TypeQualifierSet second ) {
		return static_cast < TypeQualifierSet > ( static_cast < unsigned > ( first ) & static_cast < unsigned > ( second ) );
	}

	friend constexpr bool operator && ( TypeQualifierSet first, TypeQualifierSet second ) {
		return ( static_cast < unsigned > ( first ) & static_cast < unsigned > ( second ) ) == static_cast < unsigned > ( second );
	}

	static constexpr bool isConst ( TypeQualifierSet arg ) {
		return arg && TypeQualifiers::TypeQualifierSet::CONST;
	}

	static constexpr bool isRef ( TypeQualifierSet arg ) {
		return isRvalueRef ( arg ) || isLvalueRef ( arg );
	}

	static constexpr bool isRvalueRef ( TypeQualifierSet arg ) {
		return arg && TypeQualifiers::TypeQualifierSet::RREF;
	}

	static constexpr bool isLvalueRef ( TypeQualifierSet arg ) {
		return arg && TypeQualifiers::TypeQualifierSet::LREF;
	}

	template < class Type >
	static constexpr TypeQualifierSet typeQualifiers ( ) {
		TypeQualifierSet res = TypeQualifierSet::NONE;

		if ( std::is_lvalue_reference < Type >::value )
			res = res | TypeQualifierSet::LREF;

		if ( std::is_rvalue_reference < Type >::value )
			res = res | TypeQualifierSet::RREF;

		if ( std::is_const < typename std::remove_reference < Type >::type >::value )
			res = res | TypeQualifierSet::CONST;

		return res;
	}

	friend std::ostream & operator << ( std::ostream & os, TypeQualifierSet typeQualifierSet );
};

} /* namespace abstraction */

