/*
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#ifndef __PJ_ASYNCSOCK_H__
#define __PJ_ASYNCSOCK_H__

/**
 * @file activesock.h
 * @brief Active socket
 */

#include <pj/ioqueue.h>
#include <pj/sock.h>

PJ_BEGIN_DECL

/**
 * @defgroup PJ_ACTIVESOCK Active socket I/O
 * @brief Active socket performs active operations on socket.
 * @ingroup PJ_IO
 * @{
 *
 * Active socket is a higher level abstraction to the ioqueue. It provides
 * automation to socket operations which otherwise would have to be done
 * manually by the applications. For example with socket recv(), recvfrom(),
 * and accept() operations, application only needs to invoke these
 * operation once, and it will be notified whenever data or incoming TCP
 * connection (in the case of accept()) arrives.
 */

/**
 * This opaque structure describes the active socket.
 */
typedef struct pj_activesock_t pj_activesock_t;

/**
 * This structure contains the callbacks to be called by the active socket.
 */
typedef struct pj_activesock_cb {
    /**
     * This callback is called when a data arrives as the result of
     * pj_activesock_start_read().
     *
     * @param asock     The active socket.
     * @param data      The buffer containing the new data, if any. If
     *                  the status argument is non-PJ_SUCCESS, this
     *                  argument may be NULL.
     * @param size      The length of data in the buffer.
     * @param status    The status of the read operation. This may contain
     *                  non-PJ_SUCCESS for example when the TCP connection
     *                  has been closed. In this case, the buffer may
     *                  contain left over data from previous callback which
     *                  the application may want to process.
     * @param remainder If application wishes to leave some data in the
     *                  buffer (common for TCP applications), it should
     *                  move the remainder data to the front part of the
     *                  buffer and set the remainder length here. The value
     *                  of this parameter will be ignored for datagram
     *                  sockets.
     *
     * @return          PJ_TRUE if further read is desired, and PJ_FALSE
     *                  when application no longer wants to receive data.
     *                  Application may destroy the active socket in the
     *                  callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_data_read)(pj_activesock_t *asock, void *data, pj_size_t size, pj_status_t status,
                              pj_size_t *remainder);
    /**
     * This callback is called when a packet arrives as the result of
     * pj_activesock_start_recvfrom().
     *
     * @param asock     The active socket.
     * @param data      The buffer containing the packet, if any. If
     *                  the status argument is non-PJ_SUCCESS, this
     *                  argument will be set to NULL.
     * @param size      The length of packet in the buffer. If
     *                  the status argument is non-PJ_SUCCESS, this
     *                  argument will be set to zero.
     * @param src_addr  Source address of the packet.
     * @param addr_len  Length of the source address.
     * @param status    This contains
     *
     * @return          PJ_TRUE if further read is desired, and PJ_FALSE
     *                  when application no longer wants to receive data.
     *                  Application may destroy the active socket in the
     *                  callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_data_recvfrom)(pj_activesock_t *asock, void *data, pj_size_t size, const pj_sockaddr_t *src_addr,
                                  int addr_len, pj_status_t status);

    /**
     * This callback is called when data has been sent.
     *
     * @param asock     The active socket.
     * @param send_key  Key associated with the send operation.
     * @param sent      If value is positive non-zero it indicates the
     *                  number of data sent. When the value is negative,
     *                  it contains the error code which can be retrieved
     *                  by negating the value (i.e. status=-sent).
     *
     * @return          Application may destroy the active socket in the
     *                  callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_data_sent)(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, pj_ssize_t sent);

    /**
     * This callback is called when new connection arrives as the result
     * of pj_activesock_start_accept(). If the status of accept operation is
     * needed use on_accept_complete2 instead of this callback.
     *
     * @param asock     The active socket.
     * @param newsock   The new incoming socket.
     * @param src_addr  The source address of the connection.
     * @param addr_len  Length of the source address.
     *
     * @return          PJ_TRUE if further accept() is desired, and PJ_FALSE
     *                  when application no longer wants to accept incoming
     *                  connection. Application may destroy the active socket
     *                  in the callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_accept_complete)(pj_activesock_t *asock, pj_sock_t newsock, const pj_sockaddr_t *src_addr,
                                    int src_addr_len);

    /**
     * This callback is called when new connection arrives as the result
     * of pj_activesock_start_accept().
     *
     * @param asock     The active socket.
     * @param newsock   The new incoming socket.
     * @param src_addr  The source address of the connection.
     * @param addr_len  Length of the source address.
     * @param status    The status of the accept operation. This may contain
     *                  non-PJ_SUCCESS for example when the TCP listener is in
     *                  bad state for example on iOS platform after the
     *                  application waking up from background.
     *
     * @return          PJ_TRUE if further accept() is desired, and PJ_FALSE
     *                  when application no longer wants to accept incoming
     *                  connection. Application may destroy the active socket
     *                  in the callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_accept_complete2)(pj_activesock_t *asock, pj_sock_t newsock, const pj_sockaddr_t *src_addr,
                                     int src_addr_len, pj_status_t status);

    /**
     * This callback is called when pending connect operation has been
     * completed.
     *
     * @param asock     The active  socket.
     * @param status    The connection result. If connection has been
     *                  successfully established, the status will contain
     *                  PJ_SUCCESS.
     *
     * @return          Application may destroy the active socket in the
     *                  callback and return PJ_FALSE here.
     */
    pj_bool_t (*on_connect_complete)(pj_activesock_t *asock, pj_status_t status);

} pj_activesock_cb;

/**
 * Settings that can be given during active socket creation. Application
 * must initialize this structure with #pj_activesock_cfg_default().
 */
typedef struct pj_activesock_cfg {
    /**
     * Optional group lock to be assigned to the ioqueue key.
     */
    pj_grp_lock_t *grp_lock;

    /**
     * Number of concurrent asynchronous operations that is to be supported
     * by the active socket. This value only affects socket receive and
     * accept operations -- the active socket will issue one or more
     * asynchronous read and accept operations based on the value of this
     * field. Setting this field to more than one will allow more than one
     * incoming data or incoming connections to be processed simultaneously
     * on multiprocessor systems, when the ioqueue is polled by more than
     * one threads.
     *
     * The default value is 1.
     */
    unsigned async_cnt;

    /**
     * The ioqueue concurrency to be forced on the socket when it is
     * registered to the ioqueue. See #pj_ioqueue_set_concurrency() for more
     * info about ioqueue concurrency.
     *
     * When this value is -1, the concurrency setting will not be forced for
     * this socket, and the socket will inherit the concurrency setting of
     * the ioqueue. When this value is zero, the active socket will disable
     * concurrency for the socket. When this value is +1, the active socket
     * will enable concurrency for the socket.
     *
     * The default value is -1.
     */
    int concurrency;

    /**
     * If this option is specified, the active socket will make sure that
     * asynchronous send operation with stream oriented socket will only
     * call the callback after all data has been sent. This means that the
     * active socket will automatically resend the remaining data until
     * all data has been sent.
     *
     * Please note that when this option is specified, it is possible that
     * error is reported after partial data has been sent. Also setting
     * this will disable the ioqueue concurrency for the socket.
     *
     * Default value is 1.
     */
    pj_bool_t whole_data;

} pj_activesock_cfg;

/**
 * Initialize the active socket configuration with the default values.
 *
 * @param cfg           The configuration to be initialized.
 */
PJ_DECL(void) pj_activesock_cfg_default(pj_activesock_cfg *cfg);

/**
 * Create the active socket for the specified socket. This will register
 * the socket to the specified ioqueue.
 *
 * @param pool          Pool to allocate memory from.
 * @param sock          The socket handle.
 * @param sock_type     Specify socket type, either pj_SOCK_DGRAM() or
 *                      pj_SOCK_STREAM(). The active socket needs this
 *                      information to handle connection closure for
 *                      connection oriented sockets.
 * @param ioqueue       The ioqueue to use.
 * @param opt           Optional settings. When this setting is not specifed,
 *                      the default values will be used.
 * @param cb            Pointer to structure containing application
 *                      callbacks.
 * @param user_data     Arbitrary user data to be associated with this
 *                      active socket.
 * @param p_asock       Pointer to receive the active socket instance.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t)
pj_activesock_create(pj_pool_t *pool, pj_sock_t sock, int sock_type, const pj_activesock_cfg *opt,
                     pj_ioqueue_t *ioqueue, const pj_activesock_cb *cb, void *user_data, pj_activesock_t **p_asock);

/**
 * Create UDP socket descriptor, bind it to the specified address, and
 * create the active socket for the socket descriptor.
 *
 * @param pool          Pool to allocate memory from.
 * @param addr          Specifies the address family of the socket and the
 *                      address where the socket should be bound to. If
 *                      this argument is NULL, then AF_INET is assumed and
 *                      the socket will be bound to any addresses and port.
 * @param ioqueue       The ioqueue.
 * @param opt           Optional settings. When this setting is not specifed,
 *                      the default values will be used.
 * @param cb            Pointer to structure containing application
 *                      callbacks.
 * @param user_data     Arbitrary user data to be associated with this
 *                      active socket.
 * @param p_asock       Pointer to receive the active socket instance.
 * @param bound_addr    If this argument is specified, it will be filled with
 *                      the bound address on return.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t)
pj_activesock_create_udp(pj_pool_t *pool, const pj_sockaddr *addr, const pj_activesock_cfg *opt, pj_ioqueue_t *ioqueue,
                         const pj_activesock_cb *cb, void *user_data, pj_activesock_t **p_asock,
                         pj_sockaddr *bound_addr);

/**
 * Close the active socket. This will unregister the socket from the
 * ioqueue and ultimately close the socket.
 *
 * @param asock     The active socket.
 *
 * @return          PJ_SUCCESS if the operation has been successful,
 *                  or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_activesock_close(pj_activesock_t *asock);

#if (defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT != 0) || defined(DOXYGEN)
/**
 * Set iPhone OS background mode setting. Setting to 1 will enable TCP
 * active socket to receive incoming data when application is in the
 * background. Setting to 0 will disable it. Default value of this
 * setting is PJ_ACTIVESOCK_TCP_IPHONE_OS_BG.
 *
 * This API is only available if PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT
 * is set to non-zero.
 *
 * @param asock     The active socket.
 * @param val       The value of background mode setting.
 *
 */
PJ_DECL(void) pj_activesock_set_iphone_os_bg(pj_activesock_t *asock, int val);

/**
 * Enable/disable support for iPhone OS background mode. This setting
 * will apply globally and will affect any active sockets created
 * afterwards, if you want to change the setting for a particular
 * active socket, use #pj_activesock_set_iphone_os_bg() instead.
 * By default, this setting is enabled.
 *
 * This API is only available if PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT
 * is set to non-zero.
 *
 * @param val       The value of global background mode setting.
 *
 */
PJ_DECL(void) pj_activesock_enable_iphone_os_bg(pj_bool_t val);
#endif

/**
 * Associate arbitrary data with the active socket. Application may
 * inspect this data in the callbacks and associate it with higher
 * level processing.
 *
 * @param asock     The active socket.
 * @param user_data The user data to be associated with the active
 *                  socket.
 *
 * @return          PJ_SUCCESS if the operation has been successful,
 *                  or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_activesock_set_user_data(pj_activesock_t *asock, void *user_data);

/**
 * Retrieve the user data previously associated with this active
 * socket.
 *
 * @param asock     The active socket.
 *
 * @return          The user data.
 */
PJ_DECL(void *) pj_activesock_get_user_data(pj_activesock_t *asock);

/**
 * Starts read operation on this active socket. This function will create
 * \a async_cnt number of buffers (the \a async_cnt parameter was given
 * in \a pj_activesock_create() function) where each buffer is \a buff_size
 * long. The buffers are allocated from the specified \a pool. Once the
 * buffers are created, it then issues \a async_cnt number of asynchronous
 * \a recv() operations to the socket and returns back to caller. Incoming
 * data on the socket will be reported back to application via the
 * \a on_data_read() callback.
 *
 * Application only needs to call this function once to initiate read
 * operations. Further read operations will be done automatically by the
 * active socket when \a on_data_read() callback returns non-zero.
 *
 * @param asock     The active socket.
 * @param pool      Pool used to allocate buffers for incoming data.
 * @param buff_size The size of each buffer, in bytes.
 * @param flags     Flags to be given to pj_ioqueue_recv().
 *
 * @return          PJ_SUCCESS if the operation has been successful,
 *                  or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t)
pj_activesock_start_read(pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags);

/**
 * Same as #pj_activesock_start_read(), except that the application
 * supplies the buffers for the read operation so that the acive socket
 * does not have to allocate the buffers.
 *
 * @param asock     The active socket.
 * @param pool      Pool used to allocate buffers for incoming data.
 * @param buff_size The size of each buffer, in bytes.
 * @param readbuf   Array of packet buffers, each has buff_size size.
 * @param flags     Flags to be given to pj_ioqueue_recv().
 *
 * @return          PJ_SUCCESS if the operation has been successful,
 *                  or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t)
pj_activesock_start_read2(pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, void *readbuf[],
                          pj_uint32_t flags);

/**
 * Same as pj_activesock_start_read(), except that this function is used
 * only for datagram sockets, and it will trigger \a on_data_recvfrom()
 * callback instead.
 *
 * @param asock     The active socket.
 * @param pool      Pool used to allocate buffers for incoming data.
 * @param buff_size The size of each buffer, in bytes.
 * @param flags     Flags to be given to pj_ioqueue_recvfrom().
 *
 * @return          PJ_SUCCESS if the operation has been successful,
 *                  or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t)
pj_activesock_start_recvfrom(pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, pj_uint32_t flags);

/**
 * Same as #pj_activesock_start_recvfrom() except that the recvfrom()
 * operation takes the buffer from the argument rather than creating
 * new ones.
 *
 * @param asock     The active socket.
 * @param pool      Pool used to allocate buffers for incoming data.
 * @param buff_size The size of each buffer, in bytes.
 * @param readbuf   Array of packet buffers, each has buff_size size.
 * @param flags     Flags to be given to pj_ioqueue_recvfrom().
 *
 * @return          PJ_SUCCESS if the operation has been successful,
 *                  or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t)
pj_activesock_start_recvfrom2(pj_activesock_t *asock, pj_pool_t *pool, unsigned buff_size, void *readbuf[],
                              pj_uint32_t flags);

/**
 * Send data using the socket.
 *
 * @param asock     The active socket.
 * @param send_key  The operation key to send the data, which is useful
 *                  if application wants to submit multiple pending
 *                  send operations and want to track which exact data
 *                  has been sent in the \a on_data_sent() callback.
 * @param data      The data to be sent. This data must remain valid
 *                  until the data has been sent.
 * @param size      The size of the data.
 * @param flags     Flags to be given to pj_ioqueue_send().
 *
 *
 * @return          PJ_SUCCESS if data has been sent immediately, or
 *                  PJ_EPENDING if data cannot be sent immediately. In
 *                  this case the \a on_data_sent() callback will be
 *                  called when data is actually sent. Any other return
 *                  value indicates error condition.
 */
PJ_DECL(pj_status_t)
pj_activesock_send(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size,
                   unsigned flags);

/**
 * Send datagram using the socket.
 *
 * @param asock     The active socket.
 * @param send_key  The operation key to send the data, which is useful
 *                  if application wants to submit multiple pending
 *                  send operations and want to track which exact data
 *                  has been sent in the \a on_data_sent() callback.
 * @param data      The data to be sent. This data must remain valid
 *                  until the data has been sent.
 * @param size      The size of the data.
 * @param flags     Flags to be given to pj_ioqueue_send().
 * @param addr      The destination address.
 * @param addr_len  The length of the address.
 *
 * @return          PJ_SUCCESS if data has been sent immediately, or
 *                  PJ_EPENDING if data cannot be sent immediately. In
 *                  this case the \a on_data_sent() callback will be
 *                  called when data is actually sent. Any other return
 *                  value indicates error condition.
 */
PJ_DECL(pj_status_t)
pj_activesock_sendto(pj_activesock_t *asock, pj_ioqueue_op_key_t *send_key, const void *data, pj_ssize_t *size,
                     unsigned flags, const pj_sockaddr_t *addr, int addr_len);

#if PJ_HAS_TCP
/**
 * Starts asynchronous socket accept() operations on this active socket.
 * Application must bind the socket before calling this function. This
 * function will issue \a async_cnt number of asynchronous \a accept()
 * operations to the socket and returns back to caller. Incoming
 * connection on the socket will be reported back to application via the
 * \a on_accept_complete() callback.
 *
 * Application only needs to call this function once to initiate accept()
 * operations. Further accept() operations will be done automatically by
 * the active socket when \a on_accept_complete() callback returns non-zero.
 *
 * @param asock     The active socket.
 * @param pool      Pool used to allocate some internal data for the
 *                  operation.
 *
 * @return          PJ_SUCCESS if the operation has been successful,
 *                  or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_activesock_start_accept(pj_activesock_t *asock, pj_pool_t *pool);

/**
 * Starts asynchronous socket connect() operation for this socket. Once
 * the connection is done (either successfully or not), the
 * \a on_connect_complete() callback will be called.
 *
 * @param asock     The active socket.
 * @param pool      The pool to allocate some internal data for the
 *                  operation.
 * @param remaddr   Remote address.
 * @param addr_len  Length of the remote address.
 *
 * @return          PJ_SUCCESS if connection can be established immediately,
 *                  or PJ_EPENDING if connection cannot be established
 *                  immediately. In this case the \a on_connect_complete()
 *                  callback will be called when connection is complete.
 *                  Any other return value indicates error condition.
 */
PJ_DECL(pj_status_t)
pj_activesock_start_connect(pj_activesock_t *asock, pj_pool_t *pool, const pj_sockaddr_t *remaddr, int addr_len);

#endif /* PJ_HAS_TCP */

/**
 * @}
 */

PJ_END_DECL

#endif /* __PJ_ASYNCSOCK_H__ */
