/*
    kircengine.h - IRC Client

    Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr>
    Copyright (c) 2003      by Jason Keirstead <jason@keirstead.org>
    Copyright (c) 2002      by Nick Betcher <nbetcher@kde.org>

    Kopete    (c) 2002-2004 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 KIRCENGINE_H
#define KIRCENGINE_H

#include "kircentity.h"
#include "kircmessage.h"
#include "kircmessageredirector.h"
#include "kirctransfer.h"

#include <tdeversion.h>

// FIXME: Move the following kdedebug class to the *.cpp.
#include <kdebug.h>
#if TDE_VERSION < TDE_MAKE_VERSION( 3, 1, 90 )
#include <kdebugclasses.h>
#endif

#include <tqdatetime.h>
#include <tqdict.h>
#include <tqintdict.h>
#include <tqregexp.h>
#include <tqstring.h>
#include <tqstringlist.h>

class TQRegExp;

namespace KIRC
{

/**
 * @author Nick Betcher <nbetcher@kde.org>
 * @author Michel Hermier <michel.hermier@wanadoo.fr>
 * @author Jason Keirstead <jason@keirstead.org>
 */
class Engine
	: public TQObject
{
	TQ_OBJECT
  

//	TQ_PROPERTY(TQUrl serverURL READ serverURL WRITE setServerURL)

//	Extracted from the base of the serverURL.
//	TQ_PROPERTY(bool useSSL);
//	TQ_PROPERTY(TQString user READ user);
//	TQ_PROPERTY(TQString password);
//	TQ_PROPERTY(TQString host READ host);
//	TQ_PROPERTY(int port READ host);

//	Extracted from the query of the serverURL.
//	TQ_PROPERTY(bool reqsPasswd);
//	TQ_PROPERTY(TQString name); // real name
//	TQ_PROPERTY(TQStringList nickList READ nickList WRITE setNickList)
//	TQ_PROPERTY(TQString nick READ nick)
//	TQ_PROPERTY(TQStringList portList)

	TQ_ENUMS(Status)

public:
	enum Error
	{
		ParsingFailed,
		UnknownCommand,
		UnknownNumericReply,
		InvalidNumberOfArguments,
		MethodFailed
	};

	enum Status
	{
		Idle,
		Connecting,
		Authentifying,
		Connected,
		Closing,
		AuthentifyingFailed,
		Timeout,
		Disconnected
	};

	enum ServerMessageType
	{
		ErrorMessage = -1,
		PrivateMessage,
		InfoMessage,

		MessageOfTheDayMessage,
		MessageOfTheDayCondensedMessage
	};

	Engine( TQObject *parent = 0, const char* name = 0 );
	~Engine();

//	TQString nick() const;
//	TQStringList nickList() const;
//	void setNickList(const TQStringList& nickList);

//	TQUrl serverURL() const;
//	bool setServerURL(const TQUrl &url);

	inline const TQString &currentHost() const
		{ return m_Host; };

	inline TQ_UINT16 currentPort()
		{ return m_Port; }

	inline const TQString &nickName() const
		{ return m_Nickname; };

	inline const TQString &password() const
		{ return m_Passwd; }

	inline void setPassword(const TQString &passwd)
		{ m_Passwd = passwd; };

	inline const TQString &userName() const
		{ return m_Username; }

	void setUserName(const TQString &newName);

	void setRealName(const TQString &newName);
	inline const TQString &realName() const
		{ return m_realName; }

	inline const bool reqsPassword() const
		{ return m_ReqsPasswd; };

	inline void setReqsPassword(bool b)
		{ m_ReqsPasswd = b; };

	const bool useSSL() const { return m_useSSL; };
	void setUseSSL( bool useSSL );

	inline const TQTextCodec *codec() const
		{ return defaultCodec; };

	const TQTextCodec *codecForNick( const TQString &nick ) const;

	inline void setDefaultCodec( TQTextCodec* codec )
		{ defaultCodec = codec; };

	void setVersionString(const TQString &versionString);
	void setUserString(const TQString &userString);
	void setSourceString(const TQString &sourceString);
	void connectToServer(const TQString &host, TQ_UINT16 port, const TQString &nickname, bool useSSL = false);

	KExtendedSocket *socket()
		{ return m_sock; };

	inline KIRC::Engine::Status status() const
		{ return m_status; }

	inline bool isDisconnected() const
		{ return m_status == Disconnected || m_status == Idle; }

	inline bool isConnected() const
		{ return m_status == Connected; }

	inline void setCodec( const TQString &nick, const TQTextCodec *codec )
		{ codecs.replace( nick, codec ); }

	/* Custom CTCP replies handling */
	inline TQString &customCtcp( const TQString &s )
	{ return customCtcpMap[s];  }

	inline void addCustomCtcp( const TQString &ctcp, const TQString &reply )
	{ customCtcpMap[ ctcp.lower() ] = reply; }

	KIRC::EntityPtr getEntity(const TQString &name);

public slots:
	//Message output
	void writeRawMessage(const TQString &message);

	void writeMessage(const TQString &message, const TQTextCodec *codec = 0 );
	void writeMessage(const TQString &command, const TQStringList &args,
		const TQString &suffix = TQString(), const TQTextCodec *codec = 0);

	void writeCtcpMessage(const TQString &command, const TQString &to, const TQString &ctcpMessage);

	void writeCtcpMessage(const TQString &command, const TQString &to, const TQString &suffix,
		const TQString &ctcpCommand, const TQStringList &ctcpArgs, const TQString &ctcpSuffix = TQString(),
		bool emitRepliedCtcp = true);

	inline void writeCtcpQueryMessage(const TQString &to, const TQString &suffix,
		const TQString &ctcpCommand, const TQStringList &ctcpArgs = TQStringList(), const TQString &ctcpSuffix = TQString(),
		bool emitRepliedCtcp = true)
		{ return writeCtcpMessage("PRIVMSG", to, suffix, ctcpCommand, ctcpArgs, ctcpSuffix, emitRepliedCtcp); }

	inline void writeCtcpReplyMessage(const TQString &to, const TQString &ctcpMessage)
		{ writeCtcpMessage("NOTICE", to, ctcpMessage); }

	inline void writeCtcpReplyMessage(const TQString &to, const TQString &suffix,
		const TQString &ctcpCommand, const TQStringList &ctcpArgs = TQStringList(), const TQString &ctcpSuffix = TQString(),
		bool emitRepliedCtcp = true)
		{ return writeCtcpMessage("NOTICE", to, suffix, ctcpCommand, ctcpArgs, ctcpSuffix, emitRepliedCtcp); }

	inline void writeCtcpErrorMessage(const TQString &to, const TQString &ctcpLine, const TQString &errorMsg,
		bool emitRepliedCtcp=true)
		{ return writeCtcpReplyMessage(to, TQString(), "ERRMSG", ctcpLine, errorMsg, emitRepliedCtcp); }

	bool bind(const TQString &command, TQObject *object, const char *member,
		  int minArgs = KIRC::MessageRedirector::Unknown,
		  int maxArgs = KIRC::MessageRedirector::Unknown,
		  const TQString &helpMessage = TQString());

	bool bind(int id, TQObject *object, const char *member,
		  int minArgs = KIRC::MessageRedirector::Unknown,
		  int maxArgs = KIRC::MessageRedirector::Unknown,
		  const TQString &helpMessage = TQString());

	bool bindCtcpQuery(const TQString &command, TQObject *object, const char *member,
			   int minArgs = KIRC::MessageRedirector::Unknown,
			   int maxArgs = KIRC::MessageRedirector::Unknown,
			   const TQString &helpMessage = TQString());

	bool bindCtcpReply(const TQString &command, TQObject *object, const char *member,
			   int minArgs = KIRC::MessageRedirector::Unknown,
			   int maxArgs = KIRC::MessageRedirector::Unknown,
			   const TQString &helpMessage = TQString());


	void away(bool isAway, const TQString &awayMessage = TQString());
	void ison(const TQStringList &nickList);
	void join(const TQString &name, const TQString &key);
	void kick(const TQString &user, const TQString &channel, const TQString &reason);
	void list();
	void mode(const TQString &target, const TQString &mode);
	void motd(const TQString &server = TQString());
	void nick(const TQString &newNickname);
	void notice(const TQString &target, const TQString &message);
	void part(const TQString &name, const TQString &reason);
	void pass(const TQString &password);
	void privmsg(const TQString &contact, const TQString &message);

	/**
	 * Send a quit message for the given reason.
	 * If now is set to true the connection is closed and no event message is sent.
	 * Therefore setting now to true should only be used while destroying the object.
	 */
	void quit(const TQString &reason, bool now=false);

	void topic(const TQString &channel, const TQString &topic);
	void user(const TQString &newUsername, const TQString &hostname, const TQString &newRealname);
	void user(const TQString &newUsername, TQ_UINT8 mode, const TQString &newRealname);
	void whois(const TQString &user);


	/* CTCP commands */
	void CtcpRequestCommand(const TQString &contact, const TQString &command);
	void CtcpRequest_action(const TQString &contact, const TQString &message);
	void CtcpRequest_dcc(const TQString &, const TQString &, unsigned int port, KIRC::Transfer::Type type);
	void CtcpRequest_ping(const TQString &target);
	void CtcpRequest_version(const TQString &target);

public slots:
	void showInfoDialog();

signals:
	void statusChanged(KIRC::Engine::Status newStatus);
	void internalError(KIRC::Engine::Error, KIRC::Message &);

	void receivedMessage(KIRC::Message &);

	/**
	 * Emit a received message.
	 * The received message could have been translated to your locale.
	 *
	 * @param type the message type.
	 * @param from the originator of the message.
	 * @param to is the list of entities that are related to this message.
	 * @param msg the message (usually translated).
	 *
	 * @note Most of the following numeric messages should be deprecated, and call this method instead.
	 *	 Most of the methods, using it, update KIRC::Entities.
	 *	 Lists based messages are sent via dedicated API, therefore they don't use this.
	 */
	// @param args the args to apply to this message.
	void receivedMessage(	KIRC::Engine::ServerMessageType type,
				const KIRC::EntityPtr &from,
				const KIRC::EntityPtrList &to,
				const TQString &msg);

	void successfullyChangedNick(const TQString &, const TQString &);

	//ServerContact Signals
	void incomingMotd(const TQString &motd);
	void incomingNotice(const TQString &originating, const TQString &message);
	void incomingHostInfo(const TQString &servername, const TQString &version,
		const TQString &userModes, const TQString &channelModes);
	void incomingYourHostInfo(const TQString &servername, const TQString &version,
		const TQString &userModes, const TQString &channelModes);
	void incomingConnectString(const TQString &clients);

	//Channel Contact Signals
	void incomingMessage(const TQString &originating, const TQString &target, const TQString &message);
	void incomingTopicChange(const TQString &, const TQString &, const TQString &);
	void incomingExistingTopic(const TQString &, const TQString &);
	void incomingTopicUser(const TQString &channel, const TQString &user, const TQDateTime &time);
	void incomingJoinedChannel(const TQString &channel,const TQString &nick);
	void incomingPartedChannel(const TQString &channel,const TQString &nick, const TQString &reason);
	void incomingNamesList(const TQString &channel, const TQStringList &nicknames);
	void incomingEndOfNames(const TQString &channel);
	void incomingChannelMode(const TQString &channel, const TQString &mode, const TQString &params);
	void incomingCannotSendToChannel(const TQString  &channel, const TQString &message);
	void incomingChannelModeChange(const TQString &channel, const TQString &nick, const TQString &mode);
	void incomingChannelHomePage(const TQString &channel, const TQString &url);

	//Contact Signals
	void incomingPrivMessage(const TQString &, const TQString &, const TQString &);
	void incomingQuitIRC(const TQString &user, const TQString &reason);
	void incomingUserModeChange(const TQString &nick, const TQString &mode);
	void incomingNoSuchNickname(const TQString &nick);

	// CTCP Signals
//	void action(const TQString &from, const TQString &to, const TQString &message);
	void incomingAction(const TQString &channel, const TQString &originating, const TQString &message);
	void incomingPrivAction(const TQString &target, const TQString &originating, const TQString &message);

	//Response Signals
	void incomingUserOnline(const TQString &nick);
	void incomingWhoIsUser(const TQString &nickname, const TQString &username,
		const TQString &hostname, const TQString &realname);
	void incomingWhoWasUser(const TQString &nickname, const TQString &username,
		const TQString &hostname, const TQString &realname);
	void incomingWhoIsServer(const TQString &nickname, const TQString &server, const TQString &serverInfo);
	void incomingWhoIsOperator(const TQString &nickname);
	void incomingWhoIsIdentified(const TQString &nickname);
	void incomingWhoIsChannels(const TQString &nickname, const TQString &channel);
	void incomingWhoIsIdle(const TQString &nickname, unsigned long seconds); /* 317 */
	void incomingSignOnTime(const TQString &nickname, unsigned long seconds); /* 317 */
	void incomingEndOfWhois(const TQString &nickname);
	void incomingEndOfWhoWas(const TQString &nickname);

	void incomingWhoReply( const TQString &nick, const TQString &channel, const TQString &user, const TQString &host,
		const TQString &server,bool away, const TQString &flag, uint hops, const TQString &realName );

	void incomingEndOfWho( const TQString &query );

	//Error Message Signals
	void incomingServerLoadTooHigh();
	void incomingNickInUse(const TQString &usingNick);
	void incomingNickChange(const TQString &, const TQString &);
	void incomingFailedServerPassword();
	void incomingFailedChankey(const TQString &);
	void incomingFailedChanBanned(const TQString &);
	void incomingFailedChanInvite(const TQString &);
	void incomingFailedChanFull(const TQString &);
	void incomingFailedNickOnLogin(const TQString &);
	void incomingNoNickChan(const TQString &);
	void incomingWasNoNick(const TQString &);

	//General Signals
	void incomingUnknown(const TQString &);
	void incomingUnknownCtcp(const TQString &);
	void incomingKick(const TQString &channel, const TQString &nick,
		const TQString &nickKicked, const TQString &reason);

	void incomingUserIsAway(const TQString &nick, const TQString &awayMessage);
	void incomingListedChan(const TQString &chan, uint users, const TQString &topic);
	void incomingEndOfList();

	void incomingCtcpReply(const TQString &type, const TQString &target, const TQString &messageReceived);

private slots:
	void destroyed(KIRC::Entity *entity);

	void slotReadyRead();

	void slotConnected();
	void slotConnectionClosed();
	void error(int errCode = 0);

	void ignoreMessage(KIRC::Message &msg);
	void emitSuffix(KIRC::Message &);

	void error(KIRC::Message &msg);
	void join(KIRC::Message &msg);
	void kick(KIRC::Message &msg);
	void mode(KIRC::Message &msg);
	void nick(KIRC::Message &msg);
	void notice(KIRC::Message &msg);
	void part(KIRC::Message &msg);
	void ping(KIRC::Message &msg);
	void pong(KIRC::Message &msg);
	void privmsg(KIRC::Message &msg);
//	void squit(KIRC::Message &msg);
	void quit(KIRC::Message &msg);
	void topic(KIRC::Message &msg);

	void numericReply_001(KIRC::Message &msg);
	void numericReply_002(KIRC::Message &msg);
	void numericReply_003(KIRC::Message &msg);
	void numericReply_004(KIRC::Message &msg);
	void numericReply_005(KIRC::Message &msg);
	void numericReply_250(KIRC::Message &msg);
	void numericReply_251(KIRC::Message &msg);
	void numericReply_252(KIRC::Message &msg);
	void numericReply_253(KIRC::Message &msg);
	void numericReply_254(KIRC::Message &msg);
	void numericReply_255(KIRC::Message &msg);
	void numericReply_263(KIRC::Message &msg);
	void numericReply_265(KIRC::Message &msg);
	void numericReply_266(KIRC::Message &msg);
	void numericReply_301(KIRC::Message &msg);
	void numericReply_303(KIRC::Message &msg);
//	void numericReply_305(KIRC::Message &msg);
//	void numericReply_306(KIRC::Message &msg);
	void numericReply_307(KIRC::Message &msg);
	void numericReply_311(KIRC::Message &msg);
	void numericReply_312(KIRC::Message &msg);
	void numericReply_313(KIRC::Message &msg);
	void numericReply_314(KIRC::Message &msg);
	void numericReply_315(KIRC::Message &msg);
	void numericReply_317(KIRC::Message &msg);
	void numericReply_318(KIRC::Message &msg);
	void numericReply_319(KIRC::Message &msg);
	void numericReply_320(KIRC::Message &msg);
	void numericReply_322(KIRC::Message &msg);
	void numericReply_323(KIRC::Message &msg);
	void numericReply_324(KIRC::Message &msg);
	void numericReply_328(KIRC::Message &msg);
	void numericReply_329(KIRC::Message &msg);
	void numericReply_331(KIRC::Message &msg);
	void numericReply_332(KIRC::Message &msg);
	void numericReply_333(KIRC::Message &msg);
	void numericReply_352(KIRC::Message &msg);
	void numericReply_353(KIRC::Message &msg);
	void numericReply_366(KIRC::Message &msg);
	void numericReply_369(KIRC::Message &msg);
	void numericReply_372(KIRC::Message &msg);
//	void numericReply_376(KIRC::Message &msg);

	void numericReply_401(KIRC::Message &msg);
	void numericReply_406(KIRC::Message &msg);
	void numericReply_422(KIRC::Message &msg);
	void numericReply_433(KIRC::Message &msg);
	void numericReply_464(KIRC::Message &msg);
	void numericReply_471(KIRC::Message &msg);
	void numericReply_473(KIRC::Message &msg);
	void numericReply_474(KIRC::Message &msg);
	void numericReply_475(KIRC::Message &msg);


	void CtcpQuery_action(KIRC::Message &msg);
	void CtcpQuery_clientinfo(KIRC::Message &msg);
	void CtcpQuery_finger(KIRC::Message &msg);
	void CtcpQuery_dcc(KIRC::Message &msg);
	void CtcpQuery_ping(KIRC::Message &msg);
	void CtcpQuery_source(KIRC::Message &msg);
	void CtcpQuery_time(KIRC::Message &msg);
	void CtcpQuery_userinfo(KIRC::Message &msg);
	void CtcpQuery_version(KIRC::Message &msg);

	void CtcpReply_errmsg(KIRC::Message &msg);
	void CtcpReply_ping(KIRC::Message &msg);
	void CtcpReply_version(KIRC::Message &msg);

private:
	void bindCommands();
	void bindNumericReplies();
	void bindCtcp();

	void setStatus(KIRC::Engine::Status status);
	bool invokeCtcpCommandOfMessage(const TQDict<KIRC::MessageRedirector> &map, KIRC::Message &message);

	/*
	 * Methods that handles all the bindings creations.
	 * This methods is used by all the bind(...) methods.
	 */
	bool _bind(TQDict<KIRC::MessageRedirector> &dict,
		TQString command, TQObject *object, const char *member,
		int minArgs, int maxArgs, const TQString &helpMessage);

	//Static regexes
	static const TQRegExp m_RemoveLinefeeds;

	KIRC::Engine::Status m_status;
	TQString m_Host;
	TQ_UINT16 m_Port;

//	TQUrl serverURL;
//	TQUrl currentServerURL;
	TQString m_Nickname;
	TQString m_Username;
	TQString m_realName;
	TQString m_Passwd;
	bool m_ReqsPasswd;
	bool m_FailedNickOnLogin;
	bool m_useSSL;

	TQValueList<KIRC::Entity *> m_entities;
	KIRC::EntityPtr m_server;
	KIRC::EntityPtr m_self;

	TQString m_VersionString;
	TQString m_UserString;
	TQString m_SourceString;
	TQString m_PendingNick;

	TQDict<KIRC::MessageRedirector> m_commands;
//	TQIntDict<KIRC::MessageRedirector> m_numericCommands;
	TQDict<KIRC::MessageRedirector> m_ctcpQueries;
	TQDict<KIRC::MessageRedirector> m_ctcpReplies;

	TQMap<TQString, TQString> customCtcpMap;
	TQDict<TQTextCodec> codecs;
	TQTextCodec *defaultCodec;

	KExtendedSocket *m_sock;
};

}

#endif
