//! \addtogroup Core
//! \{
//! \~\file picollection.h
//! \brief
//! \~english Unique classes collection
//! \~russian Коллекция уникальных классов
//! \details
//! \~english Helper module to collect and retrieve classes into groups using macros for automatic registration.
//! \~russian Модуль-помощник для сбора и получения классов в группы с помощью макросов для автоматической регистрации.
//! \~\}
/*
    PIP - Platform Independent Primitives
    Peer - named I/O ethernet node, forming self-organized peering network
    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 PICOLLECTION_H
#define PICOLLECTION_H

#include "piobject.h"

#ifdef DOXYGEN

//! \relatesalso PICollection
//! \~\brief
//! \~english Adds existing object to group "group".
//! \~russian Добавляет существующий объект в группу "group".
//! \~\details
//! \~english
//! If this is no group with name "group" it will be created.
//! Only one element of the class "object" can be in group. If
//! this is already exists nothing be happens. \n "object" should to
//! be pointer to object based on \a PIObject.
//! \~russian
//! Если такой группы нет, она создается. В каждой группе может присутствовать
//! только один элемент класса объекта "object". Если такой элемент уже есть,
//! то ничего не изменится. \n "object" должен быть наследником \a PIObject.
#  define ADD_TO_COLLECTION(group, object)

//! \relatesalso PICollection
//! \~\brief
//! \~english Adds existing object to group "group" and assigns name "name".
//! \~russian Добавляет существующий объект в группу "group" и присваивает ему имя "name".
//! \~\details
//! \~english
//! Similar to \a ADD_TO_COLLECTION(group, object), but also sets object name.
//! \~russian
//! Аналогично \a ADD_TO_COLLECTION(group, object), но дополнительно задает имя объекта.
#  define ADD_TO_COLLECTION_WITH_NAME(group, object, name)

//! \relatesalso PICollection
//! \~\brief
//! \~english Creates and adds new object of class "class" to group "group".
//! \~russian Создает и добавляет новый объект класса "class" в группу "group".
//! \~\details
//! \~english
//! If this is no group with name "group" it will be created.
//! Only one element of the class "class" can be in group. If
//! this is already exists nothing be happens. \n "class" should to
//! be name of the any class based on PIObject.
//! \~russian
//! Если такой группы нет, она создается. В каждой группе может присутствовать
//! только один элемент класса "class". Если такой элемент уже есть,
//! то ничего не изменится. \n "class" должен быть любым классом, наследным от \a PIObject.
#  define ADD_NEW_TO_COLLECTION(group, class)

//! \relatesalso PICollection
//! \~\brief
//! \~english Creates and adds new object of class "class" to group "group"
//! and assigns name "name".
//! \~russian Создает и добавляет новый объект класса "class" в группу "group"
//! и присваивает ему имя "name".
//! \~\details
//! \~english
//! Similar to \a ADD_NEW_TO_COLLECTION(group, class), but also sets object name.
//! \~russian
//! Аналогично \a ADD_NEW_TO_COLLECTION(group, class), но дополнительно задает имя объекта.
#  define ADD_NEW_TO_COLLECTION_WITH_NAME(group, class, name)

#else

#  define ADD_TO_COLLECTION(group, object) \
	  static PICollection::CollectionAdder _PIP_ADD_COUNTER(_collection_adder_)(#group, object, "", false);

#  define ADD_TO_COLLECTION_WITH_NAME(group, object, name) \
	  static PICollection::CollectionAdder _PIP_ADD_COUNTER(_collection_adder_)(#group, object, #name, false);

#  define ADD_NEW_TO_COLLECTION(group, class) \
	  static PICollection::CollectionAdder _PIP_ADD_COUNTER(_collection_adder_)(#group, new class(), "", true);

#  define ADD_NEW_TO_COLLECTION_WITH_NAME(group, class, name) \
	  static PICollection::CollectionAdder _PIP_ADD_COUNTER(_collection_adder_)(#group, new class(), #name, true);

#endif

//! \~\ingroup Core
//! \~\brief
//! \~english Global collection of %PIObject-based instances grouped by name.
//! \~russian Глобальная коллекция экземпляров на базе %PIObject, сгруппированных по имени.
class PIP_EXPORT PICollection {
	friend class __PICollectionInitializer;

public:
	//! \~english Constructs collection helper.
	//! \~russian Создает вспомогательный объект коллекции.
	PICollection() { ; }

	//! \~english Returns names of all existing groups.
	//! \~russian Возвращает имена всех существующих групп.
	static PIStringList groups();

	//! \~english Returns all elements stored in group "group".
	//! \~russian Возвращает все элементы, хранящиеся в группе "group".
	static PIVector<const PIObject *> groupElements(const PIString & group);

	//! \~english Adds object to group "group" if that group has no object of the
	//! same runtime class.
	//! \~russian Добавляет объект в группу "group", если в группе еще нет объекта
	//! того же класса времени выполнения.
	static bool addToGroup(const PIString & group, const PIObject * element);

	//! \~\ingroup Core
	//! \~\brief
	//! \~english Helper that registers object in collection during static initialization.
	//! \~russian Вспомогательный класс, регистрирующий объект в коллекции при статической инициализации.
	class PIP_EXPORT CollectionAdder {
	public:
		//! \~english Registers object in group and optionally assigns object name.
		//! \~russian Регистрирует объект в группе и при необходимости задает имя объекта.
		CollectionAdder(const PIString & group, const PIObject * element, const PIString & name = PIString(), bool own = false);
	};

protected:
	struct PIP_EXPORT Group {
		Group(const PIString & name_ = PIString()) { name = name_; }
		PIString name;
		PIVector<const PIObject *> elements;
	};

	static PIVector<Group> & _groups();
};

#endif // PICOLLECTION_H
