//! \~\file piintrospection_containers.h
//! \~\ingroup Introspection
//! \~\brief
//! \~english Container introspection helpers
//! \~russian Вспомогательные средства интроспекции контейнеров
/*
    PIP - Platform Independent Primitives
    Introspection module - interface for containers
    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 PIINTROSPECTION_CONTAINERS_H
#define PIINTROSPECTION_CONTAINERS_H

#include "pibase.h"

//! \~\ingroup Introspection
//! \~\brief
//! \~english Metadata describing one tracked container element type.
//! \~russian Метаданные, описывающие один отслеживаемый тип элементов контейнера.
struct PIP_EXPORT PIIntrospectionContainersType {
	//! \~english Destroys the type descriptor.
	//! \~russian Уничтожает дескриптор типа.
	~PIIntrospectionContainersType();

	//! \~english Finalizes type information, including generated identifiers and names.
	//! \~russian Завершает подготовку информации о типе, включая сгенерированные идентификаторы и имена.
	void finish();

	//! \~english Stable identifier of the tracked type.
	//! \~russian Стабильный идентификатор отслеживаемого типа.
	uint id                = 0;

	//! \~english Compiler-provided type name.
	//! \~russian Имя типа, предоставленное компилятором.
	const char * name      = nullptr;

	//! \~english Demangled type name when available.
	//! \~russian Деманглированное имя типа, если доступно.
	const char * demangled = "?";

	//! \~english True after \a finish() prepares the descriptor.
	//! \~russian Истина после того, как \a finish() подготовит дескриптор.
	bool inited            = false;

	//! \~english True when demangled name was resolved successfully.
	//! \~russian Истина, если деманглированное имя успешно получено.
	bool has_demangled     = false;
};

#if defined(PIP_INTROSPECTION) && !defined(PIP_FORCE_NO_PIINTROSPECTION)

#  include "piintrospection_base.h"

class PIIntrospectionContainers;

//! \~\ingroup Introspection
//! \~\brief
//! \~english Lazily builds and caches type metadata for container introspection macros.
//! \~russian Лениво создает и кеширует метаданные типа для макросов интроспекции контейнеров.
template<typename T>
class PIIntrospectionContainersTypeInfo {
public:
	//! \~english Returns cached metadata for type `T`.
	//! \~russian Возвращает кешированные метаданные для типа `T`.
	static const PIIntrospectionContainersType & get() {
		static PIIntrospectionContainersType ret = create();
		return ret;
	}

private:
	static PIIntrospectionContainersType create() {
		PIIntrospectionContainersType ret;
		ret.name = __PIP_TYPENAME__(T);
		ret.finish();
		return ret;
	}
};

#  define PIINTROSPECTION_CONTAINERS (PIIntrospectionContainersInterface::instance())

#  ifdef DOXYGEN

//! \~\ingroup Introspection
//! \relatesalso PIIntrospectionContainersInterface
//! \~\brief
//! \~english Registers construction of a container storing elements of type `t`.
//! \~russian Регистрирует создание контейнера, хранящего элементы типа `t`.
#	define PIINTROSPECTION_CONTAINER_NEW(t, isz)

//! \~\ingroup Introspection
//! \relatesalso PIIntrospectionContainersInterface
//! \~\brief
//! \~english Registers destruction of a container storing elements of type `t`.
//! \~russian Регистрирует уничтожение контейнера, хранящего элементы типа `t`.
#	define PIINTROSPECTION_CONTAINER_DELETE(t)

//! \~\ingroup Introspection
//! \relatesalso PIIntrospectionContainersInterface
//! \~\brief
//! \~english Adds `cnt` allocated element slots for containers of type `t`.
//! \~russian Добавляет `cnt` выделенных слотов элементов для контейнеров типа `t`.
#	define PIINTROSPECTION_CONTAINER_ALLOC(t, cnt)

//! \~\ingroup Introspection
//! \relatesalso PIIntrospectionContainersInterface
//! \~\brief
//! \~english Removes `cnt` allocated element slots for containers of type `t`.
//! \~russian Убирает `cnt` выделенных слотов элементов для контейнеров типа `t`.
#	define PIINTROSPECTION_CONTAINER_FREE(t, cnt)

//! \~\ingroup Introspection
//! \relatesalso PIIntrospectionContainersInterface
//! \~\brief
//! \~english Adds `cnt` used element slots for containers of type `t`.
//! \~russian Добавляет `cnt` занятых слотов элементов для контейнеров типа `t`.
#	define PIINTROSPECTION_CONTAINER_USED(t, cnt)

//! \~\ingroup Introspection
//! \relatesalso PIIntrospectionContainersInterface
//! \~\brief
//! \~english Removes `cnt` used element slots for containers of type `t`.
//! \~russian Убирает `cnt` занятых слотов элементов для контейнеров типа `t`.
#	define PIINTROSPECTION_CONTAINER_UNUSED(t, cnt)

#  else

// clang-format off
#    define PIINTROSPECTION_CONTAINER_NEW(t, isz)    PIINTROSPECTION_CONTAINERS->containerNew   (PIIntrospectionContainersTypeInfo<t>::get(), isz);
#    define PIINTROSPECTION_CONTAINER_DELETE(t)      PIINTROSPECTION_CONTAINERS->containerDelete(PIIntrospectionContainersTypeInfo<t>::get()     );
#    define PIINTROSPECTION_CONTAINER_ALLOC(t, cnt)  PIINTROSPECTION_CONTAINERS->containerAlloc (PIIntrospectionContainersTypeInfo<t>::get(), cnt);
#    define PIINTROSPECTION_CONTAINER_FREE(t, cnt)   PIINTROSPECTION_CONTAINERS->containerFree  (PIIntrospectionContainersTypeInfo<t>::get(), cnt);
#    define PIINTROSPECTION_CONTAINER_USED(t, cnt)   PIINTROSPECTION_CONTAINERS->containerUsed  (PIIntrospectionContainersTypeInfo<t>::get(), cnt);
#    define PIINTROSPECTION_CONTAINER_UNUSED(t, cnt) PIINTROSPECTION_CONTAINERS->containerUnused(PIIntrospectionContainersTypeInfo<t>::get(), cnt);
// clang-format on

#  endif


//! \~\ingroup Introspection
//! \~\brief
//! \~english Entry point for collecting container allocation and usage statistics.
//! \~russian Точка входа для сбора статистики выделения и использования контейнеров.
class PIP_EXPORT PIIntrospectionContainersInterface {
	friend class PIIntrospection;
	friend class PIIntrospectionServer;

public:
	__PIINTROSPECTION_SINGLETON_H__(Containers)

	//! \~english Registers construction of a container instance with element size `isz`.
	//! \~russian Регистрирует создание экземпляра контейнера с размером элемента `isz`.
	void containerNew(const PIIntrospectionContainersType & ti, uint isz);

	//! \~english Registers destruction of a container instance.
	//! \~russian Регистрирует уничтожение экземпляра контейнера.
	void containerDelete(const PIIntrospectionContainersType & ti);

	//! \~english Adds `cnt` allocated element slots for tracked type `ti`.
	//! \~russian Добавляет `cnt` выделенных слотов элементов для отслеживаемого типа `ti`.
	void containerAlloc(const PIIntrospectionContainersType & ti, ullong cnt);

	//! \~english Removes `cnt` allocated element slots for tracked type `ti`.
	//! \~russian Убирает `cnt` выделенных слотов элементов для отслеживаемого типа `ti`.
	void containerFree(const PIIntrospectionContainersType & ti, ullong cnt);

	//! \~english Adds `cnt` used element slots for tracked type `ti`.
	//! \~russian Добавляет `cnt` занятых слотов элементов для отслеживаемого типа `ti`.
	void containerUsed(const PIIntrospectionContainersType & ti, ullong cnt);

	//! \~english Removes `cnt` used element slots for tracked type `ti`.
	//! \~russian Убирает `cnt` занятых слотов элементов для отслеживаемого типа `ti`.
	void containerUnused(const PIIntrospectionContainersType & ti, ullong cnt);

	//! \~english Private implementation pointer with collected statistics.
	//! \~russian Указатель на приватную реализацию с накопленной статистикой.
	PIIntrospectionContainers * p;

private:
	PIIntrospectionContainersInterface();
	~PIIntrospectionContainersInterface();
};


#else
#  define PIINTROSPECTION_CONTAINER_NEW(t, isz)
#  define PIINTROSPECTION_CONTAINER_DELETE(t)
#  define PIINTROSPECTION_CONTAINER_ALLOC(t, cnt)
#  define PIINTROSPECTION_CONTAINER_FREE(t, cnt)
#  define PIINTROSPECTION_CONTAINER_USED(t, cnt)
#  define PIINTROSPECTION_CONTAINER_UNUSED(t, cnt)
#endif


#endif // PIINTROSPECTION_CONTAINERS_H
