/*
 * Copyright (c) 2015-2017 Ken Bannister. All rights reserved.
 *               2017 Freie Universität Berlin
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @defgroup    net_gcoap  Gcoap
 * @ingroup     net
 * @brief       High-level interface to CoAP messaging
 *
 * gcoap provides a high-level interface for writing CoAP messages via RIOT's
 * sock networking API. gcoap internalizes network event processing so an
 * application only needs to focus on request/response handling. For a server,
 * gcoap accepts a list of resource paths with callbacks for writing the
 * response. For a client, gcoap provides a function to send a request, with a
 * callback for reading the server response.
 *
 * gcoap allocates a RIOT message processing thread, so a single instance can
 * serve multiple applications. This approach also means gcoap uses a single UDP
 * port, which supports RFC 6282 compression. Internally, gcoap depends on the
 * nanocoap package for base level structs and functionality. gcoap uses
 * nanocoap's Packet API to write message options.
 *
 * gcoap supports the Observe extension (RFC 7641) for a server. gcoap provides
 * functions to generate and send an observe notification that are similar to
 * the functions to send a client request. gcoap also supports the Block
 * extension (RFC 7959) with block-specific option functions as well as some
 * helpers.
 *
 * *Contents*
 *
 * - Server Operation
 * - Client Operation
 * - Observe Server Operation
 * - Block Operation
 * - Proxy Operation
 * - Implementation Notes
 * - Implementation Status
 *
 * ## Server Operation ##
 *
 * gcoap listens for requests on CONFIG_GCOAP_PORT, 5683 by default. You can redefine
 * this by uncommenting the appropriate lines in gcoap's make file.
 *
 * gcoap allows an application to specify a collection of request resource paths
 * it wants to be notified about. Create an array of resources (coap_resource_t
 * structs) ordered by the resource path, specifically the ASCII encoding of
 * the path characters (digit and capital precede lower case). Use
 * gcoap_register_listener() at application startup to pass in these resources,
 * wrapped in a gcoap_listener_t. Also see _Server path matching_ in the base
 * [nanocoap](group__net__nanocoap.html) documentation.
 *
 * gcoap itself defines a resource for `/.well-known/core` discovery, which
 * lists all of the registered paths. See the _Resource list creation_ section
 * below for more.
 *
 * ### Creating a response ###
 *
 * An application resource includes a callback function, a coap_handler_t. After
 * reading the request, the callback must use functions provided by gcoap to
 * format the response, as described below. The callback *must* read the request
 * thoroughly before calling the functions, because the response buffer likely
 * reuses the request buffer. See `examples/gcoap/gcoap_cli.c` for a simple
 * example of a callback.
 *
 * Here is the expected sequence for a callback function:
 *
 * Read request completely and parse request payload, if any. Use the
 * coap_pkt_t _payload_ and _payload_len_ attributes.
 *
 * If there is a payload, follow the steps below.
 *
 * -# Call gcoap_resp_init() to initialize the response.
 * -# Use the coap_opt_add_xxx() functions to include any Options, for example
 *    coap_opt_add_format() for Content-Format of the payload. Options *must*
 *    be written in order by option number (see "CoAP option numbers" in
 *    [CoAP defines](group__net__coap.html)).
 * -# Call coap_opt_finish() to complete the PDU metadata. Retain the returned
 *    metadata length.
 * -# Write the response payload, starting at the updated _payload_ pointer
 *    in the coap_pkt_t, for up to _payload_len_ bytes.
 * -# Return the sum of the metadata length and payload length. If some error
 *    has occurred, return a negative errno code from the handler, and gcoap
 *    will send a server error (5.00).
 *
 * If no payload, call only gcoap_response() to write the full response. If you
 * need to add Options, follow the first three steps in the list above instead.
 *
 * ### Resource list creation ###
 *
 * gcoap allows customization of the function that provides the list of registered
 * resources for `/.well-known/core` and CoRE Resource Directory registration.
 * By default gcoap provides gcoap_encode_link(), which lists only the target
 * path for each link. However, an application may specify a custom function in
 * the gcoap_listener_t it registers with gcoap. For example, this function may
 * add parameters to provide more information about the resource, as described
 * in RFC 6690. See the gcoap example for use of a custom encoder function.
 *
 * ## Client Operation ##
 *
 * Client operation includes two phases: creating and sending a request, and
 * handling the response asynchronously in a client supplied callback. See
 * `examples/gcoap/gcoap_cli.c` for a simple example of sending a request and
 * reading the response.
 *
 * ### Creating a request ###
 *
 * Here is the expected sequence to prepare and send a request:
 *
 * Allocate a buffer and a coap_pkt_t for the request.
 *
 * If there is a payload, follow the steps below.
 *
 * -# Call gcoap_req_init() to initialize the request.
 * -# Optionally, mark the request confirmable by calling coap_hdr_set_type()
 *    with COAP_TYPE_CON.
 * -# Use the coap_opt_add_xxx() functions to include any Options beyond
 *    Uri-Path, which was added in the first step. Options *must* be written
 *    in order by option number (see "CoAP option numbers" in
 *    [CoAP defines](group__net__coap.html)).
 * -# Call coap_opt_finish() to complete the PDU metadata. Retain the returned
 *    metadata length.
 * -# Write the request payload, starting at the updated _payload_ pointer
 *    in the coap_pkt_t, for up to _payload_len_ bytes.
 *
 * If no payload, call only gcoap_request() to write the full request. If you
 * need to add Options, follow the first four steps in the list above instead.
 *
 * Finally, call gcoap_req_send() with the sum of the metadata length and
 * payload length, the destination endpoint, and a callback function for the
 * host's response.
 *
 * ### Handling the response ###
 *
 * When gcoap receives the response to a request, it executes the callback from
 * the request. gcoap also executes the callback when a response is not
 * received within GCOAP_RESPONSE_TIMEOUT.
 *
 * Here is the expected sequence for handling a response in the callback.
 *
 * -# Test for a server response or timeout in the `state` field of the `memo`
 *    callback parameter (`memo->state`). See the GCOAP_MEMO... constants.
 * -# Test the response with coap_get_code_class() and coap_get_code_detail().
 * -# Test the response payload with the coap_pkt_t _payload_len_ and
 *    _content_type_ attributes.
 * -# Read the payload, if any.
 *
 * ## Observe Server Operation
 *
 * A CoAP client may register for Observe notifications for any resource that
 * an application has registered with gcoap. An application does not need to
 * take any action to support Observe client registration. However, gcoap
 * limits registration for a given resource to a _single_ observer.
 *
 * It is [suggested](https://tools.ietf.org/html/rfc7641#section-6) that a
 * server adds the 'obs' attribute to resources that are useful for observation
 * (i.e. will produce notifications) as a hint. Keep in mind that this is not
 * mandatory in order to enable the mechanism in RIOT, nor will it prevent a
 * client from observing a resource that does not have this attribute in the
 * link description. See the "Resource list creation" section above for how the
 * gcoap example app publishes the obs attribute.
 *
 * An Observe notification is considered a response to the original client
 * registration request. So, the Observe server only needs to create and send
 * the notification -- no further communication or callbacks are required.
 *
 * ### Creating a notification ###
 *
 * Here is the expected sequence to prepare and send a notification:
 *
 * Allocate a buffer and a coap_pkt_t for the notification, then follow the
 * steps below.
 *
 * -# Call gcoap_obs_init() to initialize the notification for a resource.
 *    Test the return value, which may indicate there is not an observer for
 *    the resource. If so, you are done.
 * -# Use the coap_opt_add_xxx() functions to include any Options, for example
 *    coap_opt_add_format() for Content-Format of the payload. Options *must*
 *    be written in order by option number (see "CoAP option numbers" in
 *    [CoAP defines](group__net__coap.html)).
 * -# Call coap_opt_finish() to complete the PDU metadata. Retain the returned
 *    metadata length.
 * -# Write the notification payload, starting at the updated _payload_ pointer
 *    in the coap_pkt_t, for up to _payload_len_ bytes.
 *
 * Finally, call gcoap_obs_send() for the resource, with the sum of the
 * metadata length and payload length for the representation.
 *
 * ### Other considerations ###
 *
 * By default, the value for the Observe option in a notification is three
 * bytes long. For resources that change slowly, this length can be reduced via
 * CONFIG_GCOAP_OBS_VALUE_WIDTH.
 *
 * A client always may re-register for a resource with the same token or with
 * a new token to indicate continued interest in receiving notifications about
 * it. Of course the client must not already be using any new token in the
 * registration for a different resource. Successful registration always is
 * indicated by the presence of the Observe option in the response.
 *
 * To cancel registration, the server expects to receive a GET request with
 * the Observe option value set to 1. The server does not support cancellation
 * via a reset (RST) response to a non-confirmable notification.
 *
 * ## Block Operation ##
 *
 * gcoap provides for both server side and client side blockwise messaging for
 * requests and responses. This section outlines how to write a message for
 * each situation.
 *
 * ### CoAP server GET handling ###
 *
 * The server must slice the full response body into smaller payloads, and
 * identify the slice with a Block2 option. This implementation toggles the
 * actual writing of data as it passes over the code for the full response
 * body. See the _riot_block2_handler() example in
 * [gcoap-block-server](https://github.com/kb2ma/riot-apps/blob/kb2ma-master/gcoap-block-server/gcoap_block.c),
 * which implements the sequence described below.
 *
 * - Use coap_block2_init() to initialize a _slicer_ struct from the Block2
 *   option in the request. The slicer tracks boundaries while writing the
 *   payload. If no option present in the initial request, the init function
 *   defaults to a payload size of 16 bytes.
 * - Use gcoap_resp_init() to begin the response.
 * - Use coap_opt_add_block2() to write the Block2 option from the slicer. Use
 *   1 as a default for the _more_ parameter. At this point, we don't know yet
 *   if this message will be the last in the block exchange. However, we must
 *   add the block option at this location in the message.
 * - Use coap_opt_finish() to add a payload marker.
 * - Add the payload using the `coap_blockwise_put_xxx()` functions. The slicer
 *   knows the current position in the overall body of the response. It writes
 *   only the portion of the body specified by the block number and block size
 *   in the slicer.
 * - Finally, use coap_block2_finish() to finalize the block option with the
 *   proper value for the _more_ parameter.
 *
 * ### CoAP server PUT/POST handling ###
 *
 * The server must ack each blockwise portion of the response body received
 * from the client by writing a Block1 option in the response. See the
 * _sha256_handler() example in
 * [gcoap-block-server](https://github.com/kb2ma/riot-apps/blob/kb2ma-master/gcoap-block-server/gcoap_block.c),
 * which implements the sequence described below.
 *
 * - Use coap_get_block1() to initialize a block1 struct from the request.
 * - Determine the response code. If the block1 _more_ attribute is 1, use
 *   COAP_CODE_CONTINUE to request more responses. Otherwise, use
 *   COAP_CODE_CHANGED to indicate a successful transfer.
 * - Use gcoap_resp_init() to begin the response, including the response code.
 * - Use coap_opt_add_block1_control() to write the Block1 option.
 * - Use coap_opt_finish() to determine the length of the PDU. If appropriate,
 *   use the COAP_OPT_FINISH_PAYLOAD parameter and then write the payload.
 *
 * ### CoAP client GET request ###
 *
 * The client requests a specific blockwise payload from the overall body by
 * writing a Block2 option in the request. See _resp_handler() in the
 * [gcoap](https://github.com/RIOT-OS/RIOT/blob/master/examples/gcoap/gcoap_cli.c)
 * example in the RIOT distribution, which implements the sequence described
 * below.
 *
 * - For the first request, use coap_block_object_init() to initialize a new
 *   block1 struct. For subsequent requests, first use coap_get_block2() to
 *   read the Block2 option in the response to the previous request. If the
 *   _more_ attribute indicates no more blocks, you are done.
 *   - The gcoap example actually does _not_ include a Block2 option in the
 *     original request, but the server response includes a blockwise response
 *     with a Block2 option anyway. On the other hand, this example shows how
 *     blockwise messaging can be supported in a generic way.
 * - If more blocks are available, use gcoap_req_init() to create a new
 *   request.
 * - Increment the _blknum_ attribute in the block1 struct from the previous
 *   response to request the next blockwise payload.
 * - Use coap_opt_put_block2_control() to write the Block2 option to the
 *   request.
 * - Use coap_opt_finish() to determine the length of the PDU.
 *
 * ### CoAP client PUT/POST request ###
 *
 * The client pushes a specific blockwise payload from the overall body to the
 * server by writing a Block1 option in the request. See _do_block_post() in
 * the [gcoap-block-client](https://github.com/kb2ma/riot-apps/blob/kb2ma-master/gcoap-block-client/gcoap_block.c)
 * example, which implements the sequence described below.
 *
 * - For the first request, use coap_block_slicer_init() to initialize a
 *   _slicer_ struct with the desired block number and block size. For
 *   subsequent requests, first read the response from the server to the
 *   previous request. If the response code is COAP_CODE_CONTINUE, then
 *   increment the last block number sent when initializing the slicer struct
 *   for the next request.
 * - Use gcoap_req_init() to initialize the request.
 * - Use coap_opt_add_block1() to add the Block1 option from the slicer. Use 1
 *   as a default for the _more_ parameter. At this point, we don't know yet if
 *   this message will be the last in the block exchange. However, we must add
 *   the block option at this location in the message.
 * - Use coap_opt_finish() with COAP_OPT_FINISH_PAYLOAD to write the payload
 *   marker.
 * - Add the payload using the `coap_blockwise_put_xxx()` functions. The slicer
 *   knows the current position in the overall body of the response. It writes
 *   only the portion of the body specified by the block number and block size
 *   in the slicer.
 * - Finally, use coap_block1_finish() to finalize the block option with the
 *   proper value for the _more_ parameter.
 *
 * ## Proxy Operation ##
 *
 * A [CoAP proxy](https://tools.ietf.org/html/rfc7252#section-5.7.1)
 * forwards incoming requests to an origin server, or again to another
 * proxy server.
 *
 * ### Proxy Client Handling
 *
 * The current implementation only allows the use of `Proxy-Uri` to
 * specify the absolute URI for the origin server and resource. A
 * request that includes a `Proxy-Uri` option must not contain any of
 * the `Uri-*` options. An example:
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c}
 * // endpoint for the proxy server
 *  sock_udp_ep_t *proxy_remote = ...;
 * // absolute URI for the origin server and resource
 * char *uri = "coap://[2001:db8::1]:5683/.well-known/core";
 *
 * gcoap_req_init(&pdu, buf, CONFIG_GCOAP_PDU_BUF_SIZE, COAP_METHOD_GET, NULL);
 * coap_opt_add_proxy_uri(&pdu, uri);
 * unsigned len = coap_opt_finish(&pdu, COAP_OPT_FINISH_NONE);
 *
 * gcoap_req_send((uint8_t *) pdu->hdr, len, proxy_remote, _resp_handler, NULL);
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * See the gcoap example for a sample implementation.
 *
 * ### Proxy Server Handling
 *
 * Not implemented yet.
 *
 * ## Implementation Notes ##
 *
 * ### Waiting for a response ###
 *
 * We take advantage of RIOT's asynchronous messaging by using an xtimer to wait
 * for a response, so the gcoap thread does not block while waiting. The user is
 * notified via the same callback, whether the message is received or the wait
 * times out. We track the response with an entry in the
 * `_coap_state.open_reqs` array.
 *
 * ## Implementation Status ##
 * gcoap includes server and client capability. Available features include:
 *
 * - Message Type: Supports non-confirmable (NON) messaging. Additionally
 *   provides a callback on timeout. Provides piggybacked ACK response to a
 *   confirmable (CON) request.
 * - Observe extension: Provides server-side registration and notifications.
 * - Server and Client provide helper functions for writing the
 *   response/request. See the CoAP topic in the source documentation for
 *   details. See the gcoap example for sample implementations.
 * - Server allows an application to register a 'listener', which includes an
 *   array of endpoint paths and function callbacks used to write a response.
 * - Server listens on a port at startup; defaults to 5683.
 * - Client operates asynchronously; sends request and then handles response
 *   in a user provided callback.
 * - Client generates token; length defined at compile time.
 * - Options: Supports Content-Format for payload.
 *
 * @{
 *
 * @file
 * @brief       gcoap definition
 *
 * @author      Ken Bannister <kb2ma@runbox.com>
 * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
 */

#ifndef NET_GCOAP_H
#define NET_GCOAP_H

#include <stdint.h>

#include "event/callback.h"
#include "event/timeout.h"
#include "net/ipv6/addr.h"
#include "net/sock/udp.h"
#include "net/nanocoap.h"
#include "xtimer.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @defgroup net_gcoap_conf    Gcoap compile configurations
 * @ingroup  net_gcoap
 * @ingroup  config
 * @{
 */
/**
 * @brief   Server port; use RFC 7252 default if not defined
 */
#ifndef CONFIG_GCOAP_PORT
#define CONFIG_GCOAP_PORT              (5683)
#endif

/**
 * @brief   Size of the buffer used to build a CoAP request or response
 */
#ifndef CONFIG_GCOAP_PDU_BUF_SIZE
#define CONFIG_GCOAP_PDU_BUF_SIZE      (128)
#endif

/**
 * @brief   Maximum number of requests awaiting a response
 */
#ifndef CONFIG_GCOAP_REQ_WAITING_MAX
#define CONFIG_GCOAP_REQ_WAITING_MAX   (2)
#endif
/** @} */

/**
 * @brief   Maximum length in bytes for a token
 */
#define GCOAP_TOKENLEN_MAX      (8)

/**
 * @brief   Maximum length in bytes for a header, including the token
 */
#define GCOAP_HEADER_MAXLEN     (sizeof(coap_hdr_t) + GCOAP_TOKENLEN_MAX)

/**
 * @ingroup net_gcoap_conf
 * @brief   Length in bytes for a token
 *
 * Value must be in the range 0 to @ref GCOAP_TOKENLEN_MAX.
 */
#ifndef CONFIG_GCOAP_TOKENLEN
#define CONFIG_GCOAP_TOKENLEN          (2)
#endif

/**
 * @brief   Marks the boundary between header and payload
 */
#define GCOAP_PAYLOAD_MARKER    (0xFF)

/**
 * @ingroup net_gcoap_conf
 * @brief   Disables gcoap startup during system auto init
 *
 * If disabled, gcoap_init() must be called by some other means.
 */
#ifndef CONFIG_GCOAP_NO_AUTO_INIT
#define CONFIG_GCOAP_NO_AUTO_INIT      0
#endif

/**
 * @name    States for the memo used to track waiting for a response
 * @{
 */
#define GCOAP_MEMO_UNUSED       (0)     /**< This memo is unused */
#define GCOAP_MEMO_RETRANSMIT   (1)     /**< Request sent, retransmitting until response arrives */
#define GCOAP_MEMO_WAIT         (2)     /**< Request sent; awaiting response */
#define GCOAP_MEMO_RESP         (3)     /**< Got response */
#define GCOAP_MEMO_TIMEOUT      (4)     /**< Timeout waiting for response */
#define GCOAP_MEMO_ERR          (5)     /**< Error processing response packet */
/** @} */

/**
 * @brief   Value for send_limit in request memo when non-confirmable type
 */
#define GCOAP_SEND_LIMIT_NON    (-1)

#ifdef DOXYGEN
/**
 * @ingroup net_gcoap_conf
 * @brief   Turns off retransmission backoff when defined (undefined per default)
 *
 * In normal operations the timeout between retransmissions doubles. When
 * CONFIG_GCOAP_NO_RETRANS_BACKOFF is defined this doubling does not happen.
 *
 * @see CONFIG_COAP_ACK_TIMEOUT
 */
#define CONFIG_GCOAP_NO_RETRANS_BACKOFF
#endif

/**
 * @ingroup net_gcoap_conf
 * @brief   Default time to wait for a non-confirmable response [in usec]
 *
 * Set to 0 to disable timeout.
 */
#ifndef CONFIG_GCOAP_NON_TIMEOUT
#define CONFIG_GCOAP_NON_TIMEOUT       (5000000U)
#endif

/**
 * @ingroup net_gcoap_conf
 * @brief   Maximum number of Observe clients
 */
#ifndef CONFIG_GCOAP_OBS_CLIENTS_MAX
#define CONFIG_GCOAP_OBS_CLIENTS_MAX   (2)
#endif

/**
 * @ingroup net_gcoap_conf
 * @brief   Maximum number of registrations for Observable resources
 */
#ifndef CONFIG_GCOAP_OBS_REGISTRATIONS_MAX
#define CONFIG_GCOAP_OBS_REGISTRATIONS_MAX     (2)
#endif

/**
 * @name    States for the memo used to track Observe registrations
 * @{
 */
#define GCOAP_OBS_MEMO_UNUSED   (0) /**< This memo is unused */
#define GCOAP_OBS_MEMO_IDLE     (1) /**< Registration OK; no current activity */
#define GCOAP_OBS_MEMO_PENDING  (2) /**< Resource changed; notification pending */
/** @} */

/**
 * @ingroup net_gcoap_conf
 * @brief   Width in bytes of the Observe option value for a notification
 *
 * This width is used to determine the length of the 'tick' used to measure
 * the time between observable changes to a resource. A tick is expressed
 * internally as GCOAP_OBS_TICK_EXPONENT, which is the base-2 log value of the
 * tick length in microseconds.
 *
 * The canonical setting for the value width is 3 (exponent 5), which results
 * in a tick length of 32 usec, per sec. 3.4, 4.4 of the RFC. Width 2
 * (exponent 16) results in a tick length of ~65 msec, and width 1 (exponent
 * 24) results in a tick length of ~17 sec.
 *
 * The tick length must be short enough so that the Observe value strictly
 * increases for each new notification. The purpose of the value is to allow a
 * client to detect message reordering within the network latency period (128
 * sec). For resources that change only slowly, the reduced message length is
 * useful when packet size is limited.
 */
#ifndef CONFIG_GCOAP_OBS_VALUE_WIDTH
#define CONFIG_GCOAP_OBS_VALUE_WIDTH   (3)
#endif

/**
 * @brief   See CONFIG_GCOAP_OBS_VALUE_WIDTH
 */
#if (CONFIG_GCOAP_OBS_VALUE_WIDTH == 3)
#define GCOAP_OBS_TICK_EXPONENT (5)
#elif (CONFIG_GCOAP_OBS_VALUE_WIDTH == 2)
#define GCOAP_OBS_TICK_EXPONENT (16)
#elif (CONFIG_GCOAP_OBS_VALUE_WIDTH == 1)
#define GCOAP_OBS_TICK_EXPONENT (24)
#endif

/**
 * @name    Return values for gcoap_obs_init()
 * @{
 */
#define GCOAP_OBS_INIT_OK       (0)
#define GCOAP_OBS_INIT_ERR      (-1)
#define GCOAP_OBS_INIT_UNUSED   (-2)
/** @} */

/**
 * @brief Stack size for module thread
 */
#ifndef GCOAP_STACK_SIZE
#define GCOAP_STACK_SIZE (THREAD_STACKSIZE_DEFAULT + DEBUG_EXTRA_STACKSIZE \
                          + sizeof(coap_pkt_t))
#endif

/**
 * @ingroup net_gcoap_conf
 * @brief   Count of PDU buffers available for resending confirmable messages
 */
#ifndef CONFIG_GCOAP_RESEND_BUFS_MAX
#define CONFIG_GCOAP_RESEND_BUFS_MAX      (1)
#endif

/**
 * @name Bitwise positional flags for encoding resource links
 * @anchor COAP_LINK_FLAG_
 * @{
 */
#define COAP_LINK_FLAG_INIT_RESLIST  (1)  /**< initialize as for first resource
                                           *   in a list */
/** @} */

/**
 * @brief   Context information required to write a resource link
 */
typedef struct {
    unsigned content_format;            /**< link format */
    size_t link_pos;                    /**< position of link within listener */
    uint16_t flags;                     /**< encoder switches; see @ref
                                             COAP_LINK_FLAG_ constants */
} coap_link_encoder_ctx_t;

/**
 * @brief   Handler function to write a resource link
 *
 * @param[in] resource      Resource for link
 * @param[out] buf          Buffer on which to write; may be null
 * @param[in] maxlen        Remaining length for @p buf
 * @param[in] context       Contextual information on what/how to write
 *
 * @return  count of bytes written to @p buf (or writable if @p buf is null)
 * @return  -1 on error
 */
typedef ssize_t (*gcoap_link_encoder_t)(const coap_resource_t *resource, char *buf,
                                        size_t maxlen, coap_link_encoder_ctx_t *context);

/**
 * @name    Return values for resource related operations
 * @{
 */
#define GCOAP_RESOURCE_FOUND        (0)
#define GCOAP_RESOURCE_WRONG_METHOD (1)
#define GCOAP_RESOURCE_NO_PATH      (2)
#define GCOAP_RESOURCE_ERROR        (3)
/** @} */

/**
 * @brief   Forward declaration of the gcoap listener state container
 */
typedef struct gcoap_listener gcoap_listener_t;

/**
 * @brief   Handler function for the request matcher strategy
 *
 * @param[in]  listener     Listener context
 * @param[out] resource     Matching resource
 * @param[in]  pdu          Pointer to the PDU
 *
 * @return  GCOAP_RESOURCE_FOUND      on resource match
 * @return  GCOAP_RESOURCE_NO_PATH    on no path found in @p resource
 *                                    that matches @p pdu
 * @return  GCOAP_RESOURCE_ERROR      on processing failure of the request
 */
typedef int (*gcoap_request_matcher_t)(gcoap_listener_t *listener,
                                       const coap_resource_t **resource,
                                       const coap_pkt_t *pdu);

/**
 * @brief   A modular collection of resources for a server
 */
struct gcoap_listener {
    const coap_resource_t *resources;   /**< First element in the array of
                                         *   resources; must order alphabetically */
    size_t resources_len;               /**< Length of array */
    gcoap_link_encoder_t link_encoder;  /**< Writes a link for a resource */
    struct gcoap_listener *next;        /**< Next listener in list */

    /**
     * @brief  Function that picks a suitable request handler from a
     * request.
     *
     * @note Leaving this NULL selects the default strategy that picks
     * handlers by matching their Uri-Path to resource paths (as per
     * the documentation of the @ref resources and @ref resources_len
     * fields). Alternative handlers may cast the @ref resources and
     * @ref resources_len fields to fit their needs.
     */
    gcoap_request_matcher_t request_matcher;
};

/**
 * @brief   Forward declaration of the request memo type
 */
typedef struct gcoap_request_memo gcoap_request_memo_t;

/**
 * @brief   Handler function for a server response, including the state for the
 *          originating request
 *
 * If request timed out, the packet header is for the request.
 */
typedef void (*gcoap_resp_handler_t)(const gcoap_request_memo_t *memo,
                                     coap_pkt_t* pdu,
                                     const sock_udp_ep_t *remote);

/**
 * @brief  Extends request memo for resending a confirmable request.
 */
typedef struct {
    uint8_t *pdu_buf;                   /**< Buffer containing the PDU */
    size_t pdu_len;                     /**< Length of pdu_buf */
} gcoap_resend_t;

/**
 * @brief   Memo to handle a response for a request
 */
struct gcoap_request_memo {
    unsigned state;                     /**< State of this memo, a GCOAP_MEMO... */
    int send_limit;                     /**< Remaining resends, 0 if none;
                                             GCOAP_SEND_LIMIT_NON if non-confirmable */
    union {
        uint8_t hdr_buf[GCOAP_HEADER_MAXLEN];
                                        /**< Copy of PDU header, if no resends */
        gcoap_resend_t data;            /**< Endpoint and PDU buffer, for resend */
    } msg;                              /**< Request message data; if confirmable,
                                             supports resending message */
    sock_udp_ep_t remote_ep;            /**< Remote endpoint */
    gcoap_resp_handler_t resp_handler;  /**< Callback for the response */
    void *context;                      /**< ptr to user defined context data */
    event_timeout_t resp_evt_tmout;     /**< Limits wait for response */
    event_callback_t resp_tmout_cb;     /**< Callback for response timeout */
};

/**
 * @brief   Memo for Observe registration and notifications
 */
typedef struct {
    sock_udp_ep_t *observer;            /**< Client endpoint; unused if null */
    const coap_resource_t *resource;    /**< Entity being observed */
    uint8_t token[GCOAP_TOKENLEN_MAX];  /**< Client token for notifications */
    unsigned token_len;                 /**< Actual length of token attribute */
} gcoap_observe_memo_t;

/**
 * @brief   Initializes the gcoap thread and device
 *
 * Must call once before first use.
 *
 * @return  PID of the gcoap thread on success.
 * @return  -EEXIST, if thread already has been created.
 * @return  -EINVAL, if the IP port already is in use.
 */
kernel_pid_t gcoap_init(void);

/**
 * @brief   Starts listening for resource paths
 *
 * @pre @p listener is a valid pointer to a single listener (that is,
 *      `listener->next == NULL`)
 *
 * @note If you are tempted to register a pre-linked chain of listeners,
 *       consider placing all their resources in the resources array of a
 *       single listener instead. In the few cases where this does not work
 *       (that is, when the resources need a different `link_encoder` or other
 *       fields of the listener struct), they can just be registered
 *       individually.
 *
 * @param[in] listener  Listener containing the resources.
 */
void gcoap_register_listener(gcoap_listener_t *listener);

/**
 * @brief   Initializes a CoAP request PDU on a buffer.
 *
 * If @p code is COAP_CODE_EMPTY, prepares a complete "CoAP ping" 4 byte empty
 * message request, ready to send.
 *
 * @param[out] pdu      Request metadata
 * @param[out] buf      Buffer containing the PDU
 * @param[in] len       Length of the buffer
 * @param[in] code      Request code, one of COAP_METHOD_XXX or COAP_CODE_EMPTY
 *                      to ping
 * @param[in] path      Resource path, may be NULL
 *
 * @pre @p path must start with `/` if not NULL
 *
 * @return  0 on success
 * @return  < 0 on error
 */
int gcoap_req_init(coap_pkt_t *pdu, uint8_t *buf, size_t len,
                   unsigned code, const char *path);

/**
 * @brief   Writes a complete CoAP request PDU when there is not a payload
 *
 * @param[in,out] pdu   Request metadata
 * @param[in,out] buf   Buffer containing the PDU
 * @param[in] len       Length of the buffer
 * @param[in] code      Request code: GCOAP_[GET|POST|PUT|DELETE]
 * @param[in] path      Resource path, *must* start with '/'
 *
 * @return  size of the PDU within the buffer
 * @return  < 0 on error
 */
static inline ssize_t gcoap_request(coap_pkt_t *pdu, uint8_t *buf, size_t len,
                                    unsigned code, char *path)
{
    return (gcoap_req_init(pdu, buf, len, code, path) == 0)
                ? coap_opt_finish(pdu, COAP_OPT_FINISH_NONE)
                : -1;
}

/**
 * @brief   Sends a buffer containing a CoAP request to the provided endpoint
 *
 * @param[in] buf           Buffer containing the PDU
 * @param[in] len           Length of the buffer
 * @param[in] remote        Destination for the packet
 * @param[in] resp_handler  Callback when response received, may be NULL
 * @param[in] context       User defined context passed to the response handler
 *
 * @return  length of the packet
 * @return  0 if cannot send
 */
size_t gcoap_req_send(const uint8_t *buf, size_t len,
                      const sock_udp_ep_t *remote,
                      gcoap_resp_handler_t resp_handler, void *context);

/**
 * @brief   Initializes a CoAP response packet on a buffer
 *
 * Initializes payload location within the buffer based on packet setup.
 *
 * @param[out] pdu      Response metadata
 * @param[in] buf       Buffer containing the PDU
 * @param[in] len       Length of the buffer
 * @param[in] code      Response code
 *
 * @return  0 on success
 * @return  < 0 on error
 */
int gcoap_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code);

/**
 * @brief   Writes a complete CoAP response PDU when there is no payload
 *
 * @param[out] pdu      Response metadata
 * @param[out] buf      Buffer containing the PDU
 * @param[in] len       Length of the buffer
 * @param[in] code      Response code
 *
 * @return  size of the PDU within the buffer
 * @return  < 0 on error
 */
static inline ssize_t gcoap_response(coap_pkt_t *pdu, uint8_t *buf,
                                     size_t len, unsigned code)
{
    return (gcoap_resp_init(pdu, buf, len, code) == 0)
                ? coap_opt_finish(pdu, COAP_OPT_FINISH_NONE)
                : -1;
}

/**
 * @brief   Initializes a CoAP Observe notification packet on a buffer, for the
 *          observer registered for a resource
 *
 * First verifies that an observer has been registered for the resource.
 *
 * @param[out] pdu      Notification metadata
 * @param[out] buf      Buffer containing the PDU
 * @param[in] len       Length of the buffer
 * @param[in] resource  Resource for the notification
 *
 * @return  GCOAP_OBS_INIT_OK     on success
 * @return  GCOAP_OBS_INIT_ERR    on error
 * @return  GCOAP_OBS_INIT_UNUSED if no observer for resource
 */
int gcoap_obs_init(coap_pkt_t *pdu, uint8_t *buf, size_t len,
                   const coap_resource_t *resource);

/**
 * @brief   Sends a buffer containing a CoAP Observe notification to the
 *          observer registered for a resource
 *
 * Assumes a single observer for a resource.
 *
 * @param[in] buf Buffer containing the PDU
 * @param[in] len Length of the buffer
 * @param[in] resource Resource to send
 *
 * @return  length of the packet
 * @return  0 if cannot send
 */
size_t gcoap_obs_send(const uint8_t *buf, size_t len,
                      const coap_resource_t *resource);

/**
 * @brief   Provides important operational statistics
 *
 * Useful for monitoring.
 *
 * @return  count of unanswered requests
 */
uint8_t gcoap_op_state(void);

/**
 * @brief   Get the resource list, currently only `CoRE Link Format`
 *          (COAP_FORMAT_LINK) supported
 *
 * If @p buf := NULL, nothing will be written but the size of the resulting
 * resource list is computed and returned.
 *
 * @param[out] buf      output buffer to write resource list into, my be NULL
 * @param[in]  maxlen   length of @p buf, ignored if @p buf is NULL
 * @param[in]  cf       content format to use for the resource list, currently
 *                      only COAP_FORMAT_LINK supported
 *
 * @todo    add support for `JSON CoRE Link Format`
 * @todo    add support for 'CBOR CoRE Link Format`
 *
 * @return  the number of bytes written to @p buf
 * @return  -1 on error
 */
int gcoap_get_resource_list(void *buf, size_t maxlen, uint8_t cf);

/**
 * @brief   Writes a resource in CoRE Link Format to a provided buffer.
 *
 * This default implementation only writes the resource path.
 *
 * @param[in]  resource  resource to write
 * @param[out] buf       output buffer to write link into, may be null
 * @param[in]  maxlen    length of @p buf, ignored if @p buf is NULL
 * @param[in]  context   other parameters that affect how link is written
 *
 * @return  count of bytes written to @p buf (or writable if @p buf is null)
 * @return  -1 on error
 */
ssize_t gcoap_encode_link(const coap_resource_t *resource, char *buf,
                          size_t maxlen, coap_link_encoder_ctx_t *context);

#ifdef __cplusplus
}
#endif

#endif /* NET_GCOAP_H */
/** @} */
