#pragma once

#include <string>
#include <unordered_map>
#include <optional>

struct LatexMathSymbol
{
	enum class Type
	{
		RAW,
		TAG,
		RELATIONAL,
		RELATIONAL_TAG,
		ENCLOSING,
		ENCLOSING_TAG
	};

	enum class Tag
	{
		NONE,
		FRAC,
		BRA,
		KET
	};

	LatexMathSymbol(Type type = Type::RAW, const std::string& name = {}, const std::string& unicode = {})
		:	mType(type),
			mName(name),
			mUnicode(unicode)
	{

	}

	Type mType{ Type::RAW };
	std::string mName;
	std::string mUnicode;
};

class LatexSymbolLookup
{
public:
	static std::optional<std::string> getLeftSymbolUtf8(LatexMathSymbol::Tag tag);

	static std::optional<std::string> getRightSymbolUtf8(LatexMathSymbol::Tag tag);

	static std::optional<std::string> getSymbolUtf8(const std::string& tag);

	static std::optional<std::wstring> getLeftSymbolUtf16(LatexMathSymbol::Tag tag);

	static std::optional<std::wstring> getRightSymbolUtf16(LatexMathSymbol::Tag tag);

	static std::optional<std::wstring> getSymbolUtf16(const std::string& tag);

	static std::optional<LatexMathSymbol::Tag> getKnownTag(const std::string& tag);

	static std::size_t getNumTagBodies(LatexMathSymbol::Tag tag)
	{
		switch (tag)
		{
		case LatexMathSymbol::Tag::FRAC:
			return 2;
		case LatexMathSymbol::Tag::BRA:
		case LatexMathSymbol::Tag::KET:
			return 1;
		default:
			return 1;
		}
	}

	static LatexMathSymbol::Type getTagType(LatexMathSymbol::Tag tag)
	{
		switch (tag)
		{
		case LatexMathSymbol::Tag::FRAC:
			return LatexMathSymbol::Type::TAG;
		case LatexMathSymbol::Tag::BRA:
		case LatexMathSymbol::Tag::KET:
			return LatexMathSymbol::Type::ENCLOSING_TAG;
		default:
			return LatexMathSymbol::Type::TAG;
		}
	}

private:
	static std::unordered_map<LatexMathSymbol::Tag, std::wstring> mLeftSymbols;
	static std::unordered_map<LatexMathSymbol::Tag, std::wstring> mRightSymbols;

	static std::unordered_map<std::string, std::wstring> mSymbols;

	static std::unordered_map<std::string, LatexMathSymbol::Tag> mTags;
};