/*  -*- 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 KSOCKETDEVICE_H
#define KSOCKETDEVICE_H

#include <QSocketNotifier>
#include "k3socketbase.h"

namespace KNetwork
{

class KSocketDevice;
class KSocketDeviceFactoryBase;

class KSocketDevicePrivate;
/** @class KSocketDevice k3socketdevice.h k3socketdevice.h
 *  @brief Low-level socket functionality.
 *
 * This class provides low-level socket functionality.
 *
 * Most users will prefer "cooked" interfaces like those of KStreamSocket or
 * KServerSocket.
 *
 * Descended classes from this one provide some other kinds of socket functionality,
 * like proxying or specific socket types.
 *
 * @author Thiago Macieira <thiago@kde.org>
 * @deprecated Use KSocketFactory or KLocalSocket instead
 */
class KDELIBS4SUPPORT_DEPRECATED_EXPORT KSocketDevice: public KActiveSocketBase, public KPassiveSocketBase
{
public:
    /**
     * Capabilities for the socket implementation.
     *
     * KSocketDevice-derived classes can implement certain capabilities that are not
     * available in the default class. These capabilities are described by these flags.
     * The default KSocketDevice class has none of these capabilities.
     *
     * For the negative capabilities (inabilities, the CanNot* forms), when a capability
     * is not present, the implementation will default to the original behaviour.
     */
    enum Capabilities {
        /** Can connect to hostnames.
         *  If this flag is present, the string form of connect() can be used. */
        CanConnectString = 0x01,

        /** Can bind to hostnames.
         *  If this flag is present, the string form of bind() can be used */
        CanBindString = 0x02,

        /** Can not bind.
         *  If this flag is present, this implementation cannot bind */
        CanNotBind = 0x04,

        /** Can not listen.
         *  If this flag is present, this implementation cannot listen */
        CanNotListen = 0x08,

        /**
         * Can send multicast as well as join/leave multicast groups.
         */
        CanMulticast = 0x10,

        /**
         * Can not use datagrams.
         * Note that this implies multicast capability not being available either.
         */
        CanNotUseDatagrams = 0x20
    };
protected:
    /// The socket file descriptor. It is used throughout the implementation
    /// and subclasses.
    int m_sockfd;

public:
    /**
     * Default constructor.
     *
     * The parameter is used to specify which socket this object is used as
     * a device for.
     */
    KDELIBS4SUPPORT_DEPRECATED explicit KSocketDevice(const KSocketBase * = nullptr, QObject *objparent = nullptr);

    /**
     * Constructs a new object around an already-open socket.
     *
     * Note: you should write programs that create sockets through
     * the classes whenever possible.
     */
    KDELIBS4SUPPORT_DEPRECATED explicit KSocketDevice(int fd, OpenMode mode = ReadWrite);

    /**
     * QObject constructor
     */
    KSocketDevice(QObject *parent);

    /**
     * Destructor. This closes the socket if it's open.
     */
    virtual ~KSocketDevice();

    /**
     * Returns the file descriptor for this socket.
     */
    int socket() const;

    /**
     * Returns the set of capabilities this socket class implements.
     * The set of capabilities is defined as an OR-ed mask of
     * Capabilities bits.
     *
     * The default implementation is guaranteed to always return 0. That
     * is, derived implementations always return bits where they differ
     * from the system standard sockets.
     */
    virtual int capabilities() const;

    /**
     * This implementation sets the options on the socket.
     */
    bool setSocketOptions(int opts) override;

    /**
     * Closes the socket. Reimplemented from QIODevice.
     *
     * Use this function to close the socket this object is holding open.
     */
    void close() override;

    /**
     * This call is not supported on sockets. Reimplemented from QIODevice.
     */
    virtual bool flush();

    /**
     * Creates a socket but don't connect or bind anywhere.
     * This function is the equivalent of the system call socket(2).
     */
    virtual bool create(int family, int type, int protocol);

    /**
     * @overload
     * Creates a socket but don't connect or bind anywhere.
     */
    bool create(const KResolverEntry &address);

    /**
     * Binds this socket to the given address.
     */
    bool bind(const KResolverEntry &address) override;

    /**
     * Puts this socket into listening mode.
     */
    bool listen(int backlog = 5) override; // 5 is arbitrary

    /**
     * Connect to a remote host.
     */
    virtual bool connect(const KResolverEntry &address,
                         OpenMode mode = ReadWrite) override;

    /**
     * Accepts a new incoming connection.
     * Note: this function returns a socket of type KSocketDevice.
     */
    KSocketDevice *accept() override;

    /**
     * Disconnects this socket.
     */
    bool disconnect() override;

    /**
     * Returns the number of bytes available for reading without blocking.
     */
    qint64 bytesAvailable() const override;

    /**
     * Waits up to @p msecs for more data to be available on this socket.
     *
     * This function is a wrapper against poll(). This function will wait
     * for any read events.
     */
    qint64 waitForMore(int msecs, bool *timeout = nullptr) override;

    /**
     * Returns this socket's local address.
     */
    KSocketAddress localAddress() const override;

    /**
     * Returns this socket's peer address. If this implementation does proxying
     * of some sort, this is the real external address, not the proxy's address.
     */
    KSocketAddress peerAddress() const override;

    /**
     * Returns this socket's externally visible local address.
     *
     * If this socket has a local address visible externally different
     * from the normal local address (as returned by localAddress()), then
     * return it.
     *
     * Certain implementations will use proxies and thus have externally visible
     * addresses different from the local socket values. The default implementation
     * returns the same value as localAddress().
     *
     * @note This function may return an empty KSocketAddress. In that case, the
     *       externally visible address could/can not be determined.
     */
    KSocketAddress externalAddress() const override;

    /**
     * Returns a socket notifier for input on this socket.
     * The notifier is created only when requested. Whether
     * it is enabled or not depends on the implementation.
     *
     * This function might return NULL.
     */
    QSocketNotifier *readNotifier() const;

    /**
     * Returns a socket notifier for output on this socket.
     * The is created only when requested.
     *
     * This function might return NULL.
     */
    QSocketNotifier *writeNotifier() const;

    /**
     * Returns a socket notifier for exceptional events on this socket.
     * The is created only when requested.
     *
     * This function might return NULL.
     */
    QSocketNotifier *exceptionNotifier() const;

    /**
     * Reads data and the source address from this socket.
     */
    qint64 readData(char *data, qint64 maxlen, KSocketAddress *from = nullptr) override;

    /**
     * Peeks the data in the socket and the source address.
     */
    qint64 peekData(char *data, qint64 maxlen, KSocketAddress *from = nullptr) override;

    /**
     * Writes the given data to the given destination address.
     */
    virtual qint64 writeData(const char *data, qint64 len,
                             const KSocketAddress *to = nullptr) override;

    /**
     * Executes a poll in the socket, via select(2) or poll(2).
     * The events polled are returned in the parameters pointers.
     * Set any of them to NULL to disable polling of that event.
     *
     * On exit, @p input, @p output and @p exception will contain
     * true if an event of that kind is waiting on the socket or false
     * if not. If a timeout occurred, set @p timedout to true (all other
     * parameters are necessarily set to false).
     *
     * @param input   if set, turns on polling for input events
     * @param output  if set, turns on polling for output events
     * @param exception   if set, turns on polling for exceptional events
     * @param timeout the time in milliseconds to wait for an event;
     *            0 for no wait and any negative value to wait forever
     * @param timedout    on exit, will contain true if the polling timed out
     * @return true if the poll call succeeded and false if an error occurred
     */
    virtual bool poll(bool *input, bool *output, bool *exception = nullptr,
                      int timeout = -1, bool *timedout = nullptr);

    /**
     * Shorter version to poll for any events in a socket. This call
     * polls for input, output and exceptional events in a socket but
     * does not return their states. This is useful if you need to wait for
     * any event, but don't need to know which; or for timeouts.
     *
     * @param timeout the time in milliseconds to wait for an event;
     *            0 for no wait and any negative value to wait forever
     * @param timedout    on exit, will contain true if the polling timed out
     * @return true if the poll call succeeded and false if an error occurred
     */
    bool poll(int timeout = -1, bool *timedout = nullptr);

protected:
    /**
     * Special constructor. This constructor will cause the internal
     * socket device NOT to be set. Use this if your socket device class
     * takes another underlying socket device.
     *
     * @param parent  the parent, if any
     */
    KDELIBS4SUPPORT_DEPRECATED explicit KSocketDevice(bool, const KSocketBase *parent = nullptr);

    /**
     * Creates a socket notifier of the given type.
     *
     * This function is called by readNotifier(), writeNotifier() and
     * exceptionNotifier() when they need to create a socket notifier
     * (i.e., the first call to those functions after the socket is open).
     * After that call, those functions cache the socket notifier and will
     * not need to call this function again.
     *
     * Reimplement this function in your derived class if your socket type
     * requires a different kind of QSocketNotifier. The return value should
     * be deleteable with delete. (close() deletes them).
     *
     * @param type    the socket notifier type
     */
    virtual QSocketNotifier *createNotifier(QSocketNotifier::Type type) const;

public:
    /**
     * Creates a new default KSocketDevice object given
     * the parent object.
     *
     * The capabilities flag indicates the desired capabilities the object being
     * created should possess. Those capabilities are not guaranteed: if no factory
     * can provide such an object, a default object will be created.
     *
     * @param parent  the KSocketBase parent
     */
    static KSocketDevice *createDefault(KSocketBase *parent);

    /**
     * @overload
     *
     * This will create an object only if the requested capabilities match.
     *
     * @param parent      the parent
     * @param capabilities    the requested capabilities
     */
    static KSocketDevice *createDefault(KSocketBase *parent, int capabilities);

    /**
     * Sets the default KSocketDevice implementation to use and
     * return the old factory.
     *
     * @param factory the factory object for the implementation
     */
    static KSocketDeviceFactoryBase *setDefaultImpl(KSocketDeviceFactoryBase *factory);

    /**
     * Adds a factory of KSocketDevice objects to the list, along with its
     * capabilities flag.
     */
    static void addNewImpl(KSocketDeviceFactoryBase *factory, int capabilities);

private:
    KSocketDevice(const KSocketDevice &);
    KSocketDevice &operator=(const KSocketDevice &);

    KSocketDevicePrivate *const d;
};

/** @internal
 * This class provides functionality for creating and registering
 *  socket implementations.
 */
class KSocketDeviceFactoryBase
{
public:
    KSocketDeviceFactoryBase() {}
    virtual ~KSocketDeviceFactoryBase() {}

    virtual KSocketDevice *create(KSocketBase *) const = 0;
};

/**
 * This class provides functionality for creating and registering
 * socket implementations.
 */
template<class Impl>
class KSocketDeviceFactory: public KSocketDeviceFactoryBase
{
public:
    KSocketDeviceFactory() {}
    virtual ~KSocketDeviceFactory() {}

    /** Create the socket implementation.
        @param parent Parent socket for this implementation
        @todo Who owns the parent afterwards?
    */
    KSocketDevice *create(KSocketBase *parent) const override
    {
        return new Impl(parent);
    }
};

}               // namespaces

#endif
