#ifndef _stream_h__
#define _stream_h__

/** stream **
 *
 * Portable functions for sending and receiving data streams over TCP sockets.
 * 
 * This module makes use of the `socket` and `buffer` modules.
 * 
 * The benefits of using this module over the standard C functions are:
 * 
 *   - portable error checking compatible with non-blocking sockets
 *   - use of dynamically-sized Buffer to handle arbitrarily large data streams
 *   - high-level processing of received data is decoupled from low-level I/O operations
 * 
 */

#ifdef _WIN32
#include <basetsd.h>
typedef SSIZE_T ssize_t;
#else
#include <unistd.h>
#endif

#include "socket.h"
#include "buffer.h"

#ifdef __cplusplus
extern "C" {
#endif


/**
 * Stream Data Handler function to be invoked as a callback by stream_receive().
 * 
 * This function is invoked as a callback after receiving data from a stream.
 * 
 * Arguments:
 * 
 *   data: pointer to the start of the received data as a char array
 *   len:  length of the data array, always greater than zero
 *   user_data: arbitrary data passed back from stream_receive()
 * 
 * Return: number of bytes processed, or negative integer to indicate an error
 * 
 *   Zero indicates that there is not enough data to be processed. It instructs the
 *   stream_receive() function to return immediately with success, retaining the partial
 *   data stored in the buffer.
 * 
 *   A positive integer N corresponds to the number of bytes that were processed by this
 *   function. The buffer cursor is advanced by N bytes. The next call to this function will
 *   point to the next segment of data, i.e. N bytes are consumed from the stream.
 * 
 *   A negative integer indicates that an application error occurred during processing.
 *   This instructs the stream_receive() function to return immediately with an error.
 */
typedef ssize_t (*stream_data_handler_fn)(char *data, size_t len, void *user_data);

/**
 * Receive data over a stream socket (can be non-blocking) and pass the received data to
 * the given handler function for processing.
 * 
 * Arguments:
 * 
 *   sock: descriptor of a stream socket (can be non-blocking)
 *   buf:  Buffer struct inside which to store the received data
 *   handler: callback function to invoke after receiving data
 *   user_data: arbitrary data to pass back to the callback function
 * 
 * Return: number of bytes received (negative if an error occurred)
 * 
 *   Zero indicates that the socket is non-blocking and no data is available yet.
 * 
 *   A negative integer indicates an error.
 * 
 *   A positive integer N indicates that N bytes were read from the socket. It does NOT
 *   refer to the number of bytes processed by the handler function. The handler may have
 *   chosen to process any number M of bytes where M <= N. Any leftover data not consumed
 *   by the handler remains in the Buffer for later processing.
 */
ssize_t stream_receive(socket_t sock, Buffer *buf, stream_data_handler_fn handler, void *user_data);

/**
 * Send data over a stream socket (can be non-blocking).
 * 
 * Arguments:
 * 
 *   sock: descriptor of a stream socket (can be non-blocking)
 *   buf: Buffer containing the data to be sent
 * 
 * Return: number of bytes sent (negative if an error occurred)
 * 
 *   Zero is returned when the socket is non-blocking. The operation will complete later.
 *   The Buffer cursor does not move.
 * 
 *   A negative integer indicates an error. The Buffer cursor does not move.
 * 
 *   A positive integer N indicates that N bytes were sent. The Buffer cursor skips ahead
 *   by N bytes.
 */
ssize_t stream_send(socket_t sock, Buffer *buf);


#ifdef __cplusplus
}
#endif

#endif
