#!/usr/bin/perl

$NS = "sigslot";
$num = $ARGV[0];
if( $num < 2 ) {
        die "Usage: $0 NUMBER\nwhere NUMBER > 1.";
}


@argt = ();
@argn = ();
$arg_type_list_named = "";
for( $i = 1; $i <= $num; $i++ ) {
        local $a = "arg".$i."_type";
        local $aname = "a${i}";
        push( @argt, $a );
        push( @argn, $aname );
        $arg_type_list_named .= "$a $aname, ";
}
$arg_type_list_named =~ s/, *$//;
$arg_params_names = join( ",", @argn );
$arg_tmpl_decl = "class ".join(", class ",@argt);
$arg_type_list = join(", ",@argt);

undef @argt;
undef @argn;

#print join("\n",@argt),"\n";

print << "EOF"
namespace $NS {
// auto-generated by $0
// tmpl decl= $arg_tmpl_decl
// args= $arg_type_list
// named args= $arg_type_list_named
EOF
;

########################################################################
# slot_proxy
print << "EOF"
        /**
           See slot_proxy0. This class is identical except that it
           proxies functions with ${num} arguments.
        */
        template <typename T, ${arg_tmpl_decl} >
        struct slot_proxy${num} : public has_slots
        {
                typedef void (T::* slot_func)( ${arg_type_list} );
                T * obj;
                slot_func memfunc;
                slot_proxy${num}(T&t, slot_func f) : obj(&t),memfunc(f){}
                void proxy_slot( ${arg_type_list_named} )
                {
                        ((this->obj)->*memfunc)( ${arg_params_names} );
                }
                void proxy(T&t, slot_func f)
                {
                        this->obj = &t;
                        this->memfunc = f;
                }
        };


        /**
           A helper class to proxy calls to global/static functions
	   taking ${num} arguments.

           See slot_proxy_func0 for the documentation.
        */
	template < ${arg_tmpl_decl} >
        struct slot_proxy_func${num} : public has_slots
        {
                typedef void (*slot_func)( ${arg_type_list} );
                slot_func func;
                /**
                   Creates a proxy object for f.
                */
                slot_proxy_func${num}(slot_func f) : func(f){}

                /**
                   Passes on the arguments to the proxied function.
                */
                void proxy( ${arg_type_list_named} )
                {
                        return func( ${arg_params_names} );
                }
                /**
                   Sets this object's proxied function to f.
                */
                void proxy_func(slot_func f)
                {
                        this->func = f;
                }

        };


EOF
;

########################################################################
# connection_base
print << "EOF"

        /**
           Base type for connections involving ${num}-arg signals/slots.
        */
	template<${arg_tmpl_decl}>
	class connection_base${num}
	{
	public:
		typedef connection_base${num}<${arg_type_list}> this_type;

		virtual ~connection_base${num}(){}

		virtual slotted_base* getdest() const = 0;
		virtual void SIGSLOT_EMIT(${arg_type_list}) const = 0;
		virtual this_type * clone() const = 0; // { return 0; }
		virtual this_type * duplicate(slotted_base* pnewdest) const = 0; // { return 0; }
// BOO: compiler is not allowing me to instantiate subclasses of this type,
// saying that clone() and duplicate() are not implemented, even though they are.

	};
EOF
;


########################################################################
# connection
print << "EOF"
        /**
           Connection type for ${num}-arg signals/slots.
        */
	template<class dest_type, ${arg_tmpl_decl}>
	class connection${num} : public connection_base${num}<${arg_type_list}>
	{
	public:
		typedef connection_base${num}<${arg_type_list} > base_type;
		typedef connection${num}< dest_type, ${arg_type_list} > this_type;
		typedef void (dest_type::* slot_func)(${arg_type_list});

		connection${num}() : m_pobject(0),m_pmemfun(0)
		{
		}

		virtual ~connection${num}() {}

		connection${num}(dest_type* pobject, slot_func pfunc ) : m_pobject(pobject),m_pmemfun(pfunc)
		{
		}

                virtual base_type * clone() const
                {
                        return new this_type(*this);
                }

		virtual base_type * duplicate(slotted_base* pnewdest) const
		{
			return new this_type(dynamic_cast<dest_type *>(pnewdest), m_pmemfun);
		}

                virtual slotted_base* getdest() const
                {
                        return m_pobject;
                }

                virtual void SIGSLOT_EMIT(${arg_type_list_named}) const
                {
                        (m_pobject->*m_pmemfun)(${arg_params_names});
                }


	private:
		dest_type* m_pobject;
		slot_func m_pmemfun;
	};

EOF
;


########################################################################
# signal
print << "EOF"
	/**
           A signal accepting ${num} arguments.
	*/
	template<${arg_tmpl_decl} >
	class signal${num} : public signal_base
	{
	public:
		typedef has_slots slotted_base;
		typedef std::list<connection_base${num}<${arg_type_list}> *>
			connections_list;

		signal${num}()
		{
			;
		}

		signal${num}(const signal${num}<${arg_type_list}>& s)
		       : signal_base(s)
		{
			if( &s == this ) return;
			lock_block<mt_policy> lock(this->mutex());
			this->copy_connections( s.connections(), this->connections() );
		}


		template<class desttype>
		void SIGSLOT_CONNECT(desttype* pslot, void (desttype::*pmemfun)(${arg_type_list}))
		{
			lock_block<mt_policy> lock(this->mutex());
			connection_base${num}<${arg_type_list}> * conn =
				new connection${num}<desttype, ${arg_type_list}>(pslot,pmemfun);
			this->connections().push_back(conn);
			pslot->add_signaler(this);
		}

		void SIGSLOT_EMIT(${arg_type_list_named}) const
		{
			//lock_block<mt_policy> lock(this->mutex());
			typename connections_list::const_iterator it = this->connections().begin();
			typename connections_list::const_iterator itEnd = this->connections().end();

			for( ; itEnd != it; ++it)
			{
				(*it)->SIGSLOT_EMIT(${arg_params_names});
			}
		}

		void operator()(${arg_type_list_named}) const
		{
			this->SIGSLOT_EMIT( ${arg_params_names} );
		}


		signal${num} & operator=(const signal${num}& s)
		{
			this->copy_connections( s.connections(), this->connections() );
			return *this;
		}

		~signal${num}()
		{
			disconnect_all();
		}

		void disconnect_all()
		{
			lock_block<mt_policy> lock(this->mutex());
			this->free_connections(connections());
		}

		bool slot_duplicate(const slotted_base* oldtarget, slotted_base* newtarget)
		{
			lock_block<mt_policy> lock(this->mutex());
			return this->duplicate_connection(oldtarget, newtarget,connections());
		}

	protected:
		bool slot_disconnect(slotted_base* pslot)
		{
			lock_block<mt_policy> lock(this->mutex());
			return this->disconnect_slot( pslot,
						      this->connections() );
		}

		connections_list & connections() { return this->m_connected_slots; }
		const connections_list & connections() const { return this->m_connected_slots; }
	private:
		connections_list m_connected_slots;

	};
EOF
;

print << "EOF"
} // namespace $NS
EOF
;

1;
