/**
	@file	EnumExtension.h
*/
#pragma once
#include <assert.h>
#include <string>
#include <sstream>
#include <vector>

// doxygen で EX_ENUM_DOXYGEN を定義しておくと enum としてドキュメント化できる
#ifndef EX_ENUM_DOXYGEN

/**
	@brief	拡張 enum 型を定義します。
	@see	Doc_EnumExtension_1
*/
#define EX_ENUM(enumName) \
	class enumName : public EnumExtension::Enum \
	{ \
	public: \
		enum _##enumName

/**
	@brief	拡張 enum 型を定義します。
	@see	Doc_EnumExtension_1
*/
#define EX_ENUM_DECLARE(enumName) \
	private: \
		int	m_value; \
	public: \
		typedef _##enumName enum_type; \
		enumName() : m_value(0) {} \
		enumName(enum_type v) : m_value(v) {} \
		inline operator int() const { return m_value; } \
		inline bool operator==(enumName right) const { return m_value == right.m_value; } \
		inline bool operator==(enum_type right) const { return m_value == right; } \
		inline bool operator!=(enumName right) const { return !operator==(right); } \
		inline bool operator!=(enum_type right) const { return !operator==(right); } \
		friend inline bool operator==(enumName::enum_type left, enumName right) throw(); \
		friend inline bool operator!=(enumName::enum_type left, enumName right) throw(); \
	}; \
	inline bool operator==(enumName::enum_type left, enumName right) throw() { return left == right.m_value; } \
	inline bool operator!=(enumName::enum_type left, enumName right) throw() { return left != right.m_value; }

/**
	@brief	拡張 enum 型の型情報にアクセスする機能を定義します。
	@see	Doc_EnumExtension_1
*/
#define EX_ENUM_REFLECTION(enumName, ...) \
private: \
	struct LocalEnumParser : public EnumParser <_##enumName> { LocalEnumParser() { _##enumName values[] = { __VA_ARGS__ };  Init(values,  ( sizeof(values) / sizeof( values[0] ) ), _T(#__VA_ARGS__)); } }; \
	static LocalEnumParser& GetEnumParser() { static LocalEnumParser parser; return parser; } \
public: \
	EnumExtension::String ToString() const { return GetEnumParser().ToString(m_value); } \
	static enumName Parse(const EnumExtension::Char* str) { return GetEnumParser().Parse(str); }; \
	static bool TryParse(const EnumExtension::Char* str, enumName* outValue) { return GetEnumParser().TryParse(str, (outValue) ? &outValue->m_value : NULL); }

/**
	@brief	ビットフィールドとしてフラグの組み合わせを表す 拡張 enum 型を定義します。
	@see	Doc_EnumExtension_1
*/
#define EX_ENUM_FLAGS(enumName) \
	class enumName : public EnumExtension::Enum \
	{ \
	public: \
		enum _##enumName

/**
	@brief	ビットフィールドとしてフラグの組み合わせを表す 拡張 enum 型を定義します。
	@see	Doc_EnumExtension_1
*/
#define EX_ENUM_FLAGS_DECLARE(enumName) \
	private: \
		int	m_value; \
	public: \
		typedef _##enumName enum_type; \
		enumName() : m_value(0) {} \
		enumName(enum_type v) : m_value(v) {} \
		inline bool TestFlag(enum_type f) const throw() { return (m_value & f) == f && (f != 0 || m_value == f); } \
		inline operator int() const { return m_value; } \
		inline bool operator==(enumName right) const { return m_value == right.m_value; } \
		inline bool operator==(_##enumName right) const { return m_value == right; } \
		inline bool operator!=(enumName right) const { return !operator==(right); } \
		inline bool operator!=(_##enumName right) const { return !operator==(right); } \
		inline enumName& operator &= (const enumName& v) { m_value &= v.m_value; return *this; } \
		inline enumName& operator &= (enum_type v) { m_value &= v; return *this; } \
		inline enumName& operator |= (const enumName& v) { m_value |= v.m_value; return *this; } \
		inline enumName& operator |= (enum_type v) { m_value |= v; return *this; } \
		inline enumName operator~() const throw() { enumName value; value.m_value = ~m_value; return value; } \
		friend inline bool operator==(enumName::enum_type left, enumName right) throw(); \
		friend inline bool operator!=(enumName::enum_type left, enumName right) throw(); \
		friend inline enumName operator&(const enumName& left, const enumName& right) throw(); \
		friend inline enumName operator&(const enumName& left, enum_type right) throw(); \
		friend inline enumName operator&(enum_type left, const enumName& right) throw(); \
		friend inline enumName operator&(enum_type left, enum_type right) throw(); \
		friend inline enumName operator|(const enumName& left, const enumName& right) throw(); \
		friend inline enumName operator|(const enumName& left, enum_type right) throw(); \
		friend inline enumName operator|(enum_type left, const enumName& right) throw(); \
		friend inline enumName operator|(enum_type left, enum_type right) throw(); \
		friend inline enumName operator~(enumName::enum_type v) throw(); \
	}; \
	inline bool operator==(enumName::enum_type left, enumName right) throw() { return left == right.m_value; } \
	inline bool operator!=(enumName::enum_type left, enumName right) throw() { return left != right.m_value; } \
	inline enumName operator&(const enumName& left, const enumName& right) throw()		{ enumName value; value.m_value = left.m_value & right.m_value; return value; } \
	inline enumName operator&(const enumName& left, enumName::enum_type right) throw()	{ enumName value; value.m_value = left.m_value & right; return value; } \
	inline enumName operator&(enumName::enum_type left, const enumName& right) throw()	{ enumName value; value.m_value = right.m_value & left;  return value; } \
	inline enumName operator&(enumName::enum_type left, enumName::enum_type right) throw() { return enumName(left) & right; } \
	inline enumName operator|(const enumName& left, const enumName& right) throw()		{ enumName value; value.m_value = left.m_value | right.m_value; return value; } \
	inline enumName operator|(const enumName& left, enumName::enum_type right) throw()	{ enumName value; value.m_value = left.m_value | right; return value; } \
	inline enumName operator|(enumName::enum_type left, const enumName& right) throw()	{ enumName value; value.m_value = right.m_value | left;  return value; } \
	inline enumName operator|(enumName::enum_type left, enumName::enum_type right) throw() { return enumName(left) | right; } \
	inline enumName operator~(enumName::enum_type v) throw() { enumName value; value.m_value = (~((int)v)); return value; }

/**
	@brief	拡張 enum 型の型情報にアクセスする機能を定義します。EX_ENUM_FLAGS で定義した enum に対して使用します。
	@see	Doc_EnumExtension_1
*/
#define EX_ENUM_FLAGS_REFLECTION(enumName, ...) \
private: \
	struct LocalEnumParser : public EnumFlagsParser <_##enumName> { LocalEnumParser() { _##enumName values[] = { __VA_ARGS__ };  Init(values, ( sizeof(values) / sizeof( values[0] ) ), _T(#__VA_ARGS__)); } }; \
	static LocalEnumParser& GetEnumParser() { static LocalEnumParser parser; return parser; } \
public: \
	EnumExtension::String ToString(const EnumExtension::Char* separator = _T("|")) const { return GetEnumParser().ToString(m_value, separator); } \
	static enumName Parse(const EnumExtension::Char* str, EnumExtension::Char separator = '|') { return GetEnumParser().Parse(str, separator); }; \
	static bool TryParse(const EnumExtension::Char* str, enumName* outValue, EnumExtension::Char separator = '|') { return GetEnumParser().TryParse(str, (outValue) ? &outValue->m_value : NULL, separator); }

#else

#define EX_ENUM(enumName)	enum enumName
#define EX_ENUM_DECLARE(enumName)
#define EX_ENUM_REFLECTION(enumName, ...)
#define EX_ENUM_FLAGS(enumName)
#define EX_ENUM_FLAGS_DECLARE(enumName)
#define EX_ENUM_FLAGS_REFLECTION(enumName, ...)

#endif

namespace EnumExtension
{

#ifndef _T

#ifdef _UNICODE
	#define __T(x)      L ## x
	#define _T(x)		__T(x)
	typedef wchar_t Char;
	typedef std::wstring String;
	typedef std::wstringstream StringStream;
#else
	#define _T(x)	x
	typedef char Char;
	typedef std::string String;
	typedef std::stringstream StringStream;
#endif

#endif

	
/**
	@brief	拡張 enum 型のベースクラスです。
*/
class Enum
{
protected:

	template<typename TEnum>
	struct EnumParser
	{
		// EX_ENUM_DECLARE マクロが非常に長くなるのを避けるため、部分的にクラス化した
	public:
		struct Pair { String Name; TEnum Value; };
		typedef typename std::vector<Pair> PairList;

		static PairList& GetMemberList()
		{
			static PairList members; return members;	// ヘッダ include だけで済ますため、static 変数は関数内に閉じ込めておく
		}
		void Init(const TEnum* values, int valuesCount, const Char* argNames)
		{
			PairList& members = GetMemberList();
			String names = argNames;
			std::vector<String> tokens = Split(names, _T(','));
			for (int i = 0; i < valuesCount; ++i)
			{
				Pair p;
				p.Name = Trim(tokens[i]);
				p.Value = values[i];
				members.push_back(p);
			}
		}
		static String ToString(int value)
		{
			PairList& members = GetMemberList();
			for (size_t i = 0; i < members.size(); ++i)
			{
				if (members[i].Value == value) {
					return members[i].Name;
				}
			}
			assert(0);
			return String();
		}
		static TEnum Parse(const Char* str)
		{
			int value;
			if (TryParse(str, &value)) {
				return (TEnum)value;
			}
			assert(0);
			return TEnum();
		}
		static bool TryParse(const Char* str, int* outValue)
		{
			PairList& members = GetMemberList();
			for (size_t i = 0; i < members.size(); ++i)
			{
				if (members[i].Name == str)
				{
					*outValue = members[i].Value;
					return true;
				}
			}
			return false;
		}

		std::vector<String> Split(const String &str, Char sep)
		{
			std::vector<String> v;
			StringStream ss(str);
			String buffer;
			while (std::getline(ss, buffer, sep)) {
				v.push_back(buffer);
			}
			return v;
		}
		String Trim(const String& string, const Char* trimCharacterList = _T(" \t\v\r\n"))
		{
			String result;
			String::size_type left = string.find_first_not_of(trimCharacterList);

			if (left !=  String::npos)
			{
				String::size_type right = string.find_last_not_of(trimCharacterList);
				result = string.substr(left, right - left + 1);
			}

			return result;
		}
	};

	template<typename TEnum>
	struct EnumFlagsParser : public EnumParser<TEnum>
	{
	public:
		typedef typename EnumParser<TEnum>::Pair Pair;

		static String ToString(int value, const Char* separator)
		{
			PairList& members = EnumParser<TEnum>::GetMemberList();
			// 先に完全一致を探す (White=Red|Green|Blue のようなパターン用)
			for (size_t i = 0; i < members.size(); ++i)
			{
				if (members[i].Value == value) {
					return members[i].Name;
				}
			}
			// 完全一致が無ければ複数のフラグを結合する
			String out;
			for (size_t i = 0; i < members.size(); ++i)
			{
				TEnum f = members[i].Value;
				if ((value & f) == f && (f != 0 || value == f))
				{
					if (!out.empty()) { out += separator; }
					out += members[i].Name;
				}
			}
			assert(!out.empty());
			return out;
		}
		static TEnum Parse(const Char* str, Char separator)
		{
			int value;
			if (TryParse(str, &value, separator)) {
				return (TEnum)value;
			}
			assert(0);
			return TEnum();
		}
		static bool TryParse(const Char* str, int* outValue, Char separator)
		{
			int state = 0;
			const Char* pos = str;
			const Char* tokenBegin = NULL;
			*outValue = 0;

			while (*pos)
			{
				switch (state)
				{
				case 0:	// 識別子を検索中
					if (isalnum(*pos)) {
						tokenBegin = pos;
						state = 1;		// 識別子の解析へ
					}
					else if (isspace(*pos)) {
						// 継続する
					}
					else {
						return false;	// 不正な文字が見つかった
					}
					break;

				case 1:	// 識別子を解析中
					if (isalnum(*pos)) {
						// 継続する
					}
					else {
						// 識別子確定
						int value;
						if (TryParseInternal(tokenBegin, (pos - tokenBegin), &value)) {
							(*outValue) |= value;
						}
						else {
							// メンバではない識別値が見つかった。とりあえず無視。
						}
						tokenBegin = NULL;
						state = 2;		// セパレータの検索へ
						--pos;			// この時点で | を指しているかもしれないので戻しておく
					}
					break;

				case 2:	// セパレータを検索中
					if (*pos == separator) {
						state = 0;		// 識別子の検索へ
					}
					else if (isspace(*pos)) {
						// 継続する
					}
					else {
						return false;	// 不正な文字が見つかった
					}
					break;
				}

				++pos;
			}

			// 終端が識別子で終わっていた場合はその分を読み取る
			if (tokenBegin != NULL)
			{
				int value;
				if (TryParseInternal(tokenBegin, (pos - tokenBegin), &value)) {
					(*outValue) |= value;
				}
				else {
					// メンバではない識別値が見つかった。とりあえず無視。
				}
			}

			return true;
		}

		static bool TryParseInternal(const Char* str, int len, int* outValue)
		{
			PairList& members = EnumParser<TEnum>::GetMemberList();
			for (size_t i = 0; i < members.size(); ++i)
			{
				//if (_tcsncmp(members[i].Name.GetCStr(), str, len) == 0)
				//if (members[i].Name.Compare(str, len) == 0)
				if (memcmp(members[i].Name.c_str(), str, len * sizeof(Char)) == 0)
				{
					*outValue = members[i].Value;
					return true;
				}
			}
			return false;
		}

	};
};

} // namespace EnumExtension
