/*
 * Copyright (C) 2010  Barracuda Networks, Inc.
 *
 * 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 WITHANY 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, see <https://www.gnu.org/licenses/>.
 *
 */

#ifndef TURNCLIENT_H
#define TURNCLIENT_H

#include <QByteArray>
#include <QHostAddress>
#include <QObject>
#include <QString>

#include "transportaddress.h"

namespace QCA {
class SecureArray;
}

namespace XMPP {
class StunAllocate;
class StunTransactionPool;

class TurnClient : public QObject {
    Q_OBJECT

public:
    enum Error {
        ErrorGeneric,
        ErrorHostNotFound,
        ErrorConnect,

        // stream error or stream unexpectedly disconnected by peer
        ErrorStream,

        ErrorProxyConnect,
        ErrorProxyNeg,
        ErrorProxyAuth,
        ErrorTls,
        ErrorAuth,
        ErrorRejected,
        ErrorProtocol,
        ErrorCapacity,

        // according to the TURN spec, a client should try three times
        //   to correct a mismatch error before giving up.  this class
        //   will perform the retries internally, and ErrorMismatch is
        //   only emitted when it has given up.  note that if this
        //   happens, the TURN spec says you should not connect to the
        //   TURN server again for at least 2 minutes.
        // note: in UDP mode, this class does not perform retries and
        //   will emit this error immediately.
        ErrorMismatch
    };

    enum Mode { PlainMode, TlsMode };

    enum DebugLevel { DL_None, DL_Info, DL_Packet };

    // adapted from XMPP::AdvancedConnector
    class Proxy {
    public:
        enum { None, HttpConnect, Socks };

        Proxy();
        ~Proxy();

        int     type() const;
        QString host() const;
        quint16 port() const;
        QString user() const;
        QString pass() const;

        void setHttpConnect(const QString &host, quint16 port);
        void setSocks(const QString &host, quint16 port);
        void setUserPass(const QString &user, const QString &pass);

    private:
        int     t;
        QString v_host;
        quint16 v_port = 0;
        QString v_user, v_pass;
    };

    TurnClient(QObject *parent = nullptr);
    ~TurnClient();

    void setProxy(const Proxy &proxy);
    void setClientSoftwareNameAndVersion(const QString &str);

    // for UDP.  does not take ownership of the pool.  stun transaction
    //   I/O occurs through the pool.  transfer of data packets occurs
    //   via processIncomingDatagram(), outgoingDatagram(), and
    //   outgoingDatagramsWritten().  authentication happens through the
    //   pool and not through this class.  the turn addr/port is optional,
    //   and used only for addr association with the pool
    void connectToHost(StunTransactionPool *pool, const TransportAddress &addr = TransportAddress());

    // for TCP and TCP-TLS
    void connectToHost(const TransportAddress &addr, Mode mode = PlainMode);

    const TransportAddress &serverAddress() const;

    // for UDP, use this function to process incoming packets instead of
    //   read().
    QByteArray processIncomingDatagram(const QByteArray &buf, bool notStun, TransportAddress &addr);

    // call after writing datagrams from outgoingDatagram.  not DOR-DS safe
    void outgoingDatagramsWritten(int count);

    QString realm() const;
    void    setUsername(const QString &username);
    void    setPassword(const QCA::SecureArray &password);
    void    setRealm(const QString &realm);
    void    continueAfterParams(const TransportAddress &addr);

    void close();

    StunAllocate *stunAllocate();

    void addChannelPeer(const TransportAddress &addr);

    int packetsToRead() const;
    int packetsToWrite() const;

    // TCP mode only
    QByteArray read(TransportAddress &addr);

    // for UDP, this call may emit outgoingDatagram() immediately (not
    //   DOR-DS safe)
    void write(const QByteArray &buf, const TransportAddress &addr);

    QString errorString() const;

    void setDebugLevel(DebugLevel level); // default DL_None
    void changeThread(QThread *thread);

signals:
    void connected(); // tcp connected
    void tlsHandshaken();
    void closed();
    void needAuthParams(const TransportAddress &addr);
    void retrying();  // mismatch error received, starting all over
    void activated(); // ready for read/write

    // TCP mode only
    void readyRead();

    void packetsWritten(int count, const TransportAddress &addr);
    void error(XMPP::TurnClient::Error e);

    // data packets to be sent to the TURN server, UDP mode only
    void outgoingDatagram(const QByteArray &buf);

    // not DOR-SS/DS safe
    void debugLine(const QString &line);

private:
    class Private;
    friend class Private;
    Private *d;
};
} // namespace XMPP

#endif // TURNCLIENT_H
