/*
 * Copyright (c) 2012-2014, Thingsquare, http://www.thingsquare.com/.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef TCP_SOCKET_H
#define TCP_SOCKET_H

#include "uip.h"

struct tcp_socket;

typedef enum {
  TCP_SOCKET_CONNECTED,
  TCP_SOCKET_CLOSED,
  TCP_SOCKET_TIMEDOUT,
  TCP_SOCKET_ABORTED,
  TCP_SOCKET_DATA_SENT
} tcp_socket_event_t;

/**
 * \brief      TCP data callback function
 * \param s    A pointer to a TCP socket
 * \param ptr  A user-defined pointer
 * \param input_data_ptr A pointer to the incoming data
 * \param input_data_len The length of the incoming data
 * \return     The function should return the number of bytes to leave in the input buffer
 *
 *             The TCP socket input callback function gets
 *             called whenever there is new data on the socket. The
 *             function can choose to either consume the data
 *             directly, or leave it in the buffer for later. The
 *             function must return the amount of data to leave in the
 *             buffer. I.e., if the callback function consumes all
 *             incoming data, it should return 0.
 */
typedef int (* tcp_socket_data_callback_t)(struct tcp_socket *s,
                                           void *ptr,
                                           const uint8_t *input_data_ptr,
                                           int input_data_len);


/**
 * \brief      TCP event callback function
 * \param s    A pointer to a TCP socket
 * \param ptr  A user-defined pointer
 * \param event The event number
 *
 *             The TCP socket event callback function gets
 *             called whenever there is an event on a socket, such as
 *             the socket getting connected or closed.
 */
typedef void (* tcp_socket_event_callback_t)(struct tcp_socket *s,
                                             void *ptr,
                                             tcp_socket_event_t event);

struct tcp_socket {
  struct tcp_socket *next;

  tcp_socket_data_callback_t input_callback;
  tcp_socket_event_callback_t event_callback;
  void *ptr;

  struct process *p;

  uint8_t *input_data_ptr;
  uint8_t *output_data_ptr;

  uint16_t input_data_maxlen;
  uint16_t input_data_len;
  uint16_t output_data_maxlen;
  uint16_t output_data_len;
  uint16_t output_data_send_nxt;
  uint16_t output_senddata_len;
  uint16_t output_data_max_seg;

  uint8_t flags;
  uint16_t listen_port;
  struct uip_conn *c;
};

enum {
  TCP_SOCKET_FLAGS_NONE      = 0x00,
  TCP_SOCKET_FLAGS_LISTENING = 0x01,
  TCP_SOCKET_FLAGS_CLOSING   = 0x02,
};

/**
 * \brief      Register a TCP socket
 * \param s    A pointer to a TCP socket
 * \param ptr  A user-defined pointer that will be sent to callbacks for this socket
 * \param input_databuf A pointer to a memory area this socket will use for input data
 * \param input_databuf_len The size of the input data buffer
 * \param output_databuf A pointer to a memory area this socket will use for outgoing data
 * \param output_databuf_len The size of the output data buffer
 * \param data_callback A pointer to the data callback function for this socket
 * \param event_callback A pointer to the event callback function for this socket
 * \retval -1  If an error occurs
 * \retval 1   If the operation succeeds.
 *
 *             This function registers a TCP socket. The function sets
 *             up the output and input buffers for the socket and
 *             callback pointers.
 *
 *             TCP sockets use input and output buffers for incoming
 *             and outgoing data. The memory for these buffers must be
 *             allocated by the caller. The size of the buffers
 *             determine the amount of data that can be received and
 *             sent, and the principle is that the application that
 *             sets up the TCP socket will know roughly how large
 *             these buffers should be. The rule of thumb is that the
 *             input buffer should be large enough to hold the largest
 *             application layer message that the application will
 *             receive and the output buffer should be large enough to
 *             hold the largest application layer message the
 *             application will send.
 *
 *             TCP throttles incoming data so that if the input buffer
 *             is filled, the connection will halt until the
 *             application has read out the data from the input
 *             buffer.
 *
 */
int tcp_socket_register(struct tcp_socket *s, void *ptr,
                         uint8_t *input_databuf, int input_databuf_len,
                         uint8_t *output_databuf, int output_databuf_len,
                         tcp_socket_data_callback_t data_callback,
                         tcp_socket_event_callback_t event_callback);

/**
 * \brief      Connect a TCP socket to a remote host
 * \param s    A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
 * \param ipaddr The IP address of the remote host
 * \param port The TCP port number, in host byte order, of the remote host
 * \retval -1  If an error occurs
 * \retval 1   If the operation succeeds.
 *
 *             This function connects a TCP socket to a remote host.
 *
 *             When the socket has connected, the event callback will
 *             get called with the TCP_SOCKET_CONNECTED event. If the
 *             remote host does not accept the connection, the
 *             TCP_SOCKET_ABORTED will be sent to the callback. If the
 *             connection times out before conecting to the remote
 *             host, the TCP_SOCKET_TIMEDOUT event is sent to the
 *             callback.
 *
 */
int tcp_socket_connect(struct tcp_socket *s,
                       const uip_ipaddr_t *ipaddr,
                       uint16_t port);

/**
 * \brief      Start listening on a specific port
 * \param s    A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
 * \param port The TCP port number, in host byte order, of the remote host
 * \retval -1  If an error occurs
 * \retval 1   If the operation succeeds.
 *
 *             This function causes the TCP socket to start listening
 *             on the given TCP port.
 *
 *             Several sockets can listen on the same port. If a
 *             remote host connects to the port, one of the listening
 *             sockets will get connected and the event callback will
 *             be called with the TCP_SOCKET_CONNECTED event. When the
 *             connection closes, the socket will go back to listening
 *             for new connections.
 *
 */
int tcp_socket_listen(struct tcp_socket *s,
                      uint16_t port);

/**
 * \brief      Stop listening for new connections
 * \param s    A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
 * \retval -1  If an error occurs
 * \retval 1   If the operation succeeds.
 *
 *             This function causes a listening TCP socket to stop
 *             listen. The socket must previously been put into listen
 *             mode with tcp_socket_listen().
 *
 */
int tcp_socket_unlisten(struct tcp_socket *s);

/**
 * \brief      Send data on a connected TCP socket
 * \param s    A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
 * \param dataptr A pointer to the data to be sent
 * \param datalen The length of the data to be sent
 * \retval -1  If an error occurs
 * \return     The number of bytes that were successfully sent
 *
 *             This function sends data over a connected TCP
 *             socket. The data is placed in the output buffer and
 *             sent to the remote host as soon as possiblce. When the
 *             data has been acknowledged by the remote host, the
 *             event callback is sent with the TCP_SOCKET_DATA_SENT
 *             event.
 */
int tcp_socket_send(struct tcp_socket *s,
                    const uint8_t *dataptr,
                    int datalen);

/**
 * \brief      Send a string on a connected TCP socket
 * \param s    A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
 * \param strptr A pointer to the string to be sent
 * \retval -1  If an error occurs
 * \return     The number of bytes that were successfully sent
 *
 *             This is a convenience function for sending strings on a
 *             TCP socket. The function calls tcp_socket_send() to
 *             send the string.
 */
int tcp_socket_send_str(struct tcp_socket *s,
                        const char *strptr);

/**
 * \brief      Close a connected TCP socket
 * \param s    A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
 * \retval -1  If an error occurs
 * \retval 1   If the operation succeeds.
 *
 *             This function closes a connected TCP socket. When the
 *             socket has been successfully closed, the event callback
 *             is called with the TCP_SOCKET_CLOSED event.
 *
 */
int tcp_socket_close(struct tcp_socket *s);

/**
 * \brief      Unregister a registered socket
 * \param s    A pointer to a TCP socket that must have been previously registered with tcp_socket_register()
 * \retval -1  If an error occurs
 * \retval 1   If the operation succeeds.
 *
 *             This function unregisters a previously registered
 *             socket. This must be done if the process will be
 *             unloaded from memory. If the TCP socket is connected,
 *             the connection will be reset.
 *
 */
int tcp_socket_unregister(struct tcp_socket *s);

/**
 * \brief      The maximum amount of data that could currently be sent
 * \param s    A pointer to a TCP socket
 * \return     The number of bytes available in the output buffer
 *
 *             This function queries the TCP socket and returns the
 *             number of bytes available in the output buffer. This
 *             function is used before calling tcp_socket_send() to
 *             ensure that one application level message can be held
 *             in the output buffer.
 *
 */
int tcp_socket_max_sendlen(struct tcp_socket *s);

#endif /* TCP_SOCKET_H */