/*
 * client.h - communicates with the Jabber network
 * Copyright (C) 2001, 2002  Justin Karneges
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

 /*
  TODO:

  core:
  - Subscriptions
  - Presence errors
  - Better support for Message errors (like bounces)

  bugs:
  - Set offline presence to LiveRoster and self when going offline

  general:
  - Better take() handlers for iq.  make a iqverify() function (for 'from', 'id', etc)
  - Task timeouts
  - convert remaining tasks to new api
  - separate QSSL plugin handling from Jabber::Stream so that DTCP can use it too

  future:
  - Groupchat
  - Finish/Integrate JidStream

 */
 
#ifndef JABBER_CLIENT_H
#define JABBER_CLIENT_H

#include<qobject.h>
#include<qvaluelist.h>
#include"types.h"

class QString;
class QDomElement;
class QDomDocument;

namespace Jabber
{
	class Client;
	class Stream;
	class StreamError;
	class StreamProxy;
	class LiveRosterItem;
	class LiveRoster;
	class Message;
	class DTCPManager;
	class DTCPConnection;
	class JidStreamManager;

	class Task : public QObject
	{
		Q_OBJECT
	public:
		enum { ErrDisc };
		Task(Task *parent);
		Task(Client *, bool isRoot);
		virtual ~Task();

		Task *parent() const;
		Client *client() const;
		QDomDocument *doc() const;
		QString id() const;

		bool success() const;
		int statusCode() const;
		const QString & statusString() const;

		void go(bool autoDelete=false);
		virtual bool take(const QDomElement &);
		void safeDelete();

	signals:
		void finished();

	protected:
		virtual void onGo();
		virtual void onDisconnect();
		void send(const QDomElement &);
		void setSuccess(int code=0, const QString &str="");
		void setError(const QDomElement &);
		void setError(int code=0, const QString &str="");
		void debug(const char *, ...);
		void debug(const QString &);

	private slots:
		void clientError(const StreamError &);

	private:
		void init();
		void done();

		class TaskPrivate;
		TaskPrivate *d;
	};

	class Client : public QObject
	{
		Q_OBJECT

	public:
		Client(QObject *parent=0);
		~Client();

		bool isActive() const;
		bool isConnected() const;
		bool isHandshaken() const;
		bool isAuthenticated() const;
		void connectToHost(const QString &host, int port=-1, const QString &virtualHost="");
		void setProxy(const StreamProxy &);
		void close(bool fast=false);

		void setNoopTime(int);

		bool isSSLEnabled() const;
		bool setSSLEnabled(bool);

		Stream & stream();
		const LiveRoster & roster() const;
		const LiveRosterItem & self() const;

		void send(const QDomElement &);
		void send(const QString &);

		QString host() const;
		QString user() const;
		QString pass() const;
		QString resource() const;
		Jid jid() const;

		void authPlain(const QString &user, const QString &pass, const QString &resource);
		void authDigest(const QString &user, const QString &pass, const QString &resource);
		void rosterRequest();
		void sendMessage(const Message &);
		void setPresence(const Status &);

		void debug(const QString &);
		QString genUniqueId();
		Task *rootTask();
		QDomDocument *doc() const;

		QString OSName() const;
		QString timeZone() const;
		int timeZoneOffset() const;

		void setOSName(const QString &);
		void setTimeZone(const QString &, int);

		JidStreamManager *jsManager();
		DTCPManager *dtcpManager();

	signals:
		void connected();
		void handshaken();
		void error(const StreamError &);
		void closeFinished();
		void authFinished(bool, int, const QString &);
		void rosterRequestFinished(bool, int, const QString &);
		void rosterItemAdded(const RosterItem &);
		void rosterItemUpdated(const RosterItem &);
		void rosterItemRemoved(const RosterItem &);
		void resourceAvailable(const Jid &, const Resource &);
		void resourceUnavailable(const Jid &, const Resource &);
		void subscription(const Jid &, const QString &);
		void messageReceived(const Message &);
		void debugText(const QString &);

	public slots:
		void continueAfterWarning();

	private slots:
		void streamConnected();
		void streamHandshaken();
		void streamError(const StreamError &);
		void streamCloseFinished();
		void streamReceivePacket(const QDomElement &);

		void slotAuthFinished();
		void slotRosterRequestFinished();

		// basic daemons
		void ppSubscription(const Jid &, const QString &);
		void ppPresence(const Jid &, const Status &);
		void pmMessage(const Message &);
		void prRoster(const Roster &);

		// dtcp
		void dtcp_incoming(DTCPConnection *);

	private:
		void cleanup();
		void distribute(const QDomElement &);
		void importRoster(const Roster &);
		void importRosterItem(const RosterItem &);
		void updatePresence(LiveRosterItem *, const Jid &, const Status &);

		class ClientPrivate;
		ClientPrivate *d;
	};

	class LiveRosterItem : public RosterItem
	{
	public:
		LiveRosterItem(const Jid &j="");
		LiveRosterItem(const RosterItem &);
		~LiveRosterItem();

		void setRosterItem(const RosterItem &);

		ResourceList & resourceList();
		ResourceList::Iterator priority();

		const ResourceList & resourceList() const;
		ResourceList::ConstIterator priority() const;

		bool isAvailable() const;
		const Status & lastUnavailableStatus() const;
		bool flagForDelete() const;

		void setLastUnavailableStatus(const Status &);
		void setFlagForDelete(bool);

	private:
		ResourceList v_resourceList;
		Status v_lastUnavailableStatus;
		bool v_flagForDelete;

		class LiveRosterItemPrivate;
		LiveRosterItemPrivate *d;
	};

	class LiveRoster : public QValueList<LiveRosterItem>
	{
	public:
		LiveRoster();
		~LiveRoster();

		void flagAllForDelete();
		LiveRoster::Iterator find(const Jid &, bool compareRes=true);
		LiveRoster::ConstIterator find(const Jid &, bool compareRes=true) const;

	private:
		class LiveRosterPrivate;
		LiveRosterPrivate *d;
	};
}

#endif
