/**
 * minisig - minimal signals and slots library
 * version 0.4 (5 IV 2008)
 * by Piotr Beling
 * 
 * minisig is provided under the terms of the GNU Library Public License,
 * Version 2 with exceptions that allow for static linking.
 * 
 * This file includes whole library,
 * just include it in Your project to use it.
 * 
 * How to use (minimal example):
 * 
 * void x(int i) { cout << i << endl; }
 * int y(long l) { cout << 2*l << endl; return 0; }
 * struct A { void a(int i) { std::cout << "A::a" << " " << i << endl; } };
 * void ignore_arg() { cout << "arg ignored" << endl; };
 * 
 * ...
 * 
 * minisig::signal<int> s;
 * s += x;
 * s += y;
 * A aa;
 * s.connect(aa, &A::a);
 * s.connect_ign1(ignore_arg);
 * s(1);	//call: x(1); y(1); aa.a(1); ignore_arg();
 * 
 * Version history:
 * v0.4 (5 IV 2008): fixed compilation problem in gcc 4.3 (rename connection template to sconnection) 
 * v0.3 (11 IX 2007): used smart pointers for connections in singal (fixed copy constructor for signals and allowed to use one connection in muny signals)
 * v0.2 (7 IX 2007): operator(...) in signals and connections are now const
 * v0.1 (6 IX 2007): first public relase
 */

#ifndef __PIOTR_BELING__MINISIG_
#define __PIOTR_BELING__MINISIG_

#include <vector>
#include <algorithm>

namespace minisig {

//connection (base)

template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
struct sconnection;

template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
struct connection_base {
	typedef Arg1 Arg1_T;
	typedef Arg2 Arg2_T;
	typedef Arg3 Arg3_T;
	typedef Arg4 Arg4_T;
	typedef sconnection<Arg1, Arg2, Arg3, Arg4> Self_T;
	virtual ~connection_base() {};
	
	/**
	 * Create functor wrapper. 
	 */
	template<typename FunctorT> static Self_T* create(FunctorT f);
	
	/**
	 * Create method member wrapper 
	 */
	template <typename T, typename fun> static Self_T* create(T& object, fun to_call);
};

/**
 * Represents connection between signal (with Arg1, ..., ArgN arguments) and any slot:
 * callback function, object member, etc.
 * It's always connect with one signal object,
 * and it's automatic delete by this signal object.
 * 
 * Each connect method return pointer to connect object (see signal.connect).
 * You must save this pointer if You plan disconnect slot from signal (see signal.disconnect).
 */
template <typename Arg1 = void, typename Arg2 = void, typename Arg3 = void, typename Arg4 = void>
struct sconnection: public connection_base<Arg1, Arg2, Arg3, Arg4> {
	typedef sconnection<Arg1, Arg2, Arg3, void> Less1_t;
	/**
	 * Calling saved slot with given arguments.
	 */
	virtual void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) const = 0;
};

template <typename Arg1, typename Arg2, typename Arg3>
struct sconnection<Arg1, Arg2, Arg3, void>: public connection_base<Arg1, Arg2, Arg3, void> {
	typedef sconnection<Arg1, Arg2, void, void> Less1_t;
	virtual void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3) const = 0;
};

template <typename Arg1, typename Arg2>
struct sconnection<Arg1, Arg2, void, void>: public connection_base<Arg1, Arg2, void, void> {
	typedef sconnection<Arg1, void, void, void> Less1_t;
	virtual void operator()(Arg1 arg1, Arg2 arg2) const  = 0;
};

template <typename Arg1>
struct sconnection<Arg1, void, void, void>: public connection_base<Arg1, void, void, void> {
	typedef sconnection<void, void, void, void> Less1_t;
	virtual void operator()(Arg1 arg1) const  = 0;
};

template <>
struct sconnection<void, void, void, void>: public connection_base<void, void, void, void> {
	typedef sconnection<void, void, void, void> Less1_t;
	virtual void operator()() const = 0;
};

//connection wrapper

/*template <typename orginal_connection_t, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
struct connection_wrapper: public connection<Arg1, Arg2, Arg3, Arg4> {
	orginal_connection_t* orginal_connection;
	explicit connection_wrapper(orginal_connection_t* orginal_connection): orginal_connection(orginal_connection) {};
	virtual ~connection_wrapper() { delete orginal_connection; }
};*/

//connection ignore last

#define __msig_do_ignore_last_conn_body(ARGS...) \
	typedef typename sconnection<ARGS>::Less1_t Less1_t; \
	Less1_t* orginal_connection; \
	explicit connection_ign1(Less1_t* orginal_connection): orginal_connection(orginal_connection) {} \
	virtual ~connection_ign1() { delete orginal_connection; }

template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
struct connection_ign1: public sconnection<Arg1, Arg2, Arg3, Arg4> {
	typedef connection_ign1<Arg1, Arg2, Arg3, void> ignore_next_t;
	__msig_do_ignore_last_conn_body(Arg1, Arg2, Arg3, Arg4)
	virtual void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) const { (*orginal_connection)(arg1, arg2, arg3); }	
};

template <typename Arg1, typename Arg2, typename Arg3>
struct connection_ign1<Arg1, Arg2, Arg3, void>: public sconnection<Arg1, Arg2, Arg3> {
	typedef connection_ign1<Arg1, Arg2, void, void> ignore_next_t;
	__msig_do_ignore_last_conn_body(Arg1, Arg2, Arg3)
	virtual void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3) const { (*orginal_connection)(arg1, arg2); }	
};

template <typename Arg1, typename Arg2>
struct connection_ign1<Arg1, Arg2, void, void>: public sconnection<Arg1, Arg2> {
	typedef connection_ign1<Arg1, void, void, void> ignore_next_t;
	__msig_do_ignore_last_conn_body(Arg1, Arg2)
	virtual void operator()(Arg1 arg1, Arg2 arg2) const { (*orginal_connection)(arg1); }	
};

template <typename Arg1>
struct connection_ign1<Arg1, void, void, void>: public sconnection<Arg1> {
    	typedef connection_ign1<void, void, void, void> ignore_next_t;
	__msig_do_ignore_last_conn_body(Arg1)
	virtual void operator()(Arg1 arg1) const { (*orginal_connection)(); }	
};

#undef __msig_do_ignore_last_conn_body

//ignore last connection for 0 arguments do nothing
template <>
struct connection_ign1<void, void, void, void>: public sconnection<> {
    	typedef connection_ign1<void, void, void, void> ignore_next_t;
	Less1_t* orginal_connection;
	explicit connection_ign1(Less1_t* orginal_connection): orginal_connection(orginal_connection) {};
	virtual ~connection_ign1() { delete orginal_connection; }
	virtual void operator()() const { (*orginal_connection)(); }	
};

//connectionFun (connection with function)

#define __msig_do_delegate_operator_4(FUNNAME) \
virtual void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) const { FUNNAME(arg1, arg2, arg3, arg4); }
		
#define __msig_do_delegate_operator_3(FUNNAME) \
virtual void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3) const { FUNNAME(arg1, arg2, arg3); }

#define __msig_do_delegate_operator_2(FUNNAME) \
virtual void operator()(Arg1 arg1, Arg2 arg2) const { FUNNAME(arg1, arg2); }

#define __msig_do_delegate_operator_1(FUNNAME) virtual void operator()(Arg1 arg1) const { FUNNAME(arg1); }

#define __msig_do_delegate_operator_0(FUNNAME) virtual void operator()() const { FUNNAME(); }

template <typename FunctT, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
struct connection_func: public sconnection<Arg1, Arg2, Arg3, Arg4> {
	FunctT f;
	explicit connection_func(FunctT f): f(f) {}
	__msig_do_delegate_operator_4(f)
};

template <typename FunctT, typename Arg1, typename Arg2, typename Arg3>
struct connection_func<FunctT, Arg1, Arg2, Arg3, void>: public sconnection<Arg1, Arg2, Arg3, void> {
	FunctT f;
	explicit connection_func(FunctT f): f(f) {}
	__msig_do_delegate_operator_3(f)
};

template <typename FunctT, typename Arg1, typename Arg2>
struct connection_func<FunctT, Arg1, Arg2, void, void>: public sconnection<Arg1, Arg2, void, void> {
	FunctT f;
	explicit connection_func(FunctT f): f(f) {}
	__msig_do_delegate_operator_2(f)
};

template <typename FunctT, typename Arg1>
struct connection_func<FunctT, Arg1, void, void, void>: public sconnection<Arg1, void, void, void> {
	FunctT f;
	explicit connection_func(FunctT f): f(f) {}
	__msig_do_delegate_operator_1(f)
};

template <typename FunctT>
struct connection_func<FunctT, void, void, void, void>: public sconnection<void, void, void, void> {
	FunctT f;
	explicit connection_func(FunctT f): f(f) {}
	__msig_do_delegate_operator_0(f)
};

//connectionM (connection with class method)

#define __msig_do_connection_mem_body(ARGS...) \
	T& object;						\
	fun to_call;					\
	connection_mem(T& object, fun to_call): object(object), to_call(to_call) {}

template <typename T, typename fun, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
struct connection_mem: public sconnection<Arg1, Arg2, Arg3, Arg4> {
	__msig_do_connection_mem_body(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4);
	__msig_do_delegate_operator_4((object.*to_call))
};

template <typename T, typename fun, typename Arg1, typename Arg2, typename Arg3>
struct connection_mem<T, fun, Arg1, Arg2, Arg3, void>: public sconnection<Arg1, Arg2, Arg3, void> {
	__msig_do_connection_mem_body(Arg1 arg1, Arg2 arg2, Arg3 arg3)
	__msig_do_delegate_operator_3((object.*to_call))
};

template <typename T, typename fun, typename Arg1, typename Arg2>
struct connection_mem<T, fun, Arg1, Arg2, void, void>: public sconnection<Arg1, Arg2, void, void> {
	__msig_do_connection_mem_body(Arg1 arg1, Arg2 arg2)
	__msig_do_delegate_operator_2((object.*to_call))
};

template <typename T, typename fun, typename Arg1>
struct connection_mem<T, fun, Arg1, void, void, void>: public sconnection<Arg1, void, void, void> {
	__msig_do_connection_mem_body(Arg1 arg1)
	__msig_do_delegate_operator_1((object.*to_call))
};

template <typename T, typename fun>
struct connection_mem<T, fun, void, void, void, void>: public sconnection<void, void, void, void> {
	__msig_do_connection_mem_body()
	__msig_do_delegate_operator_0((object.*to_call))
};

#undef __msig_do_delegate_operator_4
#undef __msig_do_delegate_operator_3
#undef __msig_do_delegate_operator_2
#undef __msig_do_delegate_operator_1
#undef __msig_do_delegate_operator_0

// connection_base create* methods definition

template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
template <typename FunctorT>
inline sconnection<Arg1, Arg2, Arg3, Arg4>*
connection_base<Arg1, Arg2, Arg3, Arg4>::create(FunctorT f) {
	return new connection_func<FunctorT, Arg1, Arg2, Arg3, Arg4>(f);
}

template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
template <typename T, typename fun>
inline sconnection<Arg1, Arg2, Arg3, Arg4>*
connection_base<Arg1, Arg2, Arg3, Arg4>::create(T& object, fun to_call) {
	return new connection_mem<T, fun, Arg1, Arg2, Arg3, Arg4>(object, to_call);
}

namespace __implemenatation_helpers {

template <class X>
class counted_ptr {
public:
    typedef X element_type;

    explicit counted_ptr(X* p = 0) // allocate a new counter
        : itsCounter(0) {if (p) itsCounter = new counter(p);}
    ~counted_ptr() {release();}
    bool operator==(const counted_ptr& to_cmp) const { return this->get() == to_cmp.get(); }
    counted_ptr(const counted_ptr& r) throw() {acquire(r.itsCounter);}
    counted_ptr& operator=(const counted_ptr& r)
    {
        if (this != &r) {
            release();
            acquire(r.itsCounter);
        }
        return *this;
    }

/*#ifndef NO_MEMBER_TEMPLATES
    template <class Y> friend class counted_ptr<Y>;
    template <class Y> counted_ptr(const counted_ptr<Y>& r) throw()
        {acquire(r.itsCounter);}
    template <class Y> counted_ptr& operator=(const counted_ptr<Y>& r)
    {
        if (this != &r) {
            release();
            acquire(r.itsCounter);
        }
        return *this;
    }
#endif // NO_MEMBER_TEMPLATES*/

    X& operator*()  const throw() {return *itsCounter->ptr;}
    X* operator->() const throw() {return itsCounter->ptr;}
    X* get()        const throw() {return itsCounter ? itsCounter->ptr : 0;}
    bool unique()   const throw() {return (itsCounter ? itsCounter->count == 1 : true);}

private:

    struct counter {
        counter(X* p = 0, unsigned c = 1) : ptr(p), count(c) {}
        X*          ptr;
        unsigned    count;
    }* itsCounter;

    void acquire(counter* c) throw() { // increment the count
        itsCounter = c;
        if (c) ++c->count;
    }

    void release() { // decrement the count, delete if it is 0
        if (itsCounter) {
            if (--itsCounter->count == 0) {
                delete itsCounter->ptr;
                delete itsCounter;
            }
            itsCounter = 0;
        }
    }
};

};	//namespace __implemenatation_helpers


/**
 * Base class for all signals.
 */
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
class signal_base {
	
	public:
	
	/**
	 * Connection type for this signal class.
	 */
	typedef sconnection<Arg1, Arg2, Arg3, Arg4> connection;
	typedef typename connection::Less1_t connection_less1;
	typedef __implemenatation_helpers::counted_ptr<connection> connection_p;
	
	protected:
	
	typedef std::vector<connection_p> slots_set;
	typedef typename slots_set::iterator slots_iterator;
	typedef typename slots_set::const_iterator const_slots_iterator;
	
	slots_set slots;
	
	public:
	
	/**
	 * Connect slot with signal.
	 * Slot will be automatic delete with delete operator by signal when it will be disconnected
	 * or signal will be distroyed.
	 * @param slot slot to connect
	 * @return slot
	 */
	connection_p connect_slot(connection* slot) {
		connection_p result = connection_p(slot);
		slots.push_back(result);
		return result;
	}
	
	connection_p connect_slot(connection_p slot) {
		slots.push_back(slot);
		return slot;
	}
	
	/**
	 * Same as connect(slot).
	 */
	connection* operator+=(connection* slot) { return connect_slot(slot); }
	
	static connection* create_conn_ign1(typename connection::Less1_t* to_decorate) {
		return new connection_ign1<Arg1, Arg2, Arg3, Arg4>(to_decorate);
	}
	
	static connection* create_conn_ign2(typename connection::Less1_t::Less1_t* to_decorate) {
		return create_conn_ign1(
		    new typename connection_ign1<Arg1, Arg2, Arg3, Arg4>::ignore_next_t(to_decorate));
	}
	
	connection_p connect_slot_ign1(typename connection::Less1_t* to_decorate) {
		return connect_slot(create_conn_ign1(to_decorate));
	}
	
	connection_p connect_slot_ign2(typename connection::Less1_t::Less1_t* to_decorate) {
		return connect_slot(create_conn_ign2(to_decorate));
	}
	
	template <typename FunctorT>
	static connection* create_conn(FunctorT f) {
		return new connection_func<FunctorT, Arg1, Arg2, Arg3, Arg4>(f);
	}
	
	/**
	 * Create a connection between this slot and created signal which wrap given functor
	 * (function or functor object).
	 * @param f functor to wrap
	 * @return created connection
	 */
	template <typename FunctorT>
	connection_p connect(FunctorT f) {
		return connect_slot(create_conn<FunctorT>(f));
	}
	
	/**
	 * Same as connectF<FunctorT>(f).
	 */
	template <typename FunctorT>
	connection_p operator+=(FunctorT f) { return connect<FunctorT>(f); }
	
	/**
	 * Connect functor with one argument less then require.
	 * Last argument are simply not passed.
	 */
	template <typename FunctorT>
	connection_p connect_ign1(FunctorT f) {
		return connect_slot_ign1(connection_less1::create(f));
	}
	
	template <typename FunctorT>
	connection_p connect_ign2(FunctorT f) {
		return connect_slot_ign2(connection_less1::Less1_t::create(f));
	}
	
	/**
	 * Create a connection between this slot and created signal which wrap given class member
	 * (and this class object)
	 * @param object oject with to_call member
	 * @param to_call method to call
	 * @return created connection
	 */
	template <typename T, typename fun>
	connection_p connect(T& object, fun to_call) {
		return connect_slot(new connection_mem<T, fun, Arg1, Arg2, Arg3, Arg4>(object, to_call));
	}
	
	template <typename T, typename fun>
	connection_p connect_ign1(T& object, fun to_call) {
		return connect_slot_ign1(connection_less1::create(object, to_call));
	}
	
	template <typename T, typename fun>
	connection_p connect_ign2(T& object, fun to_call) {
		return connect_slot_ign2(connection_less1::Less1_t::create(object, to_call));
	}
	
	/**
	 * Disconnect given connection and, if there are no other references to this connection, delete it.
	 * Do nothing if given slots was not added to this signal.
	 * @param slot connection to disconnect and delete
	 */
	void disconnect(connection_p slot) {
		slots.erase(std::remove(slots.begin(), slots.end(), slot), slots.end());
	}
	
	/**
	 * Same as disconnect(slot).
	 */
	void operator-=(connection_p slot) { disconnect(slot); }
	
	/**
	 * Disconnect and delete lastly added slot.
	 * Do nothing if none slots was added to this signal.
	 */
	void disconnect_last() {
		if (slots.empty()) return;
		slots.pop_back();
	}
	
	/**
	 * Disconnect and (eventualy) delete all slots.
	 */
	void disconnect_all() {
		slots.clear();
	}

};

/**
 * signal is container for some slots instance (connect object)
 */
template <typename Arg1 = void, typename Arg2 = void, typename Arg3 = void, typename Arg4 = void>
struct signal: public signal_base<Arg1, Arg2, Arg3, Arg4> {
	
	using signal_base<Arg1, Arg2, Arg3, Arg4>::slots;
	
	/**
	 * Call all signals with given arguments.
	 */
	void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) const {
		for (typename signal_base<Arg1, Arg2, Arg3, Arg4>::const_slots_iterator i = slots.begin(); i != slots.end(); ++i)
			(**i)(arg1, arg2, arg3, arg4);
	}
};

template <typename Arg1, typename Arg2, typename Arg3>
struct signal<Arg1, Arg2, Arg3>: public signal_base<Arg1, Arg2, Arg3, void> {
	
	using signal_base<Arg1, Arg2, Arg3, void>::slots;
	
	void operator()(Arg1 arg1, Arg2 arg2, Arg3 arg3) const {
		for (typename signal_base<Arg1, Arg2, Arg3, void>::const_slots_iterator i = slots.begin(); i != slots.end(); ++i)
			(**i)(arg1, arg2, arg3);
	}
};

template <typename Arg1, typename Arg2>
struct signal<Arg1, Arg2>: public signal_base<Arg1, Arg2, void, void> {
	
	using signal_base<Arg1, Arg2, void, void>::slots;
	
	void operator()(Arg1 arg1, Arg2 arg2) const {
		for (typename signal_base<Arg1, Arg2, void, void>::const_slots_iterator i = slots.begin(); i != slots.end(); ++i)
			(**i)(arg1, arg2);
	}
};

template <typename Arg1>
struct signal<Arg1>: public signal_base<Arg1, void, void, void> {
	
	using signal_base<Arg1, void, void, void>::slots;
	
	void operator()(Arg1 arg1) const {
		for (typename signal_base<Arg1, void, void, void>::const_slots_iterator i = slots.begin(); i != slots.end(); ++i)
			(**i)(arg1);
	}
};

template <>
struct signal<void, void, void, void>: public signal_base<void, void, void, void> {
	
	using signal_base<void, void, void, void>::slots;
	
	void operator()() const {
		for (const_slots_iterator i = slots.begin(); i != slots.end(); ++i)
			(**i)();
	}
};

}; //namespace minisig

#endif /*__PIOTR_BELING__MINISIG_*/

