//! \~\ingroup Core
//! \~\file piobject.h
//! \~\brief
//! \~english Base object class providing event -> handler mechanism
//! \~russian Базовый класс объектов, обеспечивающий механизм событий -> обработчиков
/*
    PIP - Platform Independent Primitives
    Object, base class of some PIP classes, provide EVENT -> EVENT_HANDLER mechanism
    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 PIOBJECT_H
#define PIOBJECT_H

#include "piinit.h"
#include "pimutex.h"
#include "piobject_macros.h"
#include "piqueue.h"
#include "piset.h"
#include "pivariant.h"
#include "pivariantsimple.h"

//! \~\ingroup Core
//! \~\brief
//! \~english Base class for objects that declare events, event handlers and registered methods.
//! \~russian Базовый класс для объектов, которые объявляют события, обработчики событий и зарегистрированные методы.
//! \~\details
//! \~english
//! PIObject is the base class for all PIP classes that need event-driven communication.
//! It provides signal-slot mechanism, property system, and object lifetime management.
//! %PIObject stores named properties, keeps connection state and exposes a
//! small metaobject table used by \a CONNECTU(), \a execute() and related APIs.
//! Queued delivery runs on the performer object and requires explicit draining
//! through \a callQueuedEvents() or \a maybeCallQueuedEvents().
//! \~russian
//! PIObject является базовым классом для всех классов PIP, которым необходима событийная коммуникация.
//! Он обеспечивает механизм сигналов-слотов, систему свойств и управление жизненным циклом объектов.
//! %PIObject хранит именованные свойства, состояние соединений и небольшую
//! метаобъектную таблицу, которую используют \a CONNECTU(), \a execute() и
//! связанные методы. Отложенная доставка выполняется на объекте-исполнителе и
//! требует явного опустошения очереди через \a callQueuedEvents() или
//! \a maybeCallQueuedEvents().
class PIP_EXPORT PIObject {
#ifndef MICRO_PIP
	friend class PIObjectManager;
	friend PIP_EXPORT void dumpApplication(bool);
	friend class PIIntrospection;
#endif
	typedef PIObject __PIObject__;
	typedef void __Parent__;

public:
	NO_COPY_CLASS(PIObject);

	//! \~english Constructs an object and initializes its \c name property.
	//! \~russian Создает объект и инициализирует его свойство \c name.
	explicit PIObject(const PIString & name = PIString());

	//! \~english Destroys the object, raises \a deleted() and disconnects it from the event graph.
	//! \~russian Уничтожает объект, вызывает \a deleted() и отключает его от событийного графа.
	virtual ~PIObject();

	//! \~\ingroup Core
	//! \~\brief
	//! \~english Handle of one connection between a source object and a destination object or functor.
	//! \~russian Дескриптор одного соединения между объектом-источником и объектом-приемником либо функтором.
	class PIP_EXPORT Connection {
		friend class PIObject;
		Connection(void * sl,
		           void * si,
		           const PIString & e = PIString(),
		           PIObject * s_o     = nullptr,
		           PIObject * d_o     = nullptr,
		           void * d           = nullptr,
		           int ac             = 0,
		           PIObject * p       = nullptr) {
			slot       = sl;
			signal     = si;
			event      = e;
			eventID    = e.hash();
			src_o      = s_o;
			dest_o     = d_o;
			dest       = d;
			args_count = ac;
			performer  = p;
			functor    = 0;
		}
		void destroy();
		void * slot;
		void * signal;
		std::function<void()> * functor;
		PIString event;
		uint eventID;
		PIObject *src_o, *dest_o;
		PIObject * performer;
		void * dest;
		int args_count;

	public:
		//! \~english Constructs an invalid connection handle.
		//! \~russian Создает недействительный дескриптор соединения.
		Connection();

		//! \~english Returns \c true when the connection was created successfully.
		//! \~russian Возвращает \c true, если соединение было успешно создано.
		bool isValid() const { return signal; }

		//! \~english Returns the source object that emits the event.
		//! \~russian Возвращает объект-источник, который испускает событие.
		PIObject * sourceObject() const { return src_o; }

		//! \~english Returns the destination object, or \c nullptr for a lambda connection.
		//! \~russian Возвращает объект-приемник, либо \c nullptr для соединения с лямбда-функцией.
		PIObject * destinationObject() const { return dest_o; }

		//! \~english Returns the performer object, or \c nullptr for direct delivery.
		//! \~russian Возвращает объект-исполнитель, либо \c nullptr для прямой доставки.
		//! \~\details
		//! \~english Queued delivery runs only when the performer drains its queue.
		//! \~russian Отложенная доставка выполняется только когда исполнитель обрабатывает свою очередь.
		PIObject * performerObject() const { return performer; }

		//! \~english Disconnects this single connection.
		//! \~russian Разрывает только это соединение.
		bool disconnect() const;
	};

private:
	uint _signature_;

public:
	//! \~english Returns the \c name property of this object.
	//! \~russian Возвращает свойство \c name этого объекта.
	PIString name() const { return property("name").toString(); }

	//! \~english Returns the registered class name of this object.
	//! \~russian Возвращает зарегистрированное имя класса этого объекта.
	virtual const char * className() const { return "PIObject"; }

	//! \~english Returns the hash of \a className().
	//! \~russian Возвращает хэш от \a className().
	virtual uint classNameID() const {
		static uint ret = PIStringAscii("PIObject").hash();
		return ret;
	}

	static const char * __classNameCC() { return "PIObject"; }
	static uint __classNameIDS() {
		static uint ret = PIStringAscii("PIObject").hash();
		return ret;
	}

	//! \~english Returns the registered parent class name, or an empty string for the root.
	//! \~russian Возвращает зарегистрированное имя родительского класса, либо пустую строку для корня.
	virtual const char * parentClassName() const { return ""; }


	//! \~english Returns whether \a piCoutObj output is enabled for this object.
	//! \~russian Возвращает, включен ли вывод \a piCoutObj для этого объекта.
	bool debug() const { return property("debug").toBool(); }


	//! \~english Sets the \c name property of this object.
	//! \~russian Устанавливает свойство \c name этого объекта.
	void setName(const PIString & name) { setProperty("name", name); }

	//! \~english Enables or disables \a piCoutObj output for this object.
	//! \~russian Включает или отключает вывод \a piCoutObj для этого объекта.
	void setDebug(bool debug) { setProperty("debug", debug); }

	//! \~english Returns the property with name "name".
	//! \~russian Возвращает свойство объекта по имени "name".
	PIVariant property(const char * name) const { return properties_.value(piHashData((const uchar *)name, strlen(name))); }

	//! \~english Sets the property "name" to "value" and creates it if needed.
	//! \~russian Устанавливает свойство "name" в значение "value" и создаёт его при необходимости.
	//! \~\details
	//! \~english Calls \a propertyChanged() after updating the stored value.
	//! \~russian После обновления сохранённого значения вызывает \a propertyChanged().
	void setProperty(const char * name, const PIVariant & value) {
		properties_[piHashData((const uchar *)name, strlen(name))] = value;
		propertyChanged(name);
	}

	//! \~english Returns whether the property "name" exists.
	//! \~russian Возвращает, существует ли свойство "name".
	bool isPropertyExists(const char * name) const { return properties_.contains(piHashData((const uchar *)name, strlen(name))); }

	//! \~english Enables or disables the internal object mutex during handler execution.
	//! \~russian Включает или отключает внутренний мьютекс объекта во время выполнения обработчиков.
	//! \~\details
	//! \~english This flag affects direct and queued handler invocation for this object, but does not describe full thread-safety of the
	//! class.
	//! \~russian Этот флаг влияет на прямой и отложенный вызов обработчиков для данного объекта, но не описывает полную потокобезопасность
	//! класса.
	void setThreadSafe(bool yes) { thread_safe_ = yes; }
	//! \~english Returns whether the internal object mutex is enabled for handler execution.
	//! \~russian Возвращает, включен ли внутренний мьютекс объекта для выполнения обработчиков.
	bool isThreadSafe() const { return thread_safe_; }

	//! \~english Executes a registered method or handler method by name with the supplied arguments.
	//! \~russian Выполняет зарегистрированный метод или метод-обработчик по имени с переданными аргументами.
	//! \~\details
	//! \~english
	//! This helper works only with the registered-method table built from
	//! \a EVENT_HANDLER*() and \a EVENT*() declarations. It does not provide
	//! arbitrary reflection or complex overload resolution: the implementation
	//! selects a suitable registered method by name and argument count.
	//! \~russian
	//! Этот вспомогательный метод работает только с таблицей зарегистрированных
	//! методов, построенной из объявлений \a EVENT_HANDLER*() и \a EVENT*().
	//! Он не предоставляет произвольную рефлексию и сложное разрешение
	//! перегрузок: реализация выбирает подходящий зарегистрированный метод по
	//! имени и числу аргументов.
	bool execute(const PIString & method, const PIVector<PIVariantSimple> & vl);
	//! \~english Overload of \a execute() for a method without arguments.
	//! \~russian Перегрузка \a execute() для метода без аргументов.
	bool execute(const PIString & method) { return execute(method, PIVector<PIVariantSimple>()); }
	//! \~english Overload of \a execute() for one argument.
	//! \~russian Перегрузка \a execute() для одного аргумента.
	bool execute(const PIString & method, const PIVariantSimple & v0) { return execute(method, PIVector<PIVariantSimple>() << v0); }
	//! \~english Overload of \a execute() for two arguments.
	//! \~russian Перегрузка \a execute() для двух аргументов.
	bool execute(const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) {
		return execute(method, PIVector<PIVariantSimple>() << v0 << v1);
	}
	//! \~english Overload of \a execute() for three arguments.
	//! \~russian Перегрузка \a execute() для трёх аргументов.
	bool execute(const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2) {
		return execute(method, PIVector<PIVariantSimple>() << v0 << v1 << v2);
	}
	//! \~english Overload of \a execute() for four arguments.
	//! \~russian Перегрузка \a execute() для четырёх аргументов.
	bool execute(const PIString & method,
	             const PIVariantSimple & v0,
	             const PIVariantSimple & v1,
	             const PIVariantSimple & v2,
	             const PIVariantSimple & v3) {
		return execute(method, PIVector<PIVariantSimple>() << v0 << v1 << v2 << v3);
	}

	//! \~english Queues execution of a registered method on the performer object.
	//! \~russian Ставит выполнение зарегистрированного метода в очередь объекта-исполнителя.
	//! \~\details
	//! \~english
	//! Delivery happens only when "performer" later calls \a callQueuedEvents()
	//! or \a maybeCallQueuedEvents(). Argument values are transported through
	//! \a PIVariantSimple, so queued arguments should be representable there.
	//! \~russian
	//! Доставка происходит только когда "performer" позже вызывает
	//! \a callQueuedEvents() или \a maybeCallQueuedEvents(). Значения аргументов
	//! передаются через \a PIVariantSimple, поэтому аргументы очереди должны в
	//! нём представляться.
	bool executeQueued(PIObject * performer, const PIString & method, const PIVector<PIVariantSimple> & vl);
	//! \~english Overload of \a executeQueued() for a method without arguments.
	//! \~russian Перегрузка \a executeQueued() для метода без аргументов.
	bool executeQueued(PIObject * performer, const PIString & method) {
		return executeQueued(performer, method, PIVector<PIVariantSimple>());
	}
	//! \~english Overload of \a executeQueued() for one argument.
	//! \~russian Перегрузка \a executeQueued() для одного аргумента.
	bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0) {
		return executeQueued(performer, method, PIVector<PIVariantSimple>() << v0);
	}
	//! \~english Overload of \a executeQueued() for two arguments.
	//! \~russian Перегрузка \a executeQueued() для двух аргументов.
	bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) {
		return executeQueued(performer, method, PIVector<PIVariantSimple>() << v0 << v1);
	}
	//! \~english Overload of \a executeQueued() for three arguments.
	//! \~russian Перегрузка \a executeQueued() для трёх аргументов.
	bool executeQueued(PIObject * performer,
	                   const PIString & method,
	                   const PIVariantSimple & v0,
	                   const PIVariantSimple & v1,
	                   const PIVariantSimple & v2) {
		return executeQueued(performer, method, PIVector<PIVariantSimple>() << v0 << v1 << v2);
	}
	//! \~english Overload of \a executeQueued() for four arguments.
	//! \~russian Перегрузка \a executeQueued() для четырёх аргументов.
	bool executeQueued(PIObject * performer,
	                   const PIString & method,
	                   const PIVariantSimple & v0,
	                   const PIVariantSimple & v1,
	                   const PIVariantSimple & v2,
	                   const PIVariantSimple & v3) {
		return executeQueued(performer, method, PIVector<PIVariantSimple>() << v0 << v1 << v2 << v3);
	}

	//! \~english Static convenience wrapper for \a execute().
	//! \~russian Статическая удобная обёртка над \a execute().
	static bool execute(PIObject * o, const PIString & method, const PIVector<PIVariantSimple> & vl) { return o->execute(method, vl); }
	//! \~english Static overload of \a execute() without arguments.
	//! \~russian Статическая перегрузка \a execute() без аргументов.
	static bool execute(PIObject * o, const PIString & method) { return execute(o, method, PIVector<PIVariantSimple>()); }
	//! \~english Static overload of \a execute() for one argument.
	//! \~russian Статическая перегрузка \a execute() для одного аргумента.
	static bool execute(PIObject * o, const PIString & method, const PIVariantSimple & v0) {
		return execute(o, method, PIVector<PIVariantSimple>() << v0);
	}
	//! \~english Static overload of \a execute() for two arguments.
	//! \~russian Статическая перегрузка \a execute() для двух аргументов.
	static bool execute(PIObject * o, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) {
		return execute(o, method, PIVector<PIVariantSimple>() << v0 << v1);
	}
	//! \~english Static overload of \a execute() for three arguments.
	//! \~russian Статическая перегрузка \a execute() для трёх аргументов.
	static bool
	execute(PIObject * o, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2) {
		return execute(o, method, PIVector<PIVariantSimple>() << v0 << v1 << v2);
	}
	//! \~english Static overload of \a execute() for four arguments.
	//! \~russian Статическая перегрузка \a execute() для четырёх аргументов.
	static bool execute(PIObject * o,
	                    const PIString & method,
	                    const PIVariantSimple & v0,
	                    const PIVariantSimple & v1,
	                    const PIVariantSimple & v2,
	                    const PIVariantSimple & v3) {
		return execute(o, method, PIVector<PIVariantSimple>() << v0 << v1 << v2 << v3);
	}

	//! \~english Static convenience wrapper for \a executeQueued().
	//! \~russian Статическая удобная обёртка над \a executeQueued().
	static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVector<PIVariantSimple> & vl) {
		return o->executeQueued(performer, method, vl);
	}
	//! \~english Static overload of \a executeQueued() without arguments.
	//! \~russian Статическая перегрузка \a executeQueued() без аргументов.
	static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method) {
		return executeQueued(o, performer, method, PIVector<PIVariantSimple>());
	}
	//! \~english Static overload of \a executeQueued() for one argument.
	//! \~russian Статическая перегрузка \a executeQueued() для одного аргумента.
	static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVariantSimple & v0) {
		return executeQueued(o, performer, method, PIVector<PIVariantSimple>() << v0);
	}
	//! \~english Static overload of \a executeQueued() for two arguments.
	//! \~russian Статическая перегрузка \a executeQueued() для двух аргументов.
	static bool
	executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) {
		return executeQueued(o, performer, method, PIVector<PIVariantSimple>() << v0 << v1);
	}
	//! \~english Static overload of \a executeQueued() for three arguments.
	//! \~russian Статическая перегрузка \a executeQueued() для трёх аргументов.
	static bool executeQueued(PIObject * o,
	                          PIObject * performer,
	                          const PIString & method,
	                          const PIVariantSimple & v0,
	                          const PIVariantSimple & v1,
	                          const PIVariantSimple & v2) {
		return executeQueued(o, performer, method, PIVector<PIVariantSimple>() << v0 << v1 << v2);
	}
	//! \~english Static overload of \a executeQueued() for four arguments.
	//! \~russian Статическая перегрузка \a executeQueued() для четырёх аргументов.
	static bool executeQueued(PIObject * o,
	                          PIObject * performer,
	                          const PIString & method,
	                          const PIVariantSimple & v0,
	                          const PIVariantSimple & v1,
	                          const PIVariantSimple & v2,
	                          const PIVariantSimple & v3) {
		return executeQueued(o, performer, method, PIVector<PIVariantSimple>() << v0 << v1 << v2 << v3);
	}

	//! \~english Dumps object diagnostics to the project output stream.
	//! \~russian Выводит диагностическую информацию об объекте в проектный поток вывода.
	void dump(const PIString & line_prefix = PIString()) const;


	//! \~english Returns the registered inheritance scope of this object, including its own class.
	//! \~russian Возвращает зарегистрированную цепочку наследования объекта, включая его собственный класс.
	PIStringList scopeList() const;

	//! \~english Returns full signatures of all registered event and handler methods for this class scope.
	//! \~russian Возвращает полные сигнатуры всех зарегистрированных событий и обработчиков для области этого класса.
	PIStringList methodsEH() const;
	//! \~english Returns whether a registered event or handler method with this name exists.
	//! \~russian Возвращает, существует ли зарегистрированное событие или обработчик с таким именем.
	bool isMethodEHContains(const PIString & name) const;
	//! \~english Returns the comma-separated argument type list of a registered method.
	//! \~russian Возвращает список типов аргументов зарегистрированного метода через запятую.
	PIString methodEHArguments(const PIString & name) const;
	//! \~english Returns the full registered signature of a method.
	//! \~russian Возвращает полную зарегистрированную сигнатуру метода.
	PIString methodEHFullFormat(const PIString & name) const;
	//! \~english Returns the registered method name for the specified entry-point address.
	//! \~russian Возвращает имя зарегистрированного метода для указанного адреса точки входа.
	PIString methodEHFromAddr(const void * addr) const;

	//! \~english Low-level direct connection helper behind the legacy \c CONNECT* macros.
	//! \~russian Низкоуровневый помощник прямого соединения, лежащий под устаревшими макросами \c CONNECT*.
	static PIObject::Connection
	piConnect(PIObject * src, const PIString & sig, PIObject * dest_o, void * dest, void * ev_h, void * e_h, int args, const char * loc);
	//! \~english Low-level name-based connection helper behind \a CONNECTU() and \a CONNECTU_QUEUED().
	//! \~russian Низкоуровневый помощник соединения по имени, лежащий под \a CONNECTU() и \a CONNECTU_QUEUED().
	static PIObject::Connection piConnectU(PIObject * src,
	                                       const PIString & sig,
	                                       PIObject * dest_o,
	                                       void * dest,
	                                       const PIString & hname,
	                                       const char * loc,
	                                       PIObject * performer = 0);
	//! \~english Low-level helper that connects an event to a lambda or functor wrapper.
	//! \~russian Низкоуровневый помощник, который соединяет событие с лямбдой или обёрткой функтора.
	static PIObject::Connection piConnectLS(PIObject * src, const PIString & sig, std::function<void()> * f, const char * loc);
	template<typename PIINPUT, typename... PITYPES>
	static std::function<void()> * __newFunctor(void (*stat_handler)(void *, PITYPES...), PIINPUT functor) {
		return (std::function<void()> *)(new std::function<void(PITYPES...)>(functor));
	}


	//! \~english Disconnects this source object from a specific destination handler for event "sig".
	//! \~russian Разрывает соединения этого объекта-источника с конкретным обработчиком объекта-приемника для события "sig".
	void piDisconnect(const PIString & sig, PIObject * dest, void * ev_h) { piDisconnect(this, sig, dest, ev_h); }

	//! \~english Disconnects this source object from all connections of event "sig" to destination object "dest".
	//! \~russian Разрывает все соединения этого объекта-источника от события "sig" к объекту-приемнику "dest".
	void piDisconnect(const PIString & sig, PIObject * dest) { piDisconnect(this, sig, dest); }

	//! \~english Disconnects this source object from all connections of event "sig".
	//! \~russian Разрывает все соединения этого объекта-источника от события "sig".
	void piDisconnect(const PIString & sig) { piDisconnect(this, sig); }


	//! \~english Disconnects source object "src" from a specific destination handler for event "sig".
	//! \~russian Разрывает соединения объекта-источника "src" с конкретным обработчиком объекта-приемника для события "sig".
	static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest, void * ev_h);

	//! \~english Disconnects source object "src" from all connections of event "sig" to destination object "dest".
	//! \~russian Разрывает все соединения объекта-источника "src" от события "sig" к объекту-приемнику "dest".
	static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest);

	//! \~english Disconnects source object "src" from all connections of event "sig".
	//! \~russian Разрывает все соединения объекта-источника "src" от события "sig".
	static void piDisconnect(PIObject * src, const PIString & sig);

	//! \~english Internal event delivery helper for registered events without arguments.
	//! \~russian Внутренний помощник доставки для зарегистрированных событий без аргументов.
	static void raiseEvent(PIObject * sender, const uint eventID) {
		for (int j = 0; j < sender->connections.size_s(); ++j) {
			Connection i(sender->connections[j]);
			if (i.eventID != eventID) continue;
			if (i.functor) {
				(*(i.functor))();
			} else {
				if (i.performer) {
					i.performer->postQueuedEvent(__QueuedEvent(i.slot, i.dest, i.dest_o, sender));
				} else {
					bool ts = sender->thread_safe_;
					if (ts) i.dest_o->mutex_.lock();
					i.dest_o->eventBegin();
					sender->eventBegin();
					i.dest_o->emitter_ = sender;
					((void (*)(void *))i.slot)(i.dest);
					sender->eventEnd();
					if (i.dest_o->isPIObject()) {
						i.dest_o->emitter_ = 0;
						if (ts) i.dest_o->mutex_.unlock();
						i.dest_o->eventEnd();
					}
				}
			}
			if (!sender->isPIObject()) break;
		}
	}

	//! \~english Internal event delivery helper for registered events with one argument.
	//! \~russian Внутренний помощник доставки для зарегистрированных событий с одним аргументом.
	template<typename T0>
	static void raiseEvent(PIObject * sender, const uint eventID, const T0 & v0 = T0()) {
		for (int j = 0; j < sender->connections.size_s(); ++j) {
			Connection i(sender->connections[j]);
			if (i.eventID != eventID) continue;
			if (i.functor) {
				(*((std::function<void(T0)> *)i.functor))(v0);
			} else {
				if (i.performer) {
					PIVector<PIVariantSimple> vl;
					if (i.args_count > 0) vl << PIVariantSimple::fromValue(v0);
					i.performer->postQueuedEvent(__QueuedEvent(i.slot, i.dest, i.dest_o, sender, vl));
				} else {
					bool ts = sender->thread_safe_;
					if (ts) i.dest_o->mutex_.lock();
					i.dest_o->eventBegin();
					sender->eventBegin();
					i.dest_o->emitter_ = sender;
					if (i.args_count == 0)
						((void (*)(void *))i.slot)(i.dest);
					else
						((void (*)(void *, T0))i.slot)(i.dest, v0);
					sender->eventEnd();
					if (i.dest_o->isPIObject()) {
						i.dest_o->emitter_ = 0;
						if (ts) i.dest_o->mutex_.unlock();
						i.dest_o->eventEnd();
					}
				}
			}
			if (!sender->isPIObject()) break;
		}
	}
	//! \~english Internal event delivery helper for registered events with two arguments.
	//! \~russian Внутренний помощник доставки для зарегистрированных событий с двумя аргументами.
	template<typename T0, typename T1>
	static void raiseEvent(PIObject * sender, const uint eventID, const T0 & v0 = T0(), const T1 & v1 = T1()) {
		for (int j = 0; j < sender->connections.size_s(); ++j) {
			Connection i(sender->connections[j]);
			if (i.eventID != eventID) continue;
			if (i.functor) {
				(*((std::function<void(T0, T1)> *)i.functor))(v0, v1);
			} else {
				if (i.performer) {
					PIVector<PIVariantSimple> vl;
					if (i.args_count > 0) vl << PIVariantSimple::fromValue(v0);
					if (i.args_count > 1) vl << PIVariantSimple::fromValue(v1);
					i.performer->postQueuedEvent(__QueuedEvent(i.slot, i.dest, i.dest_o, sender, vl));
				} else {
					bool ts = sender->thread_safe_;
					if (ts) i.dest_o->mutex_.lock();
					i.dest_o->eventBegin();
					sender->eventBegin();
					i.dest_o->emitter_ = sender;
					switch (i.args_count) {
					case 0: ((void (*)(void *))i.slot)(i.dest); break;
					case 1: ((void (*)(void *, T0))i.slot)(i.dest, v0); break;
					default: ((void (*)(void *, T0, T1))i.slot)(i.dest, v0, v1); break;
					}
					sender->eventEnd();
					if (i.dest_o->isPIObject()) {
						i.dest_o->emitter_ = 0;
						if (ts) i.dest_o->mutex_.unlock();
						i.dest_o->eventEnd();
					}
				}
			}
			if (!sender->isPIObject()) break;
		}
	}
	//! \~english Internal event delivery helper for registered events with three arguments.
	//! \~russian Внутренний помощник доставки для зарегистрированных событий с тремя аргументами.
	template<typename T0, typename T1, typename T2>
	static void raiseEvent(PIObject * sender, const uint eventID, const T0 & v0 = T0(), const T1 & v1 = T1(), const T2 & v2 = T2()) {
		for (int j = 0; j < sender->connections.size_s(); ++j) {
			Connection i(sender->connections[j]);
			if (i.eventID != eventID) continue;
			if (i.functor) {
				(*((std::function<void(T0, T1, T2)> *)i.functor))(v0, v1, v2);
			} else {
				if (i.performer) {
					PIVector<PIVariantSimple> vl;
					if (i.args_count > 0) vl << PIVariantSimple::fromValue(v0);
					if (i.args_count > 1) vl << PIVariantSimple::fromValue(v1);
					if (i.args_count > 2) vl << PIVariantSimple::fromValue(v2);
					i.performer->postQueuedEvent(__QueuedEvent(i.slot, i.dest, i.dest_o, sender, vl));
				} else {
					bool ts = sender->thread_safe_;
					if (ts) i.dest_o->mutex_.lock();
					i.dest_o->eventBegin();
					sender->eventBegin();
					i.dest_o->emitter_ = sender;
					switch (i.args_count) {
					case 0: ((void (*)(void *))i.slot)(i.dest); break;
					case 1: ((void (*)(void *, T0))i.slot)(i.dest, v0); break;
					case 2: ((void (*)(void *, T0, T1))i.slot)(i.dest, v0, v1); break;
					default: ((void (*)(void *, T0, T1, T2))i.slot)(i.dest, v0, v1, v2); break;
					}
					sender->eventEnd();
					if (i.dest_o->isPIObject()) {
						i.dest_o->emitter_ = 0;
						if (ts) i.dest_o->mutex_.unlock();
						i.dest_o->eventEnd();
					}
				}
			}
			if (!sender->isPIObject()) break;
		}
	}
	//! \~english Internal event delivery helper for registered events with four arguments.
	//! \~russian Внутренний помощник доставки для зарегистрированных событий с четырьмя аргументами.
	template<typename T0, typename T1, typename T2, typename T3>
	static void raiseEvent(PIObject * sender,
	                       const uint eventID,
	                       const T0 & v0 = T0(),
	                       const T1 & v1 = T1(),
	                       const T2 & v2 = T2(),
	                       const T3 & v3 = T3()) {
		for (int j = 0; j < sender->connections.size_s(); ++j) {
			Connection i(sender->connections[j]);
			if (i.eventID != eventID) continue;
			if (i.functor) {
				(*((std::function<void(T0, T1, T2, T3)> *)i.functor))(v0, v1, v2, v3);
			} else {
				if (i.performer) {
					PIVector<PIVariantSimple> vl;
					if (i.args_count > 0) vl << PIVariantSimple::fromValue(v0);
					if (i.args_count > 1) vl << PIVariantSimple::fromValue(v1);
					if (i.args_count > 2) vl << PIVariantSimple::fromValue(v2);
					if (i.args_count > 3) vl << PIVariantSimple::fromValue(v3);
					i.performer->postQueuedEvent(__QueuedEvent(i.slot, i.dest, i.dest_o, sender, vl));
				} else {
					bool ts = sender->thread_safe_;
					if (ts) i.dest_o->mutex_.lock();
					i.dest_o->eventBegin();
					sender->eventBegin();
					i.dest_o->emitter_ = sender;
					switch (i.args_count) {
					case 0: ((void (*)(void *))i.slot)(i.dest); break;
					case 1: ((void (*)(void *, T0))i.slot)(i.dest, v0); break;
					case 2: ((void (*)(void *, T0, T1))i.slot)(i.dest, v0, v1); break;
					case 3: ((void (*)(void *, T0, T1, T2))i.slot)(i.dest, v0, v1, v2); break;
					default: ((void (*)(void *, T0, T1, T2, T3))i.slot)(i.dest, v0, v1, v2, v3); break;
					}
					sender->eventEnd();
					if (i.dest_o->isPIObject()) {
						i.dest_o->emitter_ = 0;
						if (ts) i.dest_o->mutex_.unlock();
						i.dest_o->eventEnd();
					}
				}
			}
			if (!sender->isPIObject()) break;
		}
	}

	//! \~english Returns the first live object with name "name", or \c nullptr.
	//! \~russian Возвращает первый живой объект с именем "name", либо \c nullptr.
	static PIObject * findByName(const PIString & name) {
		PIMutexLocker _ml(mutexObjects());
		for (auto * i: PIObject::objects()) {
			if (i->name() != name) continue;
			return i;
		}
		return nullptr;
	}

	//! \~english Returns whether this pointer still refers to a live %PIObject instance.
	//! \~russian Возвращает, указывает ли этот указатель на ещё существующий экземпляр %PIObject.
	bool isPIObject() const { return isPIObject(this); }

	//! \~english Returns whether this object belongs to class "T" or one of its registered descendants.
	//! \~russian Возвращает, принадлежит ли этот объект классу "T" или одному из его зарегистрированных потомков.
	template<typename T>
	bool isTypeOf() const {
		if (!isPIObject()) return false;
		PIMutexLocker ml(__meta_mutex());
		return __meta_data()[classNameID()].scope_id.contains(T::__classNameIDS());
	}

	//! \~english Returns this object cast to "T" when \a isTypeOf<T>() succeeds, otherwise \c nullptr.
	//! \~russian Возвращает этот объект, приведённый к типу "T", если \a isTypeOf<T>() успешно, иначе \c nullptr.
	template<typename T>
	T * cast() const {
		if (!isTypeOf<T>()) return (T *)nullptr;
		return (T *)this;
	}

	//! \~english Returns whether "o" points to a live %PIObject instance.
	//! \~russian Возвращает, указывает ли "o" на ещё существующий экземпляр %PIObject.
	static bool isPIObject(const PIObject * o);
	//! \~english Overload of \a isPIObject() for an untyped pointer.
	//! \~russian Перегрузка \a isPIObject() для нетипизированного указателя.
	static bool isPIObject(const void * o) { return isPIObject((PIObject *)o); }

	//! \~english Returns whether "o" belongs to class "T" or one of its registered descendants.
	//! \~russian Возвращает, принадлежит ли "o" классу "T" или одному из его зарегистрированных потомков.
	template<typename T>
	static bool isTypeOf(const PIObject * o) {
		return o->isTypeOf<T>();
	}
	//! \~english Overload of \a isTypeOf() for an untyped pointer.
	//! \~russian Перегрузка \a isTypeOf() для нетипизированного указателя.
	template<typename T>
	static bool isTypeOf(const void * o) {
		return isTypeOf<T>((PIObject *)o);
	}
	//! \~english Simplifies a C++ type spelling for registered-method metadata.
	//! \~russian Упрощает запись типа C++ для метаданных зарегистрированных методов.
	static PIString simplifyType(const char * a, bool readable = true);

	struct PIP_EXPORT __MetaFunc {
		__MetaFunc();
		bool isNull() const { return addr == nullptr; }
		int argumentsCount() const;
		PIString arguments() const;
		PIString fullFormat() const;
		void __setFuncName(const char * n);
		void __addArgument(const char * t, const char * n);
		bool canConnectTo(const __MetaFunc & dst, int & args_count) const;
		void * addr            = nullptr;
		void * addrV           = nullptr;
		uint func_name_id      = 0;
		const char * func_name = nullptr;
		const char * type_ret  = nullptr;
		const char * scope     = nullptr;
		const char * types[__PIOBJECT_MAX_ARGS__];
		const char * names[__PIOBJECT_MAX_ARGS__];
		uint types_id[__PIOBJECT_MAX_ARGS__];
	};

	struct PIP_EXPORT __MetaData {
		__MetaData() {
			scope_list << "PIObject";
			scope_id << PIStringAscii("PIObject").hash();
		}
		void addScope(const char * s, uint shash);
		PIVector<const char *> scope_list;
		PISet<uint> scope_id;
		PISet<const void *> eh_set;
		PIMap<const void *, __MetaFunc> eh_func;
	};
	typedef PIPair<const void *, __MetaFunc> __EHPair;

	//! \~english Executes all queued deliveries posted to this performer object.
	//! \~russian Выполняет все отложенные доставки, поставленные в очередь этому объекту-исполнителю.
	void callQueuedEvents();

	//! \~\brief
	//! \~english Executes queued deliveries only when this object was used as a performer.
	//! \~russian Выполняет отложенные доставки только если этот объект использовался как исполнитель.
	//! \~\details
	//! \~english This helper is cheaper than unconditional \a callQueuedEvents() for objects that are rarely used as performer targets.
	//! \~russian Этот помощник дешевле, чем безусловный \a callQueuedEvents(), для объектов, которые редко используются как исполнители.
	bool maybeCallQueuedEvents() {
		if (proc_event_queue) callQueuedEvents();
		return proc_event_queue;
	}

	//! \~english Schedules the object for deferred deletion.
	//! \~russian Планирует отложенное удаление объекта.
	void deleteLater();

	//! \events
	//! \{

	//! \fn void deleted(PIObject * o)
	//! \brief
	//! \~english Raised immediately before object destruction.
	//! \~russian Вызывается непосредственно перед уничтожением объекта.
	//! \~\warning
	//! \~english
	//! This event raised from destructor, so use only "o" numeric value,
	//! don`t try to cast deleted object to some subclass!
	//! \~russian
	//! Это событие вызывается из деструктора, поэтому используйте
	//! только численное значение "o", не надо кастовать его в другие типы!
	EVENT1(deleted, PIObject *, o);

	//! \}

	static PIMutex & __meta_mutex();
	static PIMap<uint, __MetaData> & __meta_data();

protected:
	//! \~english Returns the source object that raised the current event.
	//! \~russian Возвращает объект-источник, который вызвал текущее событие.
	//! \~\details
	//! \~english This value is valid only while an event handler is running.
	//! \~russian Это значение корректно только пока выполняется обработчик события.
	PIObject * emitter() const { return emitter_; }

	//! \~english Virtual method called after property "name" has been changed by \a setProperty().
	//! \~russian Виртуальный метод, вызываемый после изменения свойства "name" через \a setProperty().
	virtual void propertyChanged(const char * name) {}

private:
	struct __QueuedEvent {
		__QueuedEvent(void * sl                           = 0,
		              void * d                            = 0,
		              PIObject * d_o                      = 0,
		              PIObject * s                        = 0,
		              const PIVector<PIVariantSimple> & v = PIVector<PIVariantSimple>()) {
			slot   = sl;
			dest   = d;
			dest_o = d_o;
			src    = s;
			values = v;
		}
		void * slot;
		void * dest;
		PIObject * dest_o;
		PIObject * src;
		PIVector<PIVariantSimple> values;
	};

	class Deleter {
	public:
		Deleter();
		~Deleter();
		static Deleter * instance();
		void post(PIObject * o);

	private:
		void deleteObject(PIObject * o);
		PRIVATE_DECLARATION(PIP_EXPORT)
	};

	bool findSuitableMethodV(const PIString & method, int args, int & ret_args, __MetaFunc & ret);
	PIVector<__MetaFunc> findEH(const PIString & name) const;
	__MetaFunc methodEH(const void * addr) const;
	void updateConnectors();
	void piDisconnectAll();

	void postQueuedEvent(const __QueuedEvent & e);
	void eventBegin() { in_event_cnt++; }
	void eventEnd() { in_event_cnt--; }
	bool isInEvent() const { return in_event_cnt > 0; }
	void * toThis() const;
	virtual int ptrOffset() const { return 0; }

	static PIVector<PIObject *> & objects();
	static PIMutex & mutexObjects();
	static void callAddrV(void * slot, void * obj, int args, const PIVector<PIVariantSimple> & vl);


	PIVector<Connection> connections;
	PIMap<uint, PIVariant> properties_;
	PISet<PIObject *> connectors;
	PIVector<__QueuedEvent> events_queue;
	PIMutex mutex_, mutex_connect, mutex_queue;
	PIObject * emitter_;
	bool thread_safe_, proc_event_queue;
	std::atomic_int in_event_cnt;
};

#ifndef MICRO_PIP

//! \~english Dumps application-level %PIObject diagnostics.
//! \~russian Выводит диагностическую информацию уровня приложения для %PIObject.
PIP_EXPORT void dumpApplication(bool with_objects = true);

//! \~english Dumps application-level %PIObject diagnostics to file "path".
//! \~russian Выводит диагностическую информацию уровня приложения для %PIObject в файл "path".
PIP_EXPORT bool dumpApplicationToFile(const PIString & path, bool with_objects = true);

#endif

#endif // PIOBJECT_H
