//! \~\file picodeinfo.h
//! \~\ingroup Code
//! \~\brief
//! \~english C++ code info structs. See \ref code_model.
//! \~russian Структуры для C++ кода. Подробнее \ref code_model.
//! \~\details
//! \~english Contains structures for code generation and reflection: TypeInfo, FunctionInfo, ClassInfo, EnumInfo, EnumeratorInfo.
//! \~russian Содержит структуры для кодогенерации и рефлексии: TypeInfo, FunctionInfo, ClassInfo, EnumInfo, EnumeratorInfo.
/*
    PIP - Platform Independent Primitives
    C++ code info structs
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#ifndef PICODEINFO_H
#define PICODEINFO_H

#include "piconstchars.h"
#include "pistringlist.h"
#include "pivarianttypes.h"


class PIVariant;

//! \~english Namespace contains structures for code generation. See \ref code_model.
//! \~russian Пространство имен содержит структуры для кодогенерации. Подробнее \ref code_model.
//! \~\details
//! \~english Provides classes and structures for code introspection, including type information, function details, class metadata, and enum
//! information.
//! \~russian Предоставляет классы и структуры для интроспекции кода, включая информацию о типах, детали функций, метаданные классов и
//! информацию о перечислениях.
namespace PICodeInfo {


//! \~english Type modifiers.
//! \~russian Модификаторы типа.
enum TypeFlag {
	NoFlag /** \~english No modifiers. \~russian Модификаторы отсутствуют. */,
	Const    = 0x01 /** \~english \c const modifier. \~russian Модификатор \c const. */,
	Static   = 0x02 /** \~english \c static modifier. \~russian Модификатор \c static. */,
	Mutable  = 0x04 /** \~english \c mutable modifier. \~russian Модификатор \c mutable. */,
	Volatile = 0x08 /** \~english \c volatile modifier. \~russian Модификатор \c volatile. */,
	Inline   = 0x10 /** \~english \c inline modifier. \~russian Модификатор \c inline. */,
	Virtual  = 0x20 /** \~english \c virtual modifier. \~russian Модификатор \c virtual. */,
	Extern   = 0x40 /** \~english \c extern modifier. \~russian Модификатор \c extern. */
};

//! \~english Bitmask of type modifiers.
//! \~russian Битовая маска модификаторов типа.
typedef PIFlags<PICodeInfo::TypeFlag> TypeFlags;
//! \~english Custom metadata map produced by \c PIMETA.
//! \~russian Карта пользовательских метаданных, создаваемых \c PIMETA.
typedef PIMap<PIString, PIString> MetaMap;
//! \~english Callback returning serialized member data by member name.
//! \~russian Обратный вызов, возвращающий сериализованные данные члена по имени.
typedef PIByteArray (*AccessValueFunction)(const void *, const char *);
//! \~english Callback returning a member type name by member name.
//! \~russian Обратный вызов, возвращающий имя типа члена по его имени.
typedef const char * (*AccessTypeFunction)(const char *);
//! \~english Callback returning a member offset by member name.
//! \~russian Обратный вызов, возвращающий смещение члена по его имени.
typedef int (*AccessOffsetFunction)(const char *);


//! \~english Type information
//! \~russian Информация о типе
struct PIP_EXPORT TypeInfo {
	//! \~english Constructs type information for one variable or argument.
	//! \~russian Создает описание типа для одной переменной или аргумента.
	TypeInfo(const PIConstChars & n = PIConstChars(), const PIConstChars & t = PIConstChars(), PICodeInfo::TypeFlags f = 0, int b = -1) {
		name  = n;
		type  = t;
		flags = f;
		bits  = b;
	}

	//! \~english Returns whether the described variable is a bitfield.
	//! \~russian Возвращает, является ли описываемая переменная битовым полем.
	bool isBitfield() const { return bits > 0; }

	//! \~english Custom PIMETA content
	//! \~russian Произвольное содержимое PIMETA
	MetaMap meta;

	//! \~english Name
	//! \~russian Имя
	PIConstChars name;

	//! \~english Type
	//! \~russian Тип
	PIConstChars type;

	//! \~english Modifiers
	//! \~russian Модификаторы
	PICodeInfo::TypeFlags flags;

	//! \~english Bitfield variable bit count
	//! \~russian Количество бит битового поля
	int bits;
};


//! \~english Method information
//! \~russian Информация о методе
struct PIP_EXPORT FunctionInfo {
	//! \~english Custom PIMETA content
	//! \~russian Произвольное содержимое PIMETA
	MetaMap meta;

	//! \~english Name
	//! \~russian Имя
	PIConstChars name;

	//! \~english Return type
	//! \~russian Возвращаемый тип
	TypeInfo return_type;

	//! \~english Arguments types
	//! \~russian Типы аргументов
	PIVector<PICodeInfo::TypeInfo> arguments;
};


//! \~english Class or struct information
//! \~russian Информация о классе или структуре
struct PIP_EXPORT ClassInfo {
	//! \~english Constructs an empty class description.
	//! \~russian Создает пустое описание класса.
	ClassInfo() { is_anonymous = false; }

	//! \~english Custom PIMETA content
	//! \~russian Произвольное содержимое PIMETA
	MetaMap meta;

	//! \~english Indicates that the type was declared without a name
	//! \~russian Показывает, что тип был объявлен без имени
	bool is_anonymous;

	//! \~english Declaration kind, for example \c class or \c struct
	//! \~russian Вид объявления, например \c class или \c struct
	PIConstChars type;

	//! \~english Name
	//! \~russian Имя
	PIConstChars name;

	//! \~english Base class names
	//! \~russian Имена базовых классов
	PIVector<PIConstChars> parents;

	//! \~english Variables
	//! \~russian Переменные
	PIVector<PICodeInfo::TypeInfo> variables;

	//! \~english Methods
	//! \~russian Методы
	PIVector<PICodeInfo::FunctionInfo> functions;

	//! \~english Registered derived class descriptions
	//! \~russian Зарегистрированные описания производных классов
	PIVector<PICodeInfo::ClassInfo *> children_info;
};


//! \~english Enumerator information
//! \~russian Информация об элементе перечисления
struct PIP_EXPORT EnumeratorInfo {
	//! \~english Constructs one enum member description.
	//! \~russian Создает описание одного элемента перечисления.
	EnumeratorInfo(const PIConstChars & n = PIConstChars(), int v = 0) {
		name  = n;
		value = v;
	}
	//! \~english Converts the enumerator to the %PIVariantTypes representation.
	//! \~russian Преобразует элемент перечисления в представление %PIVariantTypes.
	PIVariantTypes::Enumerator toPIVariantEnumerator() { return PIVariantTypes::Enumerator(value, name.toString()); }

	//! \~english Custom PIMETA content
	//! \~russian Произвольное содержимое PIMETA
	MetaMap meta;

	//! \~english Name
	//! \~russian Имя
	PIConstChars name;

	//! \~english Value
	//! \~russian Значение
	int value;
};


//! \~english Enum information
//! \~russian Информация о перечислении
struct PIP_EXPORT EnumInfo {
	//! \~english Returns the member name for the value \a value.
	//! \~russian Возвращает имя элемента для значения \a value.
	PIString memberName(int value) const;

	//! \~english Returns the member value for the name \a name.
	//! \~russian Возвращает значение элемента для имени \a name.
	int memberValue(const PIString & name) const;

	//! \~english Converts the enum description to %PIVariantTypes::Enum.
	//! \~russian Преобразует описание перечисления в %PIVariantTypes::Enum.
	PIVariantTypes::Enum toPIVariantEnum();

	//! \~english Custom PIMETA content
	//! \~russian Произвольное содержимое PIMETA
	MetaMap meta;

	//! \~english Name
	//! \~russian Имя
	PIConstChars name;

	//! \~english Members
	//! \~russian Элементы
	PIVector<PICodeInfo::EnumeratorInfo> members;
};


//! \~english Writes a declaration-like view of \a v to \a s.
//! \~russian Записывает в \a s представление \a v в стиле объявления.
inline PICout operator<<(PICout s, const PICodeInfo::TypeInfo & v) {
	if (v.flags[Inline]) s << "inline ";
	if (v.flags[Virtual]) s << "virtual ";
	if (v.flags[Mutable]) s << "mutable ";
	if (v.flags[Volatile]) s << "volatile ";
	if (v.flags[Static]) s << "static ";
	if (v.flags[Const]) s << "const ";
	s << v.type;
	if (!v.name.isEmpty()) s << " " << v.name;
	return s;
}

//! \~english Writes an enum member description to \a s.
//! \~russian Записывает описание элемента перечисления в \a s.
inline PICout operator<<(PICout s, const PICodeInfo::EnumeratorInfo & v) {
	s << v.name << " = " << v.value << " Meta" << v.meta;
	return s;
}

//! \~english Writes a human-readable class description to \a s.
//! \~russian Записывает в \a s человекочитаемое описание класса.
inline PICout operator<<(PICout s, const PICodeInfo::ClassInfo & v) {
	s.saveAndSetControls(0);
	s << "class " << v.name;
	if (!v.parents.isEmpty()) {
		s << ": ";
		bool first = true;
		for (const auto & i: v.parents) {
			if (first)
				first = false;
			else
				s << ", ";
			s << i;
		}
	}
	s << " Meta" << v.meta << " {\n";
	for (const auto & i: v.functions) {
		s << PICoutManipulators::Tab << i.return_type << " " << i.name << "(";
		bool fa = true;
		for (const auto & a: i.arguments) {
			if (fa)
				fa = false;
			else
				s << ", ";
			s << a;
		}
		s << ") Meta" << i.meta << ";\n";
	}
	if (!v.functions.isEmpty() && !v.variables.isEmpty()) s << "\n";
	for (const auto & i: v.variables) {
		s << PICoutManipulators::Tab << i << " Meta" << i.meta << ";\n";
	}
	s << "}\n";
	s.restoreControls();
	return s;
}

//! \~english Writes a human-readable enum description to \a s.
//! \~russian Записывает в \a s человекочитаемое описание перечисления.
inline PICout operator<<(PICout s, const PICodeInfo::EnumInfo & v) {
	s.saveAndSetControls(0);
	s << "enum " << v.name << " Meta" << v.meta << " {\n";
	for (const auto & i: v.members) {
		bool f = true;
		if (f)
			f = false;
		else
			s << ", ";
		s << PICoutManipulators::Tab << i << "\n";
	}
	s << "}\n";
	s.restoreControls();
	return s;
}


class PIP_EXPORT __Storage__ {
	__Storage__();
	~__Storage__();

public:
	static __Storage__ * instance();

	PIMap<PIConstChars, PICodeInfo::ClassInfo *> * classesInfo;
	PIMap<PIConstChars, PICodeInfo::EnumInfo *> * enumsInfo;
	PIMap<PIConstChars, PICodeInfo::AccessValueFunction> * accessValueFunctions;
	PIMap<PIConstChars, PICodeInfo::AccessTypeFunction> * accessTypeFunctions;
	PIMap<PIConstChars, PICodeInfo::AccessOffsetFunction> * accessOffsetFunctions;

private:
	NO_COPY_CLASS(__Storage__)
};

class PIP_EXPORT __StorageAccess__ {
public:
	static const PIMap<PIConstChars, PICodeInfo::ClassInfo *> & classes() {
		return *(__Storage__::instance()->classesInfo);
	} // namespace PICodeInfo

	static const PIMap<PIConstChars, PICodeInfo::EnumInfo *> & enums() { return *(__Storage__::instance()->enumsInfo); }

	static const PIMap<PIConstChars, PICodeInfo::AccessValueFunction> & accessValueFunctions() {
		return *(__Storage__::instance()->accessValueFunctions);
	}

	static const PIMap<PIConstChars, PICodeInfo::AccessTypeFunction> & accessTypeFunctions() {
		return *(__Storage__::instance()->accessTypeFunctions);
	}

	static const PIMap<PIConstChars, PICodeInfo::AccessOffsetFunction> & accessOffsetFunctions() {
		return *(__Storage__::instance()->accessOffsetFunctions);
	}
};

//! \relatesalso PICodeInfo
//! \~english Macro for the global code model registries.
//! \~russian Макрос для доступа к глобальным реестрам модели кода.
#define PICODEINFO PICodeInfo::__StorageAccess__


class PIP_EXPORT ClassInfoInterface {
public:
	const PIMap<PIConstChars, PICodeInfo::ClassInfo *> * operator->() const DEPRECATEDM("use PICODEINFO::classes()") {
		return __Storage__::instance()->classesInfo;
	}
};

static ClassInfoInterface classesInfo;


class PIP_EXPORT EnumsInfoInterface {
public:
	const PIMap<PIConstChars, PICodeInfo::EnumInfo *> * operator->() const DEPRECATEDM("use PICODEINFO::enums()") {
		return __Storage__::instance()->enumsInfo;
	}
};

static EnumsInfoInterface enumsInfo;


class PIP_EXPORT AccessValueFunctionInterface {
public:
	const PIMap<PIConstChars, PICodeInfo::AccessValueFunction> * operator->() const DEPRECATEDM("use PICODEINFO::accessValueFunctions()") {
		return __Storage__::instance()->accessValueFunctions;
	}
};

static AccessValueFunctionInterface accessValueFunctions;


class PIP_EXPORT AccessTypeFunctionInterface {
public:
	const PIMap<PIConstChars, PICodeInfo::AccessTypeFunction> * operator->() const DEPRECATEDM("use PICODEINFO::accessTypeFunctions()") {
		return __Storage__::instance()->accessTypeFunctions;
	}
};

static AccessTypeFunctionInterface accessTypeFunctions;


STATIC_INITIALIZER_BEGIN
	NO_UNUSED(classesInfo);
	NO_UNUSED(enumsInfo);
	NO_UNUSED(accessValueFunctions);
	NO_UNUSED(accessTypeFunctions);
STATIC_INITIALIZER_END


//! \~english Returns a serialized value of \a member_name from an instance of \a class_name.
//! \~russian Возвращает сериализованное значение \a member_name из экземпляра \a class_name.
inline PIByteArray getMemberValue(const void * p, const char * class_name, const char * member_name) {
	if (!p || !class_name || !member_name) return PIByteArray();
	AccessValueFunction af = PICODEINFO::accessValueFunctions().value(class_name, (AccessValueFunction)0);
	if (!af) return PIByteArray();
	return af(p, member_name);
}

//! \~english Returns the registered type name of \a member_name in \a class_name.
//! \~russian Возвращает зарегистрированное имя типа \a member_name в \a class_name.
inline const char * getMemberType(const char * class_name, const char * member_name) {
	if (!class_name || !member_name) return "";
	AccessTypeFunction af = PICODEINFO::accessTypeFunctions().value(class_name, (AccessTypeFunction)0);
	if (!af) return "";
	return af(member_name);
}

//! \~english Returns \a member_name from \a class_name as %PIVariant when accessors are registered.
//! \~russian Возвращает \a member_name из \a class_name как %PIVariant, если зарегистрированы функции доступа.
PIP_EXPORT PIVariant getMemberAsVariant(const void * p, const char * class_name, const char * member_name);


//! \~english Serializes assignable values into \a ret through the stream operator.
//! \~russian Сериализует присваиваемые значения в \a ret через оператор потока.
template<typename T, typename std::enable_if<std::is_assignable<T &, const T &>::value, int>::type = 0>
void serialize(PIByteArray & ret, const T & v) {
	ret << v;
}

//! \~english Fallback overload for values that cannot be written to the byte-array stream.
//! \~russian Резервная перегрузка для значений, которые нельзя записать в поток массива байт.
template<typename T, typename std::enable_if<!std::is_assignable<T &, const T &>::value, int>::type = 0>
void serialize(PIByteArray & ret, const T & v) {}

} // namespace PICodeInfo


#endif // PICODEINFO_H
