/*
    ircsignalhandler.h - Maps signals from the IRC engine to contacts

    Copyright (c) 2004      by Jason Keirstead <jason@keirstead.org>

    Kopete    (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#ifndef _IRC_SIGNAL_HANDLER_H
#define _IRC_SIGNAL_HANDLER_H

#include <tqobject.h>
#include <tqstringlist.h>
#include <tqdatetime.h>

#include <kdebug.h>

#include "ircaccount.h"
#include "irccontactmanager.h"

/***
* IRC Signal handler. Mapps a KIRC engine signal to the right contact. Avoids
* Having a signal connected to 500+ slots where only one is valid, instead
* uses the contact dictionary.
*
* Warning: This file has a lot of black magic in it. Avoid it if
* you don't want your eyes to bleed. More below...
*
* Define some templated classes and methods to map a KIRC signal to the
* right contact. Having these templates greatly cuts down *A LOT* on the amount of
* code that would need to be in the signal mapper, at the expense of some readability.
*
* There are four IRCSignalMapping classes, one each for signals with 0, 1, 2,
* and 3 arguments ( plus the contact ID ). The classes take the signal, look
* up the contact it is for, and call the function passed into the class by the
* mapping function.
*
* Since TQObjects cannot be inside templates, the QMember classes that connect
* to the slots are seperate.
*/

/*** Pre-declare mapping types for the TQObjects **/
struct IRCSignalMappingBase{};

struct IRCSignalMappingT : IRCSignalMappingBase
{
	virtual void exec( const TQString & ) = 0;
	virtual ~IRCSignalMappingT() {};
};

struct IRCSignalMappingSingleT : IRCSignalMappingBase
{
	virtual void exec( const TQString &, const TQString & ) = 0;
	virtual ~IRCSignalMappingSingleT() {};
};

struct IRCSignalMappingDoubleT : IRCSignalMappingBase
{
	virtual void exec( const TQString &, const TQString &, const TQString & ) = 0;
	virtual ~IRCSignalMappingDoubleT() {};
};

struct IRCSignalMappingTripleT : IRCSignalMappingBase
{
	virtual void exec( const TQString &, const TQString &, const TQString &, const TQString & ) = 0;
	virtual ~IRCSignalMappingTripleT() {};
};

/***
TQObject members, these connect to the KIRC signals and call
the Mapping functions when they emit.
**/

class QMember : public TQObject
{
	TQ_OBJECT
  

	public:
		QMember( IRCSignalMappingT *m, TQObject *p ) : TQObject( p ), mapping( m ){};

	public slots:
		void slotEmit( const TQString &id )
		{
			//kdDebug(14120) << k_funcinfo << id << endl;
			mapping->exec(id);
		}

	private:
		IRCSignalMappingT *mapping;
};

class QMemberSingle : public TQObject
{
	TQ_OBJECT
  

	public:
		QMemberSingle( IRCSignalMappingSingleT *m, TQObject *p ) : TQObject( p ), mapping( m ){}

	public slots:
		void slotEmit( const TQString &id, const TQString &arg )
		{
			//kdDebug(14120) << k_funcinfo << id << " : " << arg  << endl;
			mapping->exec(id,arg);
		}

	private:
		IRCSignalMappingSingleT *mapping;
};

class QMemberDouble : public TQObject
{
	TQ_OBJECT
  

	public:
		QMemberDouble( IRCSignalMappingDoubleT *m, TQObject *p ) : TQObject( p ), mapping( m ){}

	public slots:
		void slotEmit( const TQString &id, const TQString &arg, const TQString &arg2 )
		{
			//kdDebug(14120) << k_funcinfo << id << " : " << arg << " : " << arg2 << endl;
			mapping->exec(id,arg,arg2);
		}

	private:
		IRCSignalMappingDoubleT *mapping;
};

class QMemberTriple : public TQObject
{
	TQ_OBJECT
  

	public:
		QMemberTriple( IRCSignalMappingTripleT *m, TQObject *p ) : TQObject( p ), mapping( m ){}

	public slots:
		void slotEmit( const TQString &id, const TQString &arg, const TQString &arg2, const TQString &arg3 )
		{
			//kdDebug(14120) << k_funcinfo << id << " : " << arg << " : " << arg2 << " : " << arg3 << endl;
			mapping->exec(id,arg,arg2,arg3);
		}

	private:
		IRCSignalMappingTripleT *mapping;
};

/***
Mapping classes. These contain pointers to the functions to call. We first
look up the right contact in the contact manager's dictionary, and then
call the method
**/

template <class TClass>
class IRCSignalMapping : public IRCSignalMappingT
{
	public:
		IRCSignalMapping( IRCContactManager *mgr, const char * /*signal*/,
			void (TClass::*m)() ) : manager(mgr), method(m){}

		void exec( const TQString &id )
		{
			TClass *c = (TClass*)manager->findContact( id );
			if( c )
			{
				void (TClass::*tmp)() = (void (TClass::*)())method;
				(*c.*tmp)();
			}
		}

	private:
		IRCContactManager *manager;
		void (TClass::*method)();
};

template <class TClass>
class IRCSignalMappingSingle : public IRCSignalMappingSingleT
{
	public:
		IRCSignalMappingSingle<TClass>( IRCContactManager *mgr, const char * /*signal*/,
			void (TClass::*m)(const TQString&) ) : manager(mgr), method(m){}

		void exec( const TQString &id, const TQString &arg )
		{
			TClass *c = (TClass*)manager->findContact( id );
			if( c )
			{
				void (TClass::*tmp)(const TQString&) = (void (TClass::*)(const TQString&))method;
				(*c.*tmp)( arg );
			}
		}

	private:
		IRCContactManager *manager;
		void (TClass::*method)(const TQString &);
};

template <class TClass>
class IRCSignalMappingDouble : public IRCSignalMappingDoubleT
{
	public:
		IRCSignalMappingDouble<TClass>( IRCContactManager *mgr, const char * /*signal*/,
			void (TClass::*m)(const TQString&,const TQString&) ) : manager(mgr), method(m){}

		void exec( const TQString &id,const TQString &arg, const TQString &arg2 )
		{
			TClass *c = (TClass*)manager->findContact( id );
			if( c )
			{
				void (TClass::*tmp)(const TQString&,const TQString&) =
					(void (TClass::*)(const TQString&,const TQString&))method;
				(*c.*tmp)(arg,arg2);
			}
		}

	private:
		IRCContactManager *manager;
		void (TClass::*method)(const TQString &,const TQString &);
};

template <class TClass>
class IRCSignalMappingTriple : public IRCSignalMappingTripleT
{
	public:
		IRCSignalMappingTriple<TClass>( IRCContactManager *mgr, const char * /*signal*/,
			void (TClass::*m)(const TQString&,const TQString&,const TQString&) )
			: manager(mgr), method(m){}

		void exec( const TQString &id,const TQString&arg, const TQString &arg2, const TQString &arg3 )
		{
			TClass *c = (TClass*)manager->findContact( id );
			if( c )
			{
				void (TClass::*tmp)(const TQString&,const TQString&,const TQString&) =
					(void (TClass::*)(const TQString&,const TQString&,const TQString&))method;
				(*c.*tmp)(arg,arg2,arg3);
			}
		}

	private:
		IRCContactManager *manager;
		void (TClass::*method)(const TQString &,const TQString &,const TQString &);
};

class IRCSignalHandler : public TQObject
{
	TQ_OBJECT
  

	public:
		IRCSignalHandler( IRCContactManager *manager );
		~IRCSignalHandler();

	private slots:

		/****
		Slots for signals with non-TQString types
		*/

		//Channel contact slots
		void slotNamesList( const TQString &, const TQStringList & );
		void slotEndOfNames( const TQString & );
		void slotTopicUser( const TQString &, const TQString&, const TQDateTime &);

		//User contact slots
		void slotNewWhoIsIdle(const TQString &, unsigned long  );
		void slotNewWhoReply(const TQString &, const TQString &, const TQString &, const TQString &,
			const TQString &, bool , const TQString &, uint , const TQString & );

	private:
		IRCContactManager *manager;
		TQValueList<IRCSignalMappingBase*> mappings;

		/****
		Signal mapping functions
		*/

		template <class TClass>
		inline void map( IRCContactManager *m, const char* signal, void (TClass::*method)() )
		{
			IRCSignalMappingT *mapping = new IRCSignalMapping<TClass>( m, signal, method );
			mappings.append(mapping);
			TQObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal,
				new QMember( mapping, this),
				TQ_SLOT( slotEmit( const TQString &) )
			);
		}

		template <class TClass>
		inline void mapSingle( IRCContactManager *m,
			const char* signal, void (TClass::*method)(const TQString&) )
		{
			IRCSignalMappingSingleT *mapping = new IRCSignalMappingSingle<TClass>( m, signal, method );
			mappings.append(mapping);
			TQObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal,
				new QMemberSingle( mapping, this),
				TQ_SLOT( slotEmit( const TQString &, const TQString &) )
			);
		}

		template <class TClass>
		inline void mapDouble( IRCContactManager *m,
			const char* signal, void (TClass::*method)(const TQString&,const TQString&) )
		{
			IRCSignalMappingDoubleT *mapping = new IRCSignalMappingDouble<TClass>( m, signal, method );
			mappings.append(mapping);
			TQObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal,
				new QMemberDouble( mapping, this),
				TQ_SLOT( slotEmit( const TQString &, const TQString &,const TQString &) )
			);
		}

		template <class TClass>
		inline void mapTriple( IRCContactManager *m,
			const char* signal,
			void (TClass::*method)(const TQString&,const TQString &, const TQString &) )
		{
			IRCSignalMappingTripleT *mapping = new IRCSignalMappingTriple<TClass>( m, signal, method );
			mappings.append(mapping);
			TQObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal,
				new QMemberTriple( mapping, this),
				TQ_SLOT( slotEmit( const TQString &, const TQString &,const TQString &,const TQString &) )
			);
		}
};

#endif