/*! \file piobject.h
 * \ingroup Core
 * \~\brief
 * \~english Base object
 * \~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 "pivariant.h"
#include "pivariantsimple.h"
#include "pimutex.h"
#include "piset.h"
#include "piqueue.h"
#include "piobject_macros.h"

//! \ingroup Core
//! \~\brief
//! \~english This is base class for any classes which use events -> handlers mechanism.
//! \~russian Этот класс является базовым для использования механизма события -> обработчики.
class PIP_EXPORT PIObject {
#ifndef MICRO_PIP
	friend class PIObjectManager;
	friend void dumpApplication(bool);
	friend class PIIntrospection;
#endif
	typedef PIObject __PIObject__;
	typedef void __Parent__;
public:
	NO_COPY_CLASS(PIObject);

	//! \~english Contructs %PIObject with name "name"
	//! \~russian Создает %PIObject с именем "name"
	explicit PIObject(const PIString & name = PIString());
	
	virtual ~PIObject();

	//! \ingroup Core
	//! \~\brief
	//! \~english Helper class for obtain info about if connection successful and disconnect single connection.
	//! \~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 Contructs invalid %Connection
		//! \~russian Создает недействительный %Connection
		Connection();

		//! \~english Returns if %Connection is valid
		//! \~russian Возвращает успешен ли %Connection
		bool isValid() const {return signal;}

		//! \~english Returns source object
		//! \~russian Возвращает объект-источник
		PIObject * sourceObject() const {return src_o;}

		//! \~english Returns destination object or "nullptr" if this is lambda connection
		//! \~russian Возвращает объект-приемник или "nullptr" если это соединение на лямбда-функцию
		PIObject * destinationObject() const {return dest_o;}

		//! \~english Returns performer object or "nullptr" if this is non-queued connection
		//! \~russian Возвращает объект-исполнитель или "nullptr" если это соединение не отложенное
		PIObject * performerObject() const {return performer;}

		//! \~english Disconnect this %Connection, returns if operation successful
		//! \~russian Разрывает этот %Connection, возвращает успешен ли разрыв
		bool disconnect();
	};

private:
	uint _signature_;

public:
	
	//! \~english Returns object name
	//! \~russian Возвращает имя объекта
	PIString name() const {return property("name").toString();}
	
	//! \~english Returns object class name
	//! \~russian Возвращает имя класса объекта
	virtual const char * className() const {return "PIObject";}

	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 parent class name
	//! \~russian Возвращает имя родительского класса
	virtual const char * parentClassName() const {return "";}


	//! \~english Return if \a piCoutObj of this object is active
	//! \~russian Возвращает включен ли вывод \a piCoutObj для этого объекта
	bool debug() const {return property("debug").toBool();}
	
	
	//! \~english Set object name
	//! \~russian Устанавливает имя объекта
	void setName(const PIString & name) {setProperty("name", name);}

	//! \~english Set object \a piCoutObj active
	//! \~russian Включает или отключает вывод \a piCoutObj для этого объекта
	void setDebug(bool debug) {setProperty("debug", debug);}
	
	//! \~english Returns property with name "name"
	//! \~russian Возвращает свойство объекта по имени "name"
	PIVariant property(const char * name) const {return properties_.value(piHashData((const uchar *)name, strlen(name)));}
	
	//! \~english Set property with name "name" to "value". If there is no such property in object it will be added
	//! \~russian Устанавливает у объекта свойство по имени "name" в "value". Если такого свойства нет, оно добавляется
	void setProperty(const char * name, const PIVariant & value) {properties_[piHashData((const uchar *)name, strlen(name))] = value; propertyChanged(name);}
	
	//! \~english Returns if property with name "name" exists
	//! \~russian Возвращает присутствует ли свойство по имени "name"
	bool isPropertyExists(const char * name) const {return properties_.contains(piHashData((const uchar *)name, strlen(name)));}
	
	void setThreadSafe(bool yes) {thread_safe_ = yes;}
	bool isThreadSafe() const {return thread_safe_;}

	bool execute(const PIString & method, const PIVector<PIVariantSimple> & vl);
	bool execute(const PIString & method) {return execute(method, PIVector<PIVariantSimple>());}
	bool execute(const PIString & method, const PIVariantSimple & v0) {return execute(method, PIVector<PIVariantSimple>() << v0);}
	bool execute(const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) {return execute(method, PIVector<PIVariantSimple>() << v0 << v1);}
	bool execute(const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2) {return execute(method, PIVector<PIVariantSimple>() << v0 << v1 << v2);}
	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);}

	bool executeQueued(PIObject * performer, const PIString & method, const PIVector<PIVariantSimple> & vl);
	bool executeQueued(PIObject * performer, const PIString & method) {return executeQueued(performer, method, PIVector<PIVariantSimple>());}
	bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0) {return executeQueued(performer, method, PIVector<PIVariantSimple>() << v0);}
	bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) {return executeQueued(performer, method, PIVector<PIVariantSimple>() << v0 << v1);}
	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);}
	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);}

	static bool execute(PIObject * o, const PIString & method, const PIVector<PIVariantSimple> & vl) {return o->execute(method, vl);}
	static bool execute(PIObject * o, const PIString & method) {return execute(o, method, PIVector<PIVariantSimple>());}
	static bool execute(PIObject * o, const PIString & method, const PIVariantSimple & v0) {return execute(o, method, PIVector<PIVariantSimple>() << v0);}
	static bool execute(PIObject * o, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) {return execute(o, method, PIVector<PIVariantSimple>() << v0 << v1);}
	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);}
	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);}

	static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVector<PIVariantSimple> & vl) {return o->executeQueued(performer, method, vl);}
	static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method) {return executeQueued(o, performer, method, PIVector<PIVariantSimple>());}
	static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVariantSimple & v0) {return executeQueued(o, performer, method, PIVector<PIVariantSimple>() << v0);}
	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);}
	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);}
	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);}

	void dump(const PIString & line_prefix = PIString()) const;
	
	
	//! \~english Returns subclass scope of this object (including this class name)
	//! \~russian Возвращает цепочку наследования объекта (вместе с классом самого объекта)
	PIStringList scopeList() const;

	PIStringList methodsEH() const;
	bool isMethodEHContains(const PIString & name) const;
	PIString methodEHArguments(const PIString & name) const;
	PIString methodEHFullFormat(const PIString & name) const;
	PIString methodEHFromAddr(const void * addr) const;
	
	// / Direct 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);
	static PIObject::Connection piConnectU(PIObject * src, const PIString & sig, PIObject * dest_o, void * dest, const PIString & hname, const char * loc, PIObject * performer = 0);
	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 Disconnect object from all connections with event name "sig", connected to destination object "dest" and handler "ev_h"
	//! \~russian Разрывает все соединения от события "sig" к объекту "dest" и обработчику "ev_h"
	void piDisconnect(const PIString & sig, PIObject * dest, void * ev_h) {piDisconnect(this, sig, dest, ev_h);}

	//! \~english Disconnect object from all connections with event name "sig", connected to destination object "dest"
	//! \~russian Разрывает все соединения от события "sig" к объекту "dest"
	void piDisconnect(const PIString & sig, PIObject * dest) {piDisconnect(this, sig, dest);}

	//! \~english Disconnect object from all connections with event name "sig"
	//! \~russian Разрывает все соединения от события "sig"
	void piDisconnect(const PIString & sig) {piDisconnect(this, sig);}


	//! \~english Disconnect object "src" from all connections with event name "sig", connected to destination object "dest" and handler "ev_h"
	//! \~russian Разрывает все соединения от события "sig" объекта "src" к объекту "dest" и обработчику "ev_h"
	static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest, void * ev_h);

	//! \~english Disconnect object "src" from all connections with event name "sig", connected to destination object "dest"
	//! \~russian Разрывает все соединения от события "sig" объекта "src" к объекту "dest"
	static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest);
	
	//! \~english Disconnect object "src" from all connections with event name "sig"
	//! \~russian Разрывает все соединения от события "sig" объекта "src"
	static void piDisconnect(PIObject * src, const PIString & sig);
		
	// / Raise events
	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;
		}
	}
	
	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;
		}
	}
	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;
		}
	}
	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;
		}
	}
	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;
		}
	}
	
	//! Returns PIObject* with name "name" or 0, if there is no object found
	static PIObject * findByName(const PIString & name) {
		PIMutexLocker _ml(mutexObjects());
		piForeach (PIObject * i, PIObject::objects()) {
			if (i->name() != name) continue;
			return i;
		}
		return nullptr;
	}
	
	//! \~english Returns if this is valid %PIObject (check signature)
	//! \~russian Возвращает действительный ли это %PIObject (проверяет подпись)
	bool isPIObject() const {return isPIObject(this);}

	//! \~english Returns if this is valid %PIObject subclass "T" (check signature and classname)
	//! \~russian Возвращает действительный ли это наследник %PIObject типа "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 cast to T if this is valid subclass "T" (check by \a isTypeOf()) or "nullptr"
	//! \~russian Возвращает преобразование к типу T если это действительный наследник типа "T" (проверяет через \a isTypeOf()), или "nullptr"
	template<typename T>
	T * cast() const {
		if (!isTypeOf<T>()) return (T*)nullptr;
		return (T*)this;
	}

	//! \~english Returns if "o" is valid %PIObject (check signature)
	//! \~russian Возвращает действительный ли "o" %PIObject (проверяет подпись)
	static bool isPIObject(const PIObject * o);
	static bool isPIObject(const void * o) {return isPIObject((PIObject*)o);}

	//! \~english Returns if "o" is valid %PIObject subclass "T" (check signature and classname)
	//! \~russian Возвращает действительный ли "o" наследник %PIObject типа "T" (проверяет подпись и имя класса)
	template<typename T>
	static bool isTypeOf(const PIObject * o) {return o->isTypeOf<T>();}
	template<typename T>
	static bool isTypeOf(const void * o) {return isTypeOf<T>((PIObject*)o);}
	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 Execute all posted events from CONNECTU_QUEUED connections
	//! \~russian Выполнить все отложенные события от CONNECTU_QUEUED соединений
	void callQueuedEvents();

	//! \~english
	//! \brief Check if any CONNECTU_QUEUED connections to this object and execute them
	//! \details This function is more optimized than \a callQueuedEvents() for objects that doesn`t
	//! appears as \"performer\" target at CONNECTU_QUEUED
	//! \~russian
	//! \brief Если было хотя бы одно CONNECTU_QUEUED соединение с исполнителем this, то выполнить события
	//! \details Этот метод более оптимален, чем \a callQueuedEvents(), для объектов, которые не были в роли
	//! \"performer\" в макросе CONNECTU_QUEUED
	bool maybeCallQueuedEvents() {if (proc_event_queue) callQueuedEvents(); return proc_event_queue;}

	//! \~english Mark object to delete
	//! \~russian Пометить объект на удаление
	void deleteLater();

	static PIMutex & __meta_mutex();
	static PIMap<uint, __MetaData> & __meta_data(); // [hash(classname)]=__MetaData

protected:
	
	//! \~english Returns %PIObject* which has raised an event. This value is correct only in definition of some event handler
	//! \~russian Возвращает %PIObject* который вызвал это событие. Значение допустимо только из методов обработчиков событий
	PIObject * emitter() const {return emitter_;}
	
	//! \~english Virtual function executes after property with name "name" has been changed
	//! \~russian Виртуальная функция, вызывается после изменения любого свойства.
	virtual void propertyChanged(const char * name) {}

	EVENT1(deleted, PIObject *, o);
	
//! \events
//! \{

	//! \fn void deleted(PIObject * o)
	//! \brief
	//! \~english Raise before object delete
	//! \~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", не надо кастовать его в другие типы!

//! \}

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
PIP_EXPORT void dumpApplication(bool with_objects = true);
PIP_EXPORT bool dumpApplicationToFile(const PIString & path, bool with_objects = true);
#endif

#endif // PIOBJECT_H
