/*
 *
 *    Copyright (c) 2013-2017 Nest Labs, Inc.
 *    All rights reserved.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 *    @file
 *      This header file defines the <tt>nl::Inet::TCPEndPoint</tt>
 *      class, where the Nest Inet Layer encapsulates methods for
 *      interacting with TCP transport endpoints (SOCK_DGRAM sockets
 *      on Linux and BSD-derived systems) or LwIP TCP protocol
 *      control blocks, as the system is configured accordingly.
 */

#ifndef TCPENDPOINT_H
#define TCPENDPOINT_H

#include <InetLayer/EndPointBasis.h>
#include <InetLayer/IPAddress.h>

#include <SystemLayer/SystemPacketBuffer.h>

#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#include <netinet/tcp.h>
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS

namespace nl {
namespace Inet {

class InetLayer;

#if INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED

#if (!defined(TCP_REPAIR) || !defined(TCP_REPAIR_QUEUE) || !defined(TCP_REPAIR_OPTIONS) || \
     !defined(TCPI_OPT_SACK) || !defined(TCPI_OPT_WSCALE) || !defined(TCPI_OPT_TIMESTAMPS) || \
     !defined(TCPOPT_MAXSEG) || !defined(TCP_REPAIR_WINDOW))
#error "INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED set but platform does not support TCP REPAIR"
#endif

typedef struct TCPConnRepairInfo
{
    IPAddress srcIP;                     // Source IP address
    IPAddress dstIP;                     // Destination IP address
    IPAddressType addrType;              // Address family type
    uint16_t srcPort;                    // Source port
    uint16_t dstPort;                    // Destination port
    uint32_t txSeq;                      // Transmit sequence
    uint32_t rxSeq;                      // Receive sequence
    uint32_t sndWl1;                     // Segment seq number for last window update
    uint32_t sndWnd;                     // Send window
    uint32_t maxWindow;                  // Max window
    uint32_t rcvWnd;                     // Receive window
    uint32_t rcvWup;                     // Last ack number that was sent/
    uint32_t tsVal;                      // TCP Timestamp
    uint32_t tsecr;
    uint16_t mss;                        // Max segment size
    uint8_t  sndWscale;                  // Send window scale
    uint8_t  rcvWscale;                  // Receive window scale
    uint8_t  tcpOptions;                 // TCP options

    bool IsValid (void) const;

    void Dump (void) const;

} TCPConnRepairInfo;
#endif // INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED

/**
 * @brief   Objects of this class represent TCP transport endpoints.
 *
 * @details
 *  Nest Inet Layer encapsulates methods for interacting with TCP transport
 *  endpoints (SOCK_STREAM sockets on Linux and BSD-derived systems) or LwIP
 *  TCP protocol control blocks, as the system is configured accordingly.
 */
class NL_DLL_EXPORT TCPEndPoint : public EndPointBasis
{
    friend class InetLayer;

public:
    /** Control switch indicating whether the application is receiving data. */
    bool ReceiveEnabled;

    /**
     * @brief   Basic dynamic state of the underlying endpoint.
     *
     * @details
     *  Objects are initialized in the "ready" state, proceed to subsequent
     *  states corresponding to a simplification of the states of the TCP
     *  transport state machine.
     *
     * @note
     *  The \c kBasisState_Closed state enumeration is mapped to \c kState_Ready for historical binary-compatibility reasons. The
     *  existing \c kState_Closed exists to identify separately the distinction between "not opened yet" and "previously opened now
     *  closed" that existed previously in the \c kState_Ready and \c kState_Closed states.
     */
    enum {
        kState_Ready            = kBasisState_Closed,   /**< Endpoint initialized, but not bound. */
        kState_Bound            = 1,                    /**< Endpoint bound, but not listening. */
        kState_Listening        = 2,                    /**< Endpoint receiving connections. */
        kState_Connecting       = 3,                    /**< Endpoint attempting to connect. */
        kState_Connected        = 4,                    /**< Endpoint connected, ready for tx/rx. */
        kState_SendShutdown     = 5,                    /**< Endpoint initiated its half-close. */
        kState_ReceiveShutdown  = 6,                    /**< Endpoint responded to half-close. */
        kState_Closing          = 7,                    /**< Endpoint closing bidirectionally. */
        kState_Closed           = 8                     /**< Endpoint closed, ready for release. */
    } State;

    /**
     * @brief   Bind the endpoint to an interface IP address.
     *
     * @param[in]   addrType    the protocol version of the IP address
     * @param[in]   addr        the IP address (must be an interface address)
     * @param[in]   port        the TCP port
     * @param[in]   reuseAddr   option to share binding with other endpoints
     *
     * @retval  INET_NO_ERROR               success: endpoint bound to address
     * @retval  INET_ERROR_INCORRECT_STATE  endpoint has been bound previously
     * @retval  INET_NO_MEMORY              insufficient memory for endpoint
     *
     * @retval  INET_ERROR_WRONG_PROTOCOL_TYPE
     *      \c addrType does not match \c IPVer.
     *
     * @retval  INET_ERROR_WRONG_ADDRESS_TYPE
     *      \c addrType is \c kIPAddressType_Any, or the type of \c addr is not
     *      equal to \c addrType.
     *
     * @retval  other                   another system or platform error
     *
     * @details
     *  Binds the endpoint to the specified network interface IP address.
     *
     *  On LwIP, this method must not be called with the LwIP stack lock
     *  already acquired.
     */
    INET_ERROR Bind(IPAddressType addrType, IPAddress addr, uint16_t port, bool reuseAddr = false);

    /**
     * @brief   Prepare the endpoint to receive TCP messages.
     *
     * @param[in]   backlog     maximum depth of connection acceptance queue
     *
     * @retval  INET_NO_ERROR   success: endpoint ready to receive messages.
     * @retval  INET_ERROR_INCORRECT_STATE  endpoint is already listening.
     *
     * @details
     *  If \c State is already \c kState_Listening, then no operation is
     *  performed, otherwise the \c State is set to \c kState_Listening and
     *  the endpoint is prepared to received TCP messages, according to the
     *  semantics of the platform.
     *
     *  On some platforms, the \c backlog argument is not used (the depth of
     *  the queue is fixed; only one connection may be accepted at a time).
     *
     *  On LwIP systems, this method must not be called with the LwIP stack
     *  lock already acquired
     */
    INET_ERROR Listen(uint16_t backlog);

    /**
     * @brief   Initiate a TCP connection.
     *
     * @param[in]   addr        the destination IP address
     * @param[in]   port        the destination TCP port
     * @param[in]   intf        an optional network interface indicator
     *
     * @retval  INET_NO_ERROR       success: \c msg is queued for transmit.
     * @retval  INET_ERROR_NOT_IMPLEMENTED  system implementation not complete.
     *
     * @retval  INET_ERROR_WRONG_ADDRESS_TYPE
     *      the destination address and the bound interface address do not
     *      have matching protocol versions or address type, or the destination
     *      address is an IPv6 link-local address and \c intf is not specified.
     *
     * @retval  other                   another system or platform error
     *
     * @details
     *      If possible, then this method initiates a TCP connection to the
     *      destination \c addr (with \c intf used as the scope
     *      identifier for IPv6 link-local destinations) and \c port.
     */
    INET_ERROR Connect(IPAddress addr, uint16_t port, InterfaceId intf = INET_NULL_INTERFACEID);

    /**
     * @brief   Extract IP address and TCP port of remote endpoint.
     *
     * @param[out]  retAddr     IP address of remote endpoint.
     * @param[out]  retPort     TCP port of remote endpoint.
     *
     * @retval  INET_NO_ERROR           success: address and port extracted.
     * @retval  INET_ERROR_INCORRECT_STATE  TCP connection not established.
     * @retval  INET_ERROR_CONNECTION_ABORTED   TCP connection no longer open.
     *
     * @details
     *  Do not use \c NULL pointer values for either argument.
     */
    INET_ERROR GetPeerInfo(IPAddress *retAddr, uint16_t *retPort) const;

    /**
     * @brief   Extract IP address and TCP port of local endpoint.
     *
     * @param[out]  retAddr     IP address of local endpoint.
     * @param[out]  retPort     TCP port of local endpoint.
     *
     * @retval  INET_NO_ERROR           success: address and port extracted.
     * @retval  INET_ERROR_INCORRECT_STATE  TCP connection not established.
     * @retval  INET_ERROR_CONNECTION_ABORTED   TCP connection no longer open.
     *
     * @details
     *  Do not use \c NULL pointer values for either argument.
     */
    INET_ERROR GetLocalInfo(IPAddress *retAddr, uint16_t *retPort);

    /**
     * @brief   Send message text on TCP connection.
     *
     * @param[out]  data    Message text to send.
     * @param[out]  push    If \c true, then send immediately, otherwise queue.
     *
     * @retval  INET_NO_ERROR           success: address and port extracted.
     * @retval  INET_ERROR_INCORRECT_STATE  TCP connection not established.
     *
     * @details
     *  The <tt>Weave::System::PacketBuffer::Free</tt> method is called on the \c data argument
     *  regardless of whether the transmission is successful or failed.
     */
    INET_ERROR Send(Weave::System::PacketBuffer *data, bool push = true);

    /**
     * @brief   Disable reception.
     *
     * @details
     *  Disable all event handlers. Data sent to an endpoint that disables
     *  reception will be acknowledged until the receive window is exhausted.
     */
    void DisableReceive(void);

    /**
     * @brief   Enable reception.
     *
     * @details
     *  Enable all event handlers. Data sent to an endpoint that disables
     *  reception will be acknowledged until the receive window is exhausted.
     */
    void EnableReceive(void);

    /**
     *  @brief EnableNoDelay
     */
    INET_ERROR EnableNoDelay(void);

    /**
     * @brief   Enable the TCP "keep-alive" option.
     *
     * @param[in]   interval        time in seconds between probe requests.
     * @param[in]   timeoutCount    number of probes to send before timeout.
     *
     * @retval  INET_NO_ERROR           success: address and port extracted.
     * @retval  INET_ERROR_INCORRECT_STATE  TCP connection not established.
     * @retval  INET_ERROR_CONNECTION_ABORTED   TCP connection no longer open.
     * @retval  INET_ERROR_NOT_IMPLEMENTED  system implementation not complete.
     *
     * @retval  other                   another system or platform error
     *
     * @details
     *  Start automatically  transmitting TCP "keep-alive" probe segments every
     *  \c interval seconds. The connection will abort automatically after
     *  receiving a negative response, or after sending \c timeoutCount
     *  probe segments without receiving a positive response.
     *
     *  See RFC 1122, section 4.2.3.6 for specification details.
     */
    INET_ERROR EnableKeepAlive(uint16_t interval, uint16_t timeoutCount);

    /**
     * @brief   Disable the TCP "keep-alive" option.
     *
     * @retval  INET_NO_ERROR           success: address and port extracted.
     * @retval  INET_ERROR_INCORRECT_STATE  TCP connection not established.
     * @retval  INET_ERROR_CONNECTION_ABORTED   TCP connection no longer open.
     * @retval  INET_ERROR_NOT_IMPLEMENTED  system implementation not complete.
     *
     * @retval  other                   another system or platform error
     */
    INET_ERROR DisableKeepAlive(void);

    /**
     * @brief   Set the TCP TCP_USER_TIMEOUT socket option.
     *
     * @param[in]   userTimeoutMillis    Tcp user timeout value in milliseconds.
     *
     * @retval  INET_NO_ERROR           success: address and port extracted.
     * @retval  INET_ERROR_NOT_IMPLEMENTED  system implementation not complete.
     *
     * @retval  other                   another system or platform error
     *
     * @details
     *  When the value is greater than 0, it specifies the maximum amount of
     *  time in milliseconds that transmitted data may remain
     *  unacknowledged before TCP will forcibly close the
     *  corresponding connection. If the option value is specified as 0,
     *  TCP will to use the system default.
     *  See RFC 5482, for further details.
     */
    INET_ERROR SetUserTimeout(uint32_t userTimeoutMillis);

    /**
     * @brief   Acknowledge receipt of message text.
     *
     * @param[in]   len     number of bytes to acknowledge.
     *
     * @retval  INET_NO_ERROR           success: reception acknowledged.
     * @retval  INET_ERROR_INCORRECT_STATE  TCP connection not established.
     * @retval  INET_ERROR_CONNECTION_ABORTED   TCP connection no longer open.
     *
     * @details
     *  Use this method to acknowledge reception of all or part of the data
     *  received. The operational semantics are undefined if \c len is larger
     *  than the total outstanding unacknowledged received data.
     */
    INET_ERROR AckReceive(uint16_t len);

    /**
     * @brief   Push message text back to the head of the receive queue.
     *
     * @param[out]  data    Message text to push.
     *
     * @retval  INET_NO_ERROR           success: reception acknowledged.
     * @retval  INET_ERROR_INCORRECT_STATE  TCP connection not established.
     *
     * @details
     *  This method may only be called by data reception event handlers to
     *  put an unacknowledged portion of data back on the receive queue. The
     *  operational semantics are undefined if the caller is outside the scope
     *  of a data reception event handler, \c data is not the \c Weave::System::PacketBuffer
     *  provided to the handler, or \c data does not contain the unacknowledged
     *  portion remaining after the bytes acknowledged by a prior call to the
     *  <tt>AckReceive(uint16_t len)</tt> method.
     */
    INET_ERROR PutBackReceivedData(Weave::System::PacketBuffer *data);

    /**
     * @brief   Extract the length of the data awaiting first transmit.
     *
     * @return  Number of untransmitted bytes in the transmit queue.
     */
    uint32_t PendingSendLength(void);

    /**
     * @brief   Extract the length of the unacknowledged receive data.
     *
     * @return  Number of bytes in the receive queue that have not yet been
     *      acknowledged with <tt>AckReceive(uint16_t len)</tt>.
     */
    uint32_t PendingReceiveLength(void);

    /**
     * @brief   Initiate TCP half close, in other words, finished with sending.
     *
     * @retval  INET_NO_ERROR           success: address and port extracted.
     * @retval  INET_ERROR_INCORRECT_STATE  TCP connection not established.
     *
     * @retval  other                   another system or platform error
     */
    INET_ERROR Shutdown(void);

    /**
     * @brief   Initiate TCP full close, in other words, finished with both send and
     *  receive.
     *
     * @retval  INET_NO_ERROR           success: address and port extracted.
     * @retval  INET_ERROR_INCORRECT_STATE  TCP connection not established.
     *
     * @retval  other                   another system or platform error
     */
    INET_ERROR Close(void);

    /**
     * @brief   Abortively close the endpoint, in other words, send RST packets.
     */
    void Abort(void);

    /**
     * @brief   Initiate (or continue) TCP full close, ignoring errors.
     *
     * @details
     *  The object is returned to the free pool, and all remaining user
     *  references are subsequently invalid.
     */
    void Free(void);

    /**
     * @brief   Extract whether TCP connection is established.
     */
    bool IsConnected(void) const;

    void SetConnectTimeout(const uint32_t connTimeoutMsecs);

#if INET_TCP_IDLE_CHECK_INTERVAL > 0
    /**
     * @brief   Set timer event for idle activity.
     *
     * @param[in]   timeoutMS
     *
     * @details
     *  Set the idle timer interval to \c timeoutMS milliseconds. A zero
     *  time interval implies the idle timer is disabled.
     */
    void SetIdleTimeout(uint32_t timeoutMS);
#endif // INET_TCP_IDLE_CHECK_INTERVAL > 0

    /**
     * @brief   Note activity, in other words, reset the idle timer.
     *
     * @details
     *  Reset the idle timer to zero.
     */
    void MarkActive(void);

    /**
     * @brief   Obtain an identifier for the endpoint.
     *
     * @return  Returns an opaque unique identifier for use logs.
     */
    uint16_t LogId(void);

#if INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED
    INET_ERROR RepairConnection(const TCPConnRepairInfo &connRepairInfo, InterfaceId intf);
#endif // INET_CONFIG_TCP_CONN_REPAIR_SUPPORTED


    /**
     * @brief   Type of connection establishment event handling function.
     *
     * @param[in]   endPoint    The TCP endpoint associated with the event.
     * @param[in]   err         \c INET_NO_ERROR if success, else another code.
     *
     * @details
     *  Provide a function of this type to the \c OnConnectComplete delegate
     *  member to process connection establishment events on \c endPoint. The
     *  \c err argument distinguishes successful connections from failures.
     */
    typedef void (*OnConnectCompleteFunct)(TCPEndPoint *endPoint, INET_ERROR err);

    /**
     * The endpoint's connection establishment event handling function
     * delegate.
     */
    OnConnectCompleteFunct OnConnectComplete;

    /**
     * @brief   Type of data reception event handling function.
     *
     * @param[in]   endPoint    The TCP endpoint associated with the event.
     * @param[in]   data        The data received.
     *
     * @details
     *  Provide a function of this type to the \c OnDataReceived delegate
     *  member to process data reception events on \c endPoint where \c data
     *  is the message text received.
     *
     *  A data reception event handler must acknowledge data processed using
     *  the \c AckReceive method. The \c Free method on the data buffer must
     *  also be invoked unless the \c PutBackReceivedData is used instead.
     */
    typedef void (*OnDataReceivedFunct)(TCPEndPoint *endPoint, Weave::System::PacketBuffer *data);

    /**
     * The endpoint's message text reception event handling function delegate.
     */
    OnDataReceivedFunct OnDataReceived;

    /**
     * @brief   Type of data transmission event handling function.
     *
     * @param[in]   endPoint    The TCP endpoint associated with the event.
     * @param[in]   len         Number of bytes added to the transmit window.
     *
     * @details
     *  Provide a function of this type to the \c OnDataSent delegate
     *  member to process data transmission events on \c endPoint where \c len
     *  is the length of the message text added to the TCP transmit window,
     *  which are eligible for sending by the underlying network stack.
     */
    typedef void (*OnDataSentFunct)(TCPEndPoint *endPoint, uint16_t len);

    /**
     * The endpoint's message text transmission event handling function
     * delegate.
     */
    OnDataSentFunct OnDataSent;

    /**
     * @brief   Type of connection establishment event handling function.
     *
     * @param[in]   endPoint    The TCP endpoint associated with the event.
     * @param[in]   err         \c INET_NO_ERROR if success, else another code.
     *
     * @details
     *  Provide a function of this type to the \c OnConnectionClosed delegate
     *  member to process connection termination events on \c endPoint. The
     *  \c err argument distinguishes successful terminations from failures.
     */
    typedef void (*OnConnectionClosedFunct)(TCPEndPoint *endPoint, INET_ERROR err);

    /** The endpoint's close event handling function delegate. */
    OnConnectionClosedFunct OnConnectionClosed;

    /**
     * @brief   Type of half-close reception event handling function.
     *
     * @param[in]   endPoint    The TCP endpoint associated with the event.
     *
     * @details
     *  Provide a function of this type to the \c OnPeerClose delegate member
     *  to process connection termination events on \c endPoint.
     */
    typedef void (*OnPeerCloseFunct)(TCPEndPoint *endPoint);

    /** The endpoint's half-close receive event handling function delegate. */
    OnPeerCloseFunct OnPeerClose;

    /**
     * @brief   Type of connection received event handling function.
     *
     * @param[in]   listeningEndPoint   The listening TCP endpoint.
     * @param[in]   conEndPoint         The newly received TCP endpoint.
     * @param[in]   peerAddr            The IP address of the remote peer.
     * @param[in]   peerPort            The TCP port of the remote peer.
     *
     * @details
     *  Provide a function of this type to the \c OnConnectionReceived delegate
     *  member to process connection reception events on \c listeningEndPoint.
     *  The newly received endpoint \c conEndPoint is located at IP address
     *  \c peerAddr and TCP port \c peerPort.
     */
    typedef void (*OnConnectionReceivedFunct)(TCPEndPoint *listeningEndPoint, TCPEndPoint *conEndPoint,
            const IPAddress &peerAddr, uint16_t peerPort);

    /** The endpoint's connection receive event handling function delegate. */
    OnConnectionReceivedFunct OnConnectionReceived;

    /**
     * @brief   Type of connection acceptance error event handling function.
     *
     * @param[in]   endPoint    The TCP endpoint associated with the event.
     * @param[in]   err         The reason for the error.
     *
     * @details
     *  Provide a function of this type to the \c OnAcceptError delegate
     *  member to process connection acceptance error events on \c endPoint. The
     *  \c err argument provides specific detail about the type of the error.
     */
    typedef void (*OnAcceptErrorFunct)(TCPEndPoint *endPoint, INET_ERROR err);

    /**
     * The endpoint's connection acceptance event handling function delegate.
     */
    OnAcceptErrorFunct OnAcceptError;

#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS
    /**
     * @brief   Type of TCP SendIdle changed signal handling function.
     *
     * @param[in]   endPoint    The TCP endpoint associated with the event.
     *
     * @param[in]   isIdle      True if the send channel of the TCP endpoint
     *                          is Idle, otherwise false.
     * @details
     *  Provide a function of this type to the \c OnTCPSendIdleChanged delegate
     *  member to process the event of the send channel of the TCPEndPoint
     *  changing state between being idle and not idle.
     */
    typedef void (*OnTCPSendIdleChangedFunct)(TCPEndPoint *endPoint, bool isIdle);

    /** The event handling function delegate of the endpoint signaling when the
     *  idleness of the TCP connection's send channel changes. This is utilized
     *  by upper layers to take appropriate actions based on whether sent data
     *  has been reliably delivered to the peer. */
    OnTCPSendIdleChangedFunct OnTCPSendIdleChanged;
#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS

private:
    static Weave::System::ObjectPool<TCPEndPoint, INET_CONFIG_NUM_TCP_ENDPOINTS> sPool;

    Weave::System::PacketBuffer *mRcvQueue;
    Weave::System::PacketBuffer *mSendQueue;
#if INET_TCP_IDLE_CHECK_INTERVAL > 0
    uint16_t mIdleTimeout;                              // in units of INET_TCP_IDLE_CHECK_INTERVAL; zero means no timeout
    uint16_t mRemainingIdleTime;                        // in units of INET_TCP_IDLE_CHECK_INTERVAL
#endif // INET_TCP_IDLE_CHECK_INTERVAL > 0

    uint32_t mConnectTimeoutMsecs;                      // This is the timeout to wait for a Connect call to succeed or
                                                        // return an error; zero means use system defaults.

#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
    uint32_t mUserTimeoutMillis;                        // The configured TCP user timeout value in milliseconds.
                                                        // If 0, assume not set.
#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS
    bool mIsTCPSendIdle;                                // Indicates whether the send channel of the TCPEndPoint is Idle.

    uint16_t mTCPSendQueueRemainingPollCount;           // The current remaining number of TCP SendQueue polls before
                                                        // the TCP User timeout period is reached.

    uint32_t mTCPSendQueuePollPeriodMillis;             // The configured period of active polling of the TCP
                                                        // SendQueue. If 0, assume not set.
    void SetTCPSendIdleAndNotifyChange(bool aIsSendIdle);

#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS

    bool mUserTimeoutTimerRunning;                      // Indicates whether the TCP UserTimeout timer has been started.

    static void TCPUserTimeoutHandler(Weave::System::Layer* aSystemLayer, void* aAppState, Weave::System::Error aError);

    void StartTCPUserTimeoutTimer(void);

    void StopTCPUserTimeoutTimer(void);

    void RestartTCPUserTimeoutTimer(void);

    void ScheduleNextTCPUserTimeoutPoll(uint32_t aTimeOut);

#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS
    uint16_t MaxTCPSendQueuePolls(void);
#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS

#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
    uint32_t mBytesWrittenSinceLastProbe;               // This counts the number of bytes written on the TCP socket since the
                                                        // last probe into the TCP outqueue was made.

    uint32_t mLastTCPKernelSendQueueLen;                // This is the measured size(in bytes) of the kernel TCP send queue
                                                        // at the end of the last user timeout window.
    INET_ERROR CheckConnectionProgress(bool &IsProgressing);
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS

#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT

    TCPEndPoint(void);                                  // not defined
    TCPEndPoint(const TCPEndPoint&);                    // not defined
    ~TCPEndPoint(void);                                 // not defined

    void Init(InetLayer *inetLayer);
    INET_ERROR DriveSending(void);
    void DriveReceiving(void);
    void HandleConnectComplete(INET_ERROR err);
    void HandleAcceptError(INET_ERROR err);
    INET_ERROR DoClose(INET_ERROR err, bool suppressCallback);
    static bool IsConnected(int state);

    static void TCPConnectTimeoutHandler(Weave::System::Layer* aSystemLayer, void* aAppState, Weave::System::Error aError);

    void StartConnectTimerIfSet(void);
    void StopConnectTimer(void);

#if WEAVE_SYSTEM_CONFIG_USE_LWIP
    struct BufferOffset {
        const Weave::System::PacketBuffer *buffer;
        uint32_t offset;
    };

    uint32_t mUnackedLength;                            // Amount sent but awaiting ACK. Used as a form of reference count
                                                        // to hang-on to backing packet buffers until they are no longer needed.

    uint32_t RemainingToSend();
    BufferOffset FindStartOfUnsent();
    INET_ERROR GetPCB(IPAddressType addrType);
    void HandleDataSent(uint16_t len);
    void HandleDataReceived(Weave::System::PacketBuffer *buf);
    void HandleIncomingConnection(TCPEndPoint *pcb);
    void HandleError(INET_ERROR err);

    static err_t LwIPHandleConnectComplete(void *arg, struct tcp_pcb *tpcb, err_t lwipErr);
    static err_t LwIPHandleIncomingConnection(void *arg, struct tcp_pcb *tcpConPCB, err_t lwipErr);
    static err_t LwIPHandleDataReceived(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
    static err_t LwIPHandleDataSent(void *arg, struct tcp_pcb *tpcb, u16_t len);
    static void LwIPHandleError(void *arg, err_t err);

#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP

#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
    INET_ERROR GetSocket(IPAddressType addrType);
    SocketEvents PrepareIO(void);
    void HandlePendingIO(void);
    void ReceiveData(void);
    void HandleIncomingConnection(void);
    INET_ERROR BindSrcAddrFromIntf(IPAddressType addrType, InterfaceId intf);
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS

};

#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT
inline uint16_t TCPEndPoint::MaxTCPSendQueuePolls(void)
{
    // If the UserTimeout is configured less than or equal to the poll interval,
    // return 1 to poll at least once instead of returning zero and timing out
    // immediately.
    return  (mUserTimeoutMillis > mTCPSendQueuePollPeriodMillis) ?
             (mUserTimeoutMillis / mTCPSendQueuePollPeriodMillis) : 1;
}
#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS && INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT

inline bool TCPEndPoint::IsConnected(void) const
{
    return IsConnected(State);
}

inline uint16_t TCPEndPoint::LogId(void)
{
    return static_cast<uint16_t>(reinterpret_cast<intptr_t>(this));
}

inline void TCPEndPoint::MarkActive(void)
{
#if INET_TCP_IDLE_CHECK_INTERVAL > 0
    mRemainingIdleTime = mIdleTimeout;
#endif // INET_TCP_IDLE_CHECK_INTERVAL > 0
}

} // namespace Inet
} // namespace nl

#endif // !defined(TCPENDPOINT_H)
