/*
 * jidstream.h - establish byte-streams between Jabber IDs
 * 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:

   remove method tag from jidstream tasks
   manager: ask before accepting
   ability to read/write to a jidstream
   canceling a jidstream, and sending appropriate error to the peer
   if a jidstreamhandler fails, then the jidstream should delete it and go idle.

 */
 
#ifndef JABBER_JIDSTREAM_H
#define JABBER_JIDSTREAM_H

#include<qobject.h>
#include<qdom.h>
#include<qstring.h>
#include"jid.h"
#include"client.h"

namespace Jabber
{
	class JidStreamHandler : public QObject
	{
		Q_OBJECT
	public:
		JidStreamHandler(QObject *parent=0);
		virtual ~JidStreamHandler()=0;

		virtual Jid peer() const;
		virtual void close();
		virtual bool isConnected() const;

		virtual void write(const QByteArray &);
		virtual QByteArray read();
		virtual bool canRead() const;

	signals:
		void connected();
		void connectionClosed();
		void delayedCloseFinished();
		void readyRead();
		void bytesWritten(int);
		void error(int);

	private:
		class Private;
		Private *d;
	};

	class JidStream : public QObject
	{
		Q_OBJECT
	public:
		enum { Idle, Negotiating, Establishing, Connected, Closing, Waiting };
		enum { ErrNeg, ErrStream };
		JidStream(Client *client);
		~JidStream();

		bool connectToJid(const Jid &jid, const QString &key, const QString &method="");
		void accept();

		Jid peer() const;
		QString key() const;
		QString method() const;
		int state() const;

	signals:
		void error(int);

	private slots:
		void neg_finished();
		void js_finished();
		void jsh_connectionClosed();

	private:
		class Private;
		Private *d;

		void getMethod();
		void getStream();
		void doEstablish();

		friend class JidStreamManager;
		void wait(const Jid &, const QString &);
		void connectSuccess(JidStreamHandler *);
		void connectError();
		void incomingJSHandler(JidStreamHandler *);
	};

	typedef QPtrList<JidStream> JidStreamList;
	typedef QPtrListIterator<JidStream> JidStreamListIt;
	class JidStreamManager : public QObject
	{
		Q_OBJECT
	public:
		JidStreamManager(Client *);
		~JidStreamManager();

		QStringList supportedMethods() const;

		JidStream *findStream(const Jid &, const QString &) const;
		const JidStreamList *streamList() const;

		void takeIncomingJSHandler(const QString &key, JidStreamHandler *);

	private slots:
		void pjs_incoming(const Jid &from, const QString &id, const QString &method, const QString &key);
		void dtcp_connected();
		void dtcp_error(int);

	private:
		class Private;
		Private *d;

		friend class JidStream;
		void link(JidStream *);
		void unlink(JidStream *);
		void doEstablish(JidStream *);
	};

	class JT_JidStream : public Task
	{
		Q_OBJECT
	public:
		JT_JidStream(Task *);
		~JT_JidStream();

		void set(const Jid &, const QString &, const QString &);

		void onGo();
		bool take(const QDomElement &);

		Jid jid() const;

	private:
		QDomElement iq;
		Jid to;
		QString v_key, v_method;
	};

	class JT_PushJidStream : public Task
	{
		Q_OBJECT
	public:
		JT_PushJidStream(Task *);
		~JT_PushJidStream();

		bool take(const QDomElement &);

		void respondSuccess(const Jid &to, const QString &id);
		void respondError(const Jid &to, const QString &id, int code, const QString &str);

	signals:
		void incoming(const Jid &from, const QString &id, const QString &method, const QString &key);
		void error(const Jid &from, const QString &key, int code, const QString &str);
	};

	class JT_Negotiate : public Task
	{
		Q_OBJECT
	public:
		JT_Negotiate(Task *);
		~JT_Negotiate();

		void get(const Jid &, const QString &, const QStringList &);

		void onGo();
		bool take(const QDomElement &);

		Jid jid() const;
		QString option() const;

	private:
		QDomElement iq;
		Jid to;
		QString v_option;
	};

	class JT_PushNegotiate : public Task
	{
		Q_OBJECT
	public:
		JT_PushNegotiate(Task *);
		~JT_PushNegotiate();

		bool take(const QDomElement &);

	private:
		QString getPreferred(const QString &, const QStringList &);
	};
}

#endif
