/*
 * Copyright (c) 2022 Meta
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef ZEPHYR_INCLUDE_NET_HTTP_SERVICE_H_
#define ZEPHYR_INCLUDE_NET_HTTP_SERVICE_H_

#include <stdint.h>
#include <stddef.h>

#include <zephyr/sys/iterable_sections.h>

#ifdef __cplusplus
extern "C" {
#endif

struct http_resource_desc {
	const char *resource;
	void *detail;
};

/**
 * @brief Define a static HTTP resource
 *
 * A static HTTP resource is one that is known prior to system initialization. In contrast,
 * dynamic resources may be discovered upon system initialization. Dynamic resources may also be
 * inserted, or removed by events originating internally or externally to the system at runtime.
 *
 * @note The @p _resource is the URL without the associated protocol, host, or URL parameters. E.g.
 * the resource for `http://www.foo.com/bar/baz.html#param1=value1` would be `/bar/baz.html`. It
 * is often referred to as the "path" of the URL. Every `(service, resource)` pair should be
 * unique. The @p _resource must be non-NULL.
 *
 * @param _name Name of the resource.
 * @param _service Name of the associated service.
 * @param _resource Pathname-like string identifying the resource.
 * @param _detail Implementation-specific detail associated with the resource.
 */
#define HTTP_RESOURCE_DEFINE(_name, _service, _resource, _detail)                                  \
	const STRUCT_SECTION_ITERABLE_ALTERNATE(http_resource_desc_##_service, http_resource_desc, \
						_name) = {                                         \
		.resource = _resource,                                                             \
		.detail = (_detail),                                                               \
	}

struct http_service_desc {
	const char *host;
	uint16_t *port;
	void *detail;
	size_t concurrent;
	size_t backlog;
	struct http_resource_desc *res_begin;
	struct http_resource_desc *res_end;
};

#define __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, _res_begin,   \
				_res_end)                                                          \
	static const STRUCT_SECTION_ITERABLE(http_service_desc, _name) = {                         \
		.host = _host,                                                                     \
		.port = (uint16_t *)(_port),                                                       \
		.detail = (void *)(_detail),                                                       \
		.concurrent = (_concurrent),                                                       \
		.backlog = (_backlog),                                                             \
		.res_begin = (_res_begin),                                                         \
		.res_end = (_res_end),                                                             \
	}

/**
 * @brief Define an HTTP service without static resources.
 *
 * @note The @p _host parameter must be non-`NULL`. It is used to specify an IP address either in
 * IPv4 or IPv6 format a fully-qualified hostname or a virtual host.
 *
 * @note The @p _port parameter must be non-`NULL`. It points to a location that specifies the port
 * number to use for the service. If the specified port number is zero, then an ephemeral port
 * number will be used and the actual port number assigned will be written back to memory. For
 * ephemeral port numbers, the memory pointed to by @p _port must be writeable.
 *
 * @param _name Name of the service.
 * @param _host IP address or hostname associated with the service.
 * @param[inout] _port Pointer to port associated with the service.
 * @param _concurrent Maximum number of concurrent clients.
 * @param _backlog Maximum number queued connections.
 * @param _detail Implementation-specific detail associated with the service.
 */
#define HTTP_SERVICE_DEFINE_EMPTY(_name, _host, _port, _concurrent, _backlog, _detail)             \
	__z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, NULL, NULL)

/**
 * @brief Define an HTTP service with static resources.
 *
 * @note The @p _host parameter must be non-`NULL`. It is used to specify an IP address either in
 * IPv4 or IPv6 format a fully-qualified hostname or a virtual host.
 *
 * @note The @p _port parameter must be non-`NULL`. It points to a location that specifies the port
 * number to use for the service. If the specified port number is zero, then an ephemeral port
 * number will be used and the actual port number assigned will be written back to memory. For
 * ephemeral port numbers, the memory pointed to by @p _port must be writeable.
 *
 * @param _name Name of the service.
 * @param _host IP address or hostname associated with the service.
 * @param[inout] _port Pointer to port associated with the service.
 * @param _concurrent Maximum number of concurrent clients.
 * @param _backlog Maximum number queued connections.
 * @param _detail Implementation-specific detail associated with the service.
 */
#define HTTP_SERVICE_DEFINE(_name, _host, _port, _concurrent, _backlog, _detail)                   \
	extern struct http_resource_desc _CONCAT(_http_resource_desc_##_name, _list_start)[];      \
	extern struct http_resource_desc _CONCAT(_http_resource_desc_##_name, _list_end)[];        \
	__z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail,               \
				&_CONCAT(_http_resource_desc_##_name, _list_start)[0],             \
				&_CONCAT(_http_resource_desc_##_name, _list_end)[0])

/**
 * @brief Count the number of HTTP services.
 *
 * @param[out] _dst Pointer to location where result is written.
 */
#define HTTP_SERVICE_COUNT(_dst) STRUCT_SECTION_COUNT(http_service_desc, _dst)

/**
 * @brief Count HTTP service static resources.
 *
 * @param _service Pointer to a service.
 */
#define HTTP_SERVICE_RESOURCE_COUNT(_service) ((_service)->res_end - (_service)->res_begin)

/**
 * @brief Iterate over all HTTP services.
 *
 * @param _it Name of iterator (of type @ref http_service_desc)
 */
#define HTTP_SERVICE_FOREACH(_it) STRUCT_SECTION_FOREACH(http_service_desc, _it)

/**
 * @brief Iterate over static HTTP reesources associated with a given @p _service.
 *
 * @note This macro requires that @p _service is defined with @ref HTTP_SERVICE_DEFINE.
 *
 * @param _service Name of HTTP service
 * @param _it Name of iterator (of type @ref http_resource_desc)
 */
#define HTTP_RESOURCE_FOREACH(_service, _it)                                                       \
	STRUCT_SECTION_FOREACH_ALTERNATE(http_resource_desc_##_service, http_resource_desc, _it)

/**
 * @brief Iterate over all static resources associated with @p _service .
 *
 * @note This macro is suitable for a @p _service defined with either @ref HTTP_SERVICE_DEFINE
 * or @ref HTTP_SERVICE_DEFINE_EMPTY.
 *
 * @param _service Pointer to HTTP service
 * @param _it Name of iterator (of type @ref http_resource_desc)
 */
#define HTTP_SERVICE_FOREACH_RESOURCE(_service, _it)                                               \
	for (struct http_resource_desc *_it = (_service)->res_begin; ({                            \
		     __ASSERT(_it <= (_service)->res_end, "unexpected list end location");         \
		     _it < (_service)->res_end;                                                    \
	     });                                                                                   \
	     _it++)

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_NET_HTTP_SERVICE_H_ */
