/*  -*- C++ -*-
 *  Copyright (C) 2003,2005 Thiago Macieira <thiago@kde.org>
 *
 *
 *  Permission is hereby granted, free of charge, to any person obtaining
 *  a copy of this software and associated documentation files (the
 *  "Software"), to deal in the Software without restriction, including
 *  without limitation the rights to use, copy, modify, merge, publish,
 *  distribute, sublicense, and/or sell copies of the Software, and to
 *  permit persons to whom the Software is furnished to do so, subject to
 *  the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included
 *  in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef KSTREAMSOCKET_H
#define KSTREAMSOCKET_H

#include <QString>

#include "k3clientsocketbase.h"

/** A namespace to store all networking-related (socket) classes. */
namespace KNetwork
{

class KResolverEntry;
class KServerSocket;
class KBufferedSocket;

class KStreamSocketPrivate;
/** @class KStreamSocket k3streamsocket.h k3streamsocket.h
 *  @brief Simple stream socket
 *
 * This class provides functionality to creating unbuffered, stream
 * sockets. In the case of Internet (IP) sockets, this class creates and
 * uses TCP/IP sockets.
 *
 * Objects of this class start, by default, on non-blocking mode. Call
 * setBlocking if you wish to change that.
 *
 * KStreamSocket objects are thread-safe and can be used in auxiliary
 * threads (i.e., not the thread in which the Qt event loop runs in).
 * Note that KBufferedSocket cannot be used reliably in an auxiliary thread.
 *
 * Sample usage:
 * \code
 *   QByteArray httpGet(const QString& hostname)
 *   {
 *     KStreamSocket socket(hostname, "http");
 *     if (!socket.connect())
 *       return QByteArray();
 *     QByteArray data = socket.readAll();
 *     return data;
 *   }
 * \endcode
 *
 * Here's another sample, showing asynchronous operation:
 * \code
 *  DataRetriever::DataRetriever(const QString& hostname, const QString& port)
 *    : socket(hostname, port)
 *  {
 *    // connect signals to our slots
 *    QObject::connect(&socket, SIGNAL(connected(const KNetwork::KResolverEntry&)),
 *                     this, SLOT(slotSocketConnected()));
 *    QObject::connect(&socket, SIGNAL(gotError(int)),
 *                     this, SLOT(slotSocketError(int)));
 *    QObject::connect(&socket, SIGNAL(readyRead()),
 *                     this, SLOT(slotSocketReadyToRead()));
 *    QObject::connect(&socket, SIGNAL(readyWrite()),
 *                     this, SLOT(slotSocketReadyToWrite()));
 *
 *    // set non-blocking mode in order to work asynchronously
 *    socket.setBlocking(false);
 *
 *    // turn on signal emission
 *    socket.enableRead(true);
 *    socket.enableWrite(true);
 *
 *    // start connecting
 *    socket.connect();
 *  }
 * \endcode
 *
 * @see KNetwork::KBufferedSocket, KNetwork::KServerSocket
 * @author Thiago Macieira <thiago@kde.org>
 * @version 0.9
 * @deprecated Use KSocketFactory or KLocalSocket instead
 */
class KDELIBS4SUPPORT_DEPRECATED_EXPORT KStreamSocket: public KClientSocketBase
{
    Q_OBJECT

public:
    /**
     * Default constructor.
     *
     * @param node    destination host
     * @param service destination service to connect to
     * @param parent  the parent QObject object
     */
    KDELIBS4SUPPORT_DEPRECATED explicit KStreamSocket(const QString &node = QString(), const QString &service = QString(),
                           QObject *parent = nullptr);

    /**
     * Destructor. This closes the socket.
     */
    ~KStreamSocket() override;

    /**
     * Retrieves the timeout value (in milliseconds).
     */
    int timeout() const;

    /**
     * Retrieves the remaining timeout time (in milliseconds). This value
     * equals timeout() if there's no connection in progress.
     */
    int remainingTimeout() const;

    /**
     * Sets the timeout value. Setting this value while a connection attempt
     * is in progress will reset the timer.
     *
     * Please note that the timeout value is valid for the connection attempt
     * only. No other operations are timed against this value -- including the
     * name lookup associated.
     *
     * @param msecs       the timeout value in milliseconds
     */
    void setTimeout(int msecs);

    /**
     * Binds this socket to the given nodename and service,
     * or use the default ones if none are given. In order to bind to a service
     * and allow the operating system to choose the interface, set @p node to
     * QString().
     *
     * Reimplemented from KClientSocketBase.
     *
     * Upon successful binding, the bound() signal will be
     * emitted. If an error is found, the gotError()
     * signal will be emitted.
     *
     * @note Due to the internals of the name lookup and binding
     *       mechanism, some (if not most) implementations of this function
     *       do not actually bind the socket until the connection
     *       is requested (see connect()). They only set the values
     *       for future reference.
     *
     * This function returns true on success.
     *
     * @param node    the nodename
     * @param service the service
     */
    bool bind(const QString &node = QString(),
                      const QString &service = QString()) override;

    /**
     * Reimplemented from KClientSocketBase. Connect this socket to this
     * specific address.
     *
     * Unlike bind(const QString&, const QString&) above, this function
     * really does bind the socket. No lookup is performed. The bound()
     * signal will be emitted.
     */
    bool bind(const KResolverEntry &entry) override;

    /**
     * Reimplemented from KClientSocketBase.
     *
     * Attempts to connect to the these hostname and service,
     * or use the default ones if none are given. If a connection attempt
     * is already in progress, check on its state and set the error status
     * (NoError, meaning the connection is completed, or InProgress).
     *
     * If the blocking mode for this object is on, this function will only
     * return when all the resolved peer addresses have been tried or when
     * a connection is established.
     *
     * Upon successfully connecting, the connected() signal
     * will be emitted. If an error is found, the gotError()
     * signal will be emitted.
     *
     * This function also implements timeout handling.
     *
     * @param node    the remote node to connect to
     * @param service the service on the remote node to connect to
     * @param mode        mode to operate this socket in
     */
    bool connect(const QString &node = QString(),
                         const QString &service = QString(),
                         OpenMode mode = ReadWrite) override;

    /**
     * Unshadowing from KClientSocketBase.
     */
    bool connect(const KResolverEntry &entry,
                         OpenMode mode = ReadWrite) override;

Q_SIGNALS:
    /**
     * This signal is emitted when a connection timeout occurs.
     */
    void timedOut();

private Q_SLOTS:
    void hostFoundSlot();
    void connectionEvent();
    void timeoutSlot();

private:
    /**
     * @internal
     * If the user requested local bind before connection, bind the socket to one
     * suitable address and return true. Also sets d->local to the address used.
     *
     * Return false in case of error.
     */
    bool bindLocallyFor(const KResolverEntry &peer);

    /**
     * @internal
     * Finishes the connection process by setting internal values and
     * emitting the proper signals.
     *
     * Note: assumes d->local iterator points to the address that we bound
     * to.
     */
    void connectionSucceeded(const KResolverEntry &peer);

    KStreamSocket(const KStreamSocket &);
    KStreamSocket &operator=(const KStreamSocket &);

    KStreamSocketPrivate *const d;

    friend class KServerSocket;
    friend class KBufferedSocket;
};

}               // namespace KNetwork

#endif
