/*
 *  Copyright (c) 2022, The OpenThread Authors.
 *  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.
 */

/**
 * @file
 * @brief
 *   This file defines extensions to the OpenThread TCP API.
 *
 */

#ifndef OPENTHREAD_TCP_EXT_H_
#define OPENTHREAD_TCP_EXT_H_

#include <openthread/tcp.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @addtogroup api-tcp-ext
 *
 * @brief
 *   This module includes easy-to-use abstractions on top of the base TCP API.
 *
 * @{
 *
 */

/**
 * Represents a circular send buffer for use with a TCP endpoint.
 *
 * Using a circular send buffer is optional. Applications can use a TCP
 * endpoint to send data by managing otLinkedBuffers directly. However, some
 * applications may find it more convenient to have a circular send buffer;
 * such applications can call otTcpCircularSendBufferWrite() to "attach" a
 * circular send buffer to a TCP endpoint and send out data on that TCP
 * endpoint, relying on the circular send buffer to manage the underlying
 * otLinkedBuffers.
 *
 * otTcpCircularSendBuffer is implemented on top of the otLinkedBuffer-based
 * API provided by an otTcpEndpoint. Once attached to an otTcpEndpoint, an
 * otTcpCircularSendBuffer performs all the work of managing otLinkedBuffers
 * for the connection. This means that, once an otTcpCircularSendBuffer is
 * attached to an otTcpEndpoint, the application should not call
 * otTcpSendByReference() or otTcpSendByExtension() on that otTcpEndpoint.
 * Instead, the application should use otTcpCircularSendBufferWrite() to add
 * data to the send buffer.
 *
 * The otTcpForwardProgress() callback is the intended way for users to learn
 * when space becomes available in the circular send buffer. On an
 * otTcpEndpoint to which an otTcpCircularSendBuffer is attached, the
 * application MUST install an otTcpForwardProgress() callback and call
 * otTcpCircularSendBufferHandleForwardProgress() on the attached
 * otTcpCircularSendBuffer at the start of the callback function. It is
 * recommended that the user NOT install an otTcpSendDone() callback, as all
 * management of otLinkedBuffers is handled by the circular send buffer.
 *
 * The application should not inspect the fields of this structure directly; it
 * should only interact with it via the TCP Circular Send Buffer API functions
 * whose signature are provided in this file.
 *
 */
typedef struct otTcpCircularSendBuffer
{
    uint8_t *mDataBuffer;   ///< Pointer to data in the circular send buffer
    size_t   mCapacity;     ///< Length of the circular send buffer
    size_t   mStartIndex;   ///< Index of the first valid byte in the send buffer
    size_t   mCapacityUsed; ///< Number of bytes stored in the send buffer

    otLinkedBuffer mSendLinks[2];
    uint8_t        mFirstSendLinkIndex;
} otTcpCircularSendBuffer;

/**
 * Initializes a TCP circular send buffer.
 *
 * @param[in]  aSendBuffer      A pointer to the TCP circular send buffer to initialize.
 * @param[in]  aDataBuffer      A pointer to memory to use to store data in the TCP circular send buffer.
 * @param[in]  aCapacity        The capacity, in bytes, of the TCP circular send buffer, which must equal the size of
 *                              the memory pointed to by @p aDataBuffer .
 */
void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, void *aDataBuffer, size_t aCapacity);

/**
 * Defines flags passed to @p otTcpCircularSendBufferWrite.
 *
 */
enum
{
    OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME = 1 << 0,
};

/**
 * Sends out data on a TCP endpoint, using the provided TCP circular send
 * buffer to manage buffering.
 *
 * Once this function is called, @p aSendBuffer and @p aEndpoint are considered
 * "attached" to each other. While they are attached, ALL send operations for
 * @p aEndpoint must be made using @p aSendBuffer and ALL operations on
 * @p aSendBuffer must be associated with @p aEndpoint .
 *
 * The only way to "detach" a TCP circular send buffer and a TCP endpoint is to
 * wait for the send buffer to become completely empty. This can happen in two
 * ways: (1) all data in the send buffer is sent and acknowledged in the normal
 * course of TCP protocol operation, or (2) the connection is terminated.
 *
 * The recommended usage pattern is to use a single TCP circular send buffer
 * with a TCP endpoint, and to send data on that TCP endpoint only via its
 * associated TCP circular buffer. This recommended usage pattern sidesteps the
 * issues described above by always using a TCP endpoint and TCP circular send
 * buffer together.
 *
 * If the circular send buffer reaches capacity, only a prefix of the provided
 * data is copied into the circular send buffer.
 *
 * @param[in]   aEndpoint    The TCP endpoint on which to send out data.
 * @param[in]   aSendBuffer  The TCP circular send buffer into which to copy data.
 * @param[in]   aData        A pointer to data to copy into the TCP circular send buffer.
 * @param[in]   aLength      The length of the data pointed to by @p aData to copy into the TCP circular send buffer.
 * @param[out]  aWritten     Populated with the amount of data copied into the send buffer, which might be less than
 *                           @p aLength if the send buffer reaches capacity.
 * @param[in]   aFlags       Flags specifying options for this operation (see enumeration above).
 *
 * @retval OT_ERROR_NONE    Successfully copied data into the send buffer and sent it on the TCP endpoint.
 * @retval OT_ERROR_FAILED  Failed to send out data on the TCP endpoint.
 */
otError otTcpCircularSendBufferWrite(otTcpEndpoint           *aEndpoint,
                                     otTcpCircularSendBuffer *aSendBuffer,
                                     const void              *aData,
                                     size_t                   aLength,
                                     size_t                  *aWritten,
                                     uint32_t                 aFlags);

/**
 * Performs circular-send-buffer-specific handling in the otTcpForwardProgress
 * callback.
 *
 * The application is expected to install an otTcpForwardProgress() callback on
 * the otTcpEndpoint, and call this function at the start of the callback
 * function for circular-send-buffer-specific processing.
 *
 * In the callback function, the application can determine the amount of free
 * space in the circular send buffer by calling
 * otTcpCircularSendBufferFreeSpace(), or by comparing @p aInSendBuffer with
 * the send buffer's capacity, chosen by the user when calling
 * otTcpCircularSendBufferInitialize().
 *
 * @param[in]  aSendBuffer    A pointer to the TCP circular send buffer for the endpoint for which
 *                            otTcpForwardProgress() was invoked.
 * @param[in]  aInSendBuffer  Value of @p aInSendBuffer passed to the otTcpForwardProgress() callback.
 */
void otTcpCircularSendBufferHandleForwardProgress(otTcpCircularSendBuffer *aSendBuffer, size_t aInSendBuffer);

/**
 * Returns the amount of free space in the TCP circular send buffer.
 *
 * This operation will always succeed.
 *
 * @param[in]  aSendBuffer  A pointer to the TCP circular send buffer whose amount of free space to return.
 *
 * @returns The amount of free space in the send buffer.
 */
size_t otTcpCircularSendBufferGetFreeSpace(const otTcpCircularSendBuffer *aSendBuffer);

/**
 * Forcibly discards all data in the circular send buffer.
 *
 * The application is expected to call this function when a TCP connection is
 * terminated unceremoniously (e.g., if the application calls
 * otTcpEndpointAbort() or is informed of a reset connection via the
 * otTcpConnectionLost() callback).
 *
 * Calling this function on a nonempty TCP circular send buffer attached to a
 * TCP endpoint results in undefined behavior.
 *
 * @param[in]  aSendBuffer  The TCP circular send buffer whose data to discard.
 */
void otTcpCircularSendBufferForceDiscardAll(otTcpCircularSendBuffer *aSendBuffer);

/**
 * Deinitializes a TCP circular send buffer, detaching it if attached.
 *
 * If the TCP circular send buffer is not empty, then this operation will fail.
 *
 * @param[in]  aSendBuffer  The TCP circular send buffer to deinitialize.
 *
 * @retval OT_ERROR_NONE    Successfully deinitialize the TCP circular send buffer.
 * @retval OT_ERROR_BUSY    Circular buffer contains data and cannot be deinitialized.
 */
otError otTcpCircularSendBufferDeinitialize(otTcpCircularSendBuffer *aSendBuffer);

/**
 * Context structure to use with mbedtls_ssl_set_bio.
 */
typedef struct otTcpEndpointAndCircularSendBuffer
{
    otTcpEndpoint           *mEndpoint;
    otTcpCircularSendBuffer *mSendBuffer;
} otTcpEndpointAndCircularSendBuffer;

/**
 * Non-blocking send callback to pass to mbedtls_ssl_set_bio.
 *
 * @param[in]  aCtx  A pointer to an otTcpEndpointAndCircularSendBuffer.
 * @param[in]  aBuf  The data to add to the send buffer.
 * @param[in]  aLen  The amount of data to add to the send buffer.
 *
 * @returns The number of bytes sent, or an mbedtls error code.
 */
int otTcpMbedTlsSslSendCallback(void *aCtx, const unsigned char *aBuf, size_t aLen);

/**
 * Non-blocking receive callback to pass to mbedtls_ssl_set_bio.
 *
 * @param[in]   aCtx  A pointer to an otTcpEndpointAndCircularSendBuffer.
 * @param[out]  aBuf  The buffer into which to receive data.
 * @param[in]   aLen  The maximum amount of data that can be received.
 *
 * @returns The number of bytes received, or an mbedtls error code.
 */
int otTcpMbedTlsSslRecvCallback(void *aCtx, unsigned char *aBuf, size_t aLen);

/**
 * @}
 *
 */

#ifdef __cplusplus
} // extern "C"
#endif

#endif // OPENTHREAD_TCP_EXT_H_
