// Copyright (c) 2004-2013 Sergey Lyubka
// Copyright (c) 2013-2022 Cesanta Software Limited
// All rights reserved
//
// This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see http://www.gnu.org/licenses/
//
// You are free to use this software under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// Alternatively, you can license this software under a commercial
// license, as set out in https://www.mongoose.ws/licensing/
//
// SPDX-License-Identifier: GPL-2.0-only or commercial

#ifndef MONGOOSE_H
#define MONGOOSE_H

#define MG_VERSION "7.9"

#ifdef __cplusplus
extern "C" {
#endif

#define MG_ARCH_CUSTOM    0     // User creates its own mongoose_custom.h
#define MG_ARCH_UNIX      1     // Linux, BSD, Mac, ...
#define MG_ARCH_WIN32     2     // Windows
#define MG_ARCH_ESP32     3     // ESP32
#define MG_ARCH_ESP8266   4     // ESP8266
#define MG_ARCH_FREERTOS  5     // FreeRTOS
#define MG_ARCH_AZURERTOS 6     // MS Azure RTOS
#define MG_ARCH_ZEPHYR    7     // Zephyr RTOS
#define MG_ARCH_NEWLIB    8     // Bare metal ARM
#define MG_ARCH_RTX       9     // Keil MDK RTX
#define MG_ARCH_TIRTOS    10    // Texas Semi TI-RTOS
#define MG_ARCH_RP2040    11    // Raspberry Pi RP2040

#if !defined(MG_ARCH)
#if defined(__unix__) || defined(__APPLE__)
#define MG_ARCH MG_ARCH_UNIX
#elif defined(_WIN32)
#define MG_ARCH MG_ARCH_WIN32
#elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR)
#define MG_ARCH MG_ARCH_ESP8266
#elif defined(__ZEPHYR__)
#define MG_ARCH MG_ARCH_ZEPHYR
#elif defined(ESP_PLATFORM)
#define MG_ARCH MG_ARCH_ESP32
#elif defined(FREERTOS_IP_H)
#define MG_ARCH                MG_ARCH_FREERTOS
#define MG_ENABLE_FREERTOS_TCP 1
#elif defined(AZURE_RTOS_THREADX)
#define MG_ARCH MG_ARCH_AZURERTOS
#elif defined(PICO_TARGET_NAME)
#define MG_ARCH MG_ARCH_RP2040
#endif
#endif    // !defined(MG_ARCH)

#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM)
#include "mongoose_custom.h"    // keep this include
#endif

#if !defined(MG_ARCH)
#error "MG_ARCH is not specified and we couldn't guess it. Set -D MG_ARCH=..."
#endif

// http://esr.ibiblio.org/?p=5095
#define MG_BIG_ENDIAN (*(uint16_t *)"\0\xff" < 0x100)

#if MG_ARCH == MG_ARCH_AZURERTOS

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>

#include <fx_api.h>
#include <tx_api.h>

#include <nx_api.h>
#include <nx_bsd.h>
#include <nx_port.h>
#include <tx_port.h>

#define PATH_MAX       FX_MAXIMUM_PATH
#define MG_DIRSEP      '\\'

#define socklen_t      int
#define closesocket(x) soc_close(x)

#undef FOPEN_MAX

#endif

#if MG_ARCH == MG_ARCH_ESP32

#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>

#include <esp_timer.h>

#define MG_PATH_MAX 128

#endif

#if MG_ARCH == MG_ARCH_ESP8266

#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>

#include <esp_system.h>

#define MG_PATH_MAX 128

#endif

#if MG_ARCH == MG_ARCH_FREERTOS

#include <ctype.h>
// #include <errno.h> // Cannot include errno - might conflict with lwip!
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>    // rand(), strtol(), atoi()
#include <string.h>
#include <sys/stat.h>

#include <FreeRTOS.h>
#include <task.h>

#ifndef MG_IO_SIZE
#define MG_IO_SIZE 512
#endif

#define calloc(a, b) mg_calloc(a, b)
#define free(a)      vPortFree(a)
#define malloc(a)    pvPortMalloc(a)
#define strdup(s)    ((char *)mg_strdup(mg_str(s)).ptr)

// Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
static inline void *mg_calloc(size_t cnt, size_t size) {
    void *p = pvPortMalloc(cnt * size);
    if (p != NULL) {
        memset(p, 0, size * cnt);
    }
    return p;
}

#define mkdir(a, b) mg_mkdir(a, b)

static inline int mg_mkdir(const char *path, mode_t mode) {
    (void)path, (void)mode;
    return -1;
}

#endif    // MG_ARCH == MG_ARCH_FREERTOS

#if MG_ARCH == MG_ARCH_NEWLIB
#define _POSIX_TIMERS

#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define MG_PATH_MAX       100
#define MG_ENABLE_SOCKET  0
#define MG_ENABLE_DIRLIST 0

#endif

#if MG_ARCH == MG_ARCH_RP2040
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <pico/stdlib.h>
int mkdir(const char *, mode_t);
#endif

#if MG_ARCH == MG_ARCH_RTX

#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#if !defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP)
#define MG_ENABLE_RL 1
#endif

#endif

#if MG_ARCH == MG_ARCH_TIRTOS

#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <serrno.h>
#include <sys/socket.h>

#include <ti/sysbios/knl/Clock.h>

#endif

#if MG_ARCH == MG_ARCH_UNIX

#define _DARWIN_UNLIMITED_SELECT 1    // No limit on file descriptors

#if defined(__APPLE__)
#include <mach/mach_time.h>
#endif

#if !defined(MG_ENABLE_EPOLL) && defined(__linux__)
#define MG_ENABLE_EPOLL 1
#elif !defined(MG_ENABLE_POLL)
#define MG_ENABLE_POLL 1
#endif

#include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(MG_ENABLE_EPOLL) && MG_ENABLE_EPOLL
#include <sys/epoll.h>
#elif defined(MG_ENABLE_POLL) && MG_ENABLE_POLL
#include <poll.h>
#else
#include <sys/select.h>
#endif

#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#ifndef MG_ENABLE_DIRLIST
#define MG_ENABLE_DIRLIST 1
#endif

#ifndef MG_PATH_MAX
#define MG_PATH_MAX FILENAME_MAX
#endif

#endif

#if MG_ARCH == MG_ARCH_WIN32

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif

#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif

#include <ctype.h>
#include <direct.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>

#if defined(_MSC_VER) && _MSC_VER < 1700
#define __func__ ""
typedef __int64          int64_t;
typedef unsigned __int64 uint64_t;
typedef unsigned char    uint8_t;
typedef char             int8_t;
typedef unsigned short   uint16_t;
typedef short            int16_t;
typedef unsigned int     uint32_t;
typedef int              int32_t;

typedef enum {
    false = 0,
    true  = 1
} bool;
#else
#include <stdbool.h>
#include <stdint.h>
#include <ws2tcpip.h>
#endif

#include <process.h>
#include <winerror.h>
#include <winsock2.h>

// Protect from calls like std::snprintf in app code
// See https://github.com/cesanta/mongoose/issues/1047
#ifndef __cplusplus
#define snprintf  _snprintf
#define vsnprintf _vsnprintf
#ifndef strdup    // For MSVC with _DEBUG, see #1359
#define strdup(x) _strdup(x)
#endif
#endif

#define MG_INVALID_SOCKET INVALID_SOCKET
#define MG_SOCKET_TYPE    SOCKET
typedef unsigned long nfds_t;
#define MG_SOCKET_ERRNO WSAGetLastError()
#if defined(_MSC_VER)
#pragma comment(lib, "ws2_32.lib")
#ifndef alloca
#define alloca(a) _alloca(a)
#endif
#endif
#define poll(a, b, c) WSAPoll((a), (b), (c))
#ifndef SO_EXCLUSIVEADDRUSE
#define SO_EXCLUSIVEADDRUSE ((int)(~SO_REUSEADDR))
#endif
#define closesocket(x) closesocket(x)

typedef int socklen_t;
#define MG_DIRSEP '\\'

#ifndef MG_PATH_MAX
#define MG_PATH_MAX FILENAME_MAX
#endif

#ifndef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif

#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
#define sleep(x)       Sleep(x)
#define mkdir(a, b)    _mkdir(a)

#ifndef S_ISDIR
#define S_ISDIR(x) (((x)&_S_IFMT) == _S_IFDIR)
#endif

#ifndef MG_ENABLE_DIRLIST
#define MG_ENABLE_DIRLIST 1
#endif

#endif

#if MG_ARCH == MG_ARCH_ZEPHYR

#include <zephyr/kernel.h>

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <zephyr/net/socket.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>

#define MG_PUTCHAR(x) printk("%c", x)
#ifndef strdup
#define strdup(s) ((char *)mg_strdup(mg_str(s)).ptr)
#endif
#define strerror(x)   zsock_gai_strerror(x)
#define FD_CLOEXEC    0
#define F_SETFD       0
#define MG_ENABLE_SSI 0

int rand(void);
int sscanf(const char *, const char *, ...);

#endif

#if defined(MG_ENABLE_FREERTOS_TCP) && MG_ENABLE_FREERTOS_TCP

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>

#include <FreeRTOS.h>
#include <list.h>
#include <task.h>

#include <FreeRTOS_IP.h>
#include <FreeRTOS_Sockets.h>

#define MG_SOCKET_TYPE             Socket_t
#define MG_INVALID_SOCKET          FREERTOS_INVALID_SOCKET

// Why FreeRTOS-TCP did not implement a clean BSD API, but its own thing
// with FreeRTOS_ prefix, is beyond me
#define IPPROTO_TCP                FREERTOS_IPPROTO_TCP
#define IPPROTO_UDP                FREERTOS_IPPROTO_UDP
#define AF_INET                    FREERTOS_AF_INET
#define SOCK_STREAM                FREERTOS_SOCK_STREAM
#define SOCK_DGRAM                 FREERTOS_SOCK_DGRAM
#define SO_BROADCAST               0
#define SO_ERROR                   0
#define SOL_SOCKET                 0
#define SO_REUSEADDR               0
#define sockaddr_in                freertos_sockaddr
#define sockaddr                   freertos_sockaddr
#define accept(a, b, c)            FreeRTOS_accept((a), (b), (c))
#define connect(a, b, c)           FreeRTOS_connect((a), (b), (c))
#define bind(a, b, c)              FreeRTOS_bind((a), (b), (c))
#define listen(a, b)               FreeRTOS_listen((a), (b))
#define socket(a, b, c)            FreeRTOS_socket((a), (b), (c))
#define send(a, b, c, d)           FreeRTOS_send((a), (b), (c), (d))
#define recv(a, b, c, d)           FreeRTOS_recv((a), (b), (c), (d))
#define setsockopt(a, b, c, d, e)  FreeRTOS_setsockopt((a), (b), (c), (d), (e))
#define sendto(a, b, c, d, e, f)   FreeRTOS_sendto((a), (b), (c), (d), (e), (f))
#define recvfrom(a, b, c, d, e, f) FreeRTOS_recvfrom((a), (b), (c), (d), (e), (f))
#define closesocket(x)             FreeRTOS_closesocket(x)
#define gethostbyname(x)           FreeRTOS_gethostbyname(x)
#define getsockname(a, b, c)       mg_getsockname((a), (b), (c))
#define getpeername(a, b, c)       mg_getpeername((a), (b), (c))

static inline int mg_getsockname(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) {
    (void)fd, (void)buf, (void)len;
    return -1;
}

static inline int mg_getpeername(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) {
    (void)fd, (void)buf, (void)len;
    return 0;
}
#endif

#if defined(MG_ENABLE_LWIP) && MG_ENABLE_LWIP
#if defined(__GNUC__)
#include <sys/stat.h>
#include <sys/time.h>
#else
struct timeval {
    time_t tv_sec;
    long   tv_usec;
};
#endif

#include <lwip/sockets.h>

#if LWIP_SOCKET != 1
// Sockets support disabled in LWIP by default
#error Set LWIP_SOCKET variable to 1 (in lwipopts.h)
#endif
#endif

#if defined(MG_ENABLE_RL) && MG_ENABLE_RL
#include <rl_net.h>

#define MG_ENABLE_CUSTOM_MILLIS 1
#define closesocket(x)          closesocket(x)
#define mkdir(a, b)             (-1)
#define EWOULDBLOCK             BSD_EWOULDBLOCK
#define EAGAIN                  BSD_EWOULDBLOCK
#define EINPROGRESS             BSD_EWOULDBLOCK
#define EINTR                   BSD_EWOULDBLOCK
#define ECONNRESET              BSD_ECONNRESET
#define EPIPE                   BSD_ECONNRESET
#define TCP_NODELAY             SO_KEEPALIVE
#endif

#ifndef MG_ENABLE_LOG
#define MG_ENABLE_LOG 1
#endif

#ifndef MG_ENABLE_MIP
#define MG_ENABLE_MIP 0    // Mongoose built-in network stack
#endif

#ifndef MG_ENABLE_LWIP
#define MG_ENABLE_LWIP 0    // lWIP network stack
#endif

#ifndef MG_ENABLE_FREERTOS_TCP
#define MG_ENABLE_FREERTOS_TCP 0    // Amazon FreeRTOS-TCP network stack
#endif

#ifndef MG_ENABLE_RL
#define MG_ENABLE_RL 0    // ARM MDK network stack
#endif

#ifndef MG_ENABLE_SOCKET
#define MG_ENABLE_SOCKET !MG_ENABLE_MIP
#endif

#ifndef MG_ENABLE_POLL
#define MG_ENABLE_POLL 0
#endif

#ifndef MG_ENABLE_EPOLL
#define MG_ENABLE_EPOLL 0
#endif

#ifndef MG_ENABLE_FATFS
#define MG_ENABLE_FATFS 0
#endif

#ifndef MG_ENABLE_MBEDTLS
#define MG_ENABLE_MBEDTLS 0
#endif

#ifndef MG_ENABLE_OPENSSL
#define MG_ENABLE_OPENSSL 0
#endif

#ifndef MG_ENABLE_CUSTOM_TLS
#define MG_ENABLE_CUSTOM_TLS 0
#endif

#ifndef MG_ENABLE_SSI
#define MG_ENABLE_SSI 0
#endif

#ifndef MG_ENABLE_IPV6
#define MG_ENABLE_IPV6 0
#endif

#ifndef MG_ENABLE_MD5
#define MG_ENABLE_MD5 1
#endif

// Set MG_ENABLE_WINSOCK=0 for Win32 builds with external IP stack (like LWIP)
#ifndef MG_ENABLE_WINSOCK
#define MG_ENABLE_WINSOCK 1
#endif

#ifndef MG_ENABLE_DIRLIST
#define MG_ENABLE_DIRLIST 0
#endif

#ifndef MG_ENABLE_CUSTOM_RANDOM
#define MG_ENABLE_CUSTOM_RANDOM 0
#endif

#ifndef MG_ENABLE_CUSTOM_MILLIS
#define MG_ENABLE_CUSTOM_MILLIS 0
#endif

#ifndef MG_ENABLE_PACKED_FS
#define MG_ENABLE_PACKED_FS 0
#endif

#ifndef MG_IO_SIZE
#define MG_IO_SIZE 2048    // Granularity of the send/recv IO buffer growth
#endif

#ifndef MG_MAX_RECV_SIZE
#define MG_MAX_RECV_SIZE (3 * 1024 * 1024)    // Maximum recv IO buffer size
#endif

#ifndef MG_DATA_SIZE
#define MG_DATA_SIZE 32    // struct mg_connection :: data size
#endif

#ifndef MG_MAX_HTTP_HEADERS
#define MG_MAX_HTTP_HEADERS 30
#endif

#ifndef MG_HTTP_INDEX
#define MG_HTTP_INDEX "index.html"
#endif

#ifndef MG_PATH_MAX
#ifdef PATH_MAX
#define MG_PATH_MAX PATH_MAX
#else
#define MG_PATH_MAX 128
#endif
#endif

#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
#define MG_SOCK_LISTEN_BACKLOG_SIZE 3
#endif

#ifndef MG_DIRSEP
#define MG_DIRSEP '/'
#endif

#ifndef MG_ENABLE_FILE
#if defined(FOPEN_MAX)
#define MG_ENABLE_FILE 1
#else
#define MG_ENABLE_FILE 0
#endif
#endif

#ifndef MG_INVALID_SOCKET
#define MG_INVALID_SOCKET (-1)
#endif

#ifndef MG_SOCKET_TYPE
#define MG_SOCKET_TYPE int
#endif

#ifndef MG_SOCKET_ERRNO
#define MG_SOCKET_ERRNO errno
#endif

#if MG_ENABLE_EPOLL
#define MG_EPOLL_ADD(c)                                                      \
    do {                                                                     \
        struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}};        \
        epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_ADD, (int)(size_t)c->fd, &ev); \
    } while (0)
#define MG_EPOLL_MOD(c, wr)                                                  \
    do {                                                                     \
        struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}};        \
        if (wr)                                                              \
            ev.events |= EPOLLOUT;                                           \
        epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_MOD, (int)(size_t)c->fd, &ev); \
    } while (0)
#else
#define MG_EPOLL_ADD(c)
#define MG_EPOLL_MOD(c, wr)
#endif

struct mg_str {
    const char *ptr;    // Pointer to string data
    size_t      len;    // String len
};

#define MG_NULL_STR \
    { NULL, 0 }

#define MG_C_STR(a) \
    { (a), sizeof(a) - 1 }

// Using macro to avoid shadowing C++ struct constructor, see #1298
#define mg_str(s) mg_str_s(s)

struct mg_str mg_str(const char *s);
struct mg_str mg_str_n(const char *s, size_t n);
int           mg_lower(const char *s);
int           mg_ncasecmp(const char *s1, const char *s2, size_t len);
int           mg_casecmp(const char *s1, const char *s2);
int           mg_vcmp(const struct mg_str *s1, const char *s2);
int           mg_vcasecmp(const struct mg_str *str1, const char *str2);
int           mg_strcmp(const struct mg_str str1, const struct mg_str str2);
struct mg_str mg_strstrip(struct mg_str s);
struct mg_str mg_strdup(const struct mg_str s);
const char   *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
bool          mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
bool          mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
bool          mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v);
bool          mg_split(struct mg_str *s, struct mg_str *k, struct mg_str *v, char delim);
char         *mg_hex(const void *buf, size_t len, char *dst);
void          mg_unhex(const char *buf, size_t len, unsigned char *to);
unsigned long mg_unhexn(const char *s, size_t len);
int           mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip);
int64_t       mg_to64(struct mg_str str);
uint64_t      mg_tou64(struct mg_str str);
char         *mg_remove_double_dots(char *s);

typedef void (*mg_pfn_t)(char, void *);                    // Custom putchar
typedef size_t (*mg_pm_t)(mg_pfn_t, void *, va_list *);    // %M printer
void mg_pfn_iobuf(char ch, void *param);                   // iobuf printer

size_t mg_vxprintf(void (*)(char, void *), void *, const char *fmt, va_list *);
size_t mg_xprintf(void (*fn)(char, void *), void *, const char *fmt, ...);
size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap);
size_t mg_snprintf(char *, size_t, const char *fmt, ...);
char  *mg_vmprintf(const char *fmt, va_list *ap);
char  *mg_mprintf(const char *fmt, ...);

enum {
    MG_LL_NONE,
    MG_LL_ERROR,
    MG_LL_INFO,
    MG_LL_DEBUG,
    MG_LL_VERBOSE
};

void mg_log(const char *fmt, ...);
bool mg_log_prefix(int ll, const char *file, int line, const char *fname);
void mg_log_set(int log_level);
void mg_hexdump(const void *buf, size_t len);
void mg_log_set_fn(mg_pfn_t fn, void *param);

//#if MG_ENABLE_LOG
//#define MG_LOG(level, args)                                                \
//  do {                                                                     \
//    if (mg_log_prefix((level), __FILE__, __LINE__, __func__)) mg_log args; \
//  } while (0)
//#else
//#define MG_LOG(level, args) \
//  do {                      \
//    if (0) mg_log args;     \
//  } while (0)
//#endif

struct mg_timer {
    unsigned long id;           // Timer ID
    uint64_t      period_ms;    // Timer period in milliseconds
    uint64_t      expire;       // Expiration timestamp in milliseconds
    unsigned      flags;        // Possible flags values below
#define MG_TIMER_ONCE    0      // Call function once
#define MG_TIMER_REPEAT  1      // Call function periodically
#define MG_TIMER_RUN_NOW 2      // Call immediately when timer is set
    void (*fn)(void *);         // Function to call
    void            *arg;       // Function argument
    struct mg_timer *next;      // Linkage
};

void mg_timer_init(struct mg_timer **head,
                   struct mg_timer  *timer,
                   uint64_t          milliseconds,
                   unsigned          flags,
                   void (*fn)(void *),
                   void *arg);
void mg_timer_free(struct mg_timer **head, struct mg_timer *);
void mg_timer_poll(struct mg_timer **head, uint64_t new_ms);
bool mg_timer_expired(uint64_t *expiration, uint64_t period, uint64_t now);

enum {
    MG_FS_READ  = 1,
    MG_FS_WRITE = 2,
    MG_FS_DIR   = 4
};

// Filesystem API functions
// st() returns MG_FS_* flags and populates file size and modification time
// ls() calls fn() for every directory entry, allowing to list a directory
//
// NOTE: UNIX-style shorthand names for the API functions are deliberately
// chosen to avoid conflicts with some libraries that make macros for e.g.
// stat(), write(), read() calls.
struct mg_fs {
    int (*st)(const char *path, size_t *size, time_t *mtime);    // stat file
    void (*ls)(const char *path, void (*fn)(const char *, void *), void *);
    void *(*op)(const char *path, int flags);               // Open file
    void (*cl)(void *fd);                                   // Close file
    size_t (*rd)(void *fd, void *buf, size_t len);          // Read file
    size_t (*wr)(void *fd, const void *buf, size_t len);    // Write file
    size_t (*sk)(void *fd, size_t offset);                  // Set file position
    bool (*mv)(const char *from, const char *to);           // Rename file
    bool (*rm)(const char *path);                           // Delete file
    bool (*mkd)(const char *path);                          // Create directory
};

extern struct mg_fs mg_fs_posix;     // POSIX open/close/read/write/seek
extern struct mg_fs mg_fs_packed;    // Packed FS, see examples/device-dashboard
extern struct mg_fs mg_fs_fat;       // FAT FS

// File descriptor
struct mg_fd {
    void         *fd;
    struct mg_fs *fs;
};

struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags);
void          mg_fs_close(struct mg_fd *fd);
char         *mg_file_read(struct mg_fs *fs, const char *path, size_t *size);
bool          mg_file_write(struct mg_fs *fs, const char *path, const void *, size_t);
bool          mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...);

void     mg_random(void *buf, size_t len);
char    *mg_random_str(char *buf, size_t len);
uint16_t mg_ntohs(uint16_t net);
uint32_t mg_ntohl(uint32_t net);
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
uint64_t mg_millis(void);

#define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x)

#define MG_U32(a, b, c, d) \
    (((uint32_t)((a)&255) << 24) | ((uint32_t)((b)&255) << 16) | ((uint32_t)((c)&255) << 8) | (uint32_t)((d)&255))

// Linked list management macros
#define LIST_ADD_HEAD(type_, head_, elem_) \
    do {                                   \
        (elem_)->next = (*head_);          \
        *(head_)      = (elem_);           \
    } while (0)

#define LIST_ADD_TAIL(type_, head_, elem_) \
    do {                                   \
        type_ **h = head_;                 \
        while (*h != NULL)                 \
            h = &(*h)->next;               \
        *h = (elem_);                      \
    } while (0)

#define LIST_DELETE(type_, head_, elem_) \
    do {                                 \
        type_ **h = head_;               \
        while (*h != (elem_))            \
            h = &(*h)->next;             \
        *h = (elem_)->next;              \
    } while (0)

unsigned short mg_url_port(const char *url);
int            mg_url_is_ssl(const char *url);
struct mg_str  mg_url_host(const char *url);
struct mg_str  mg_url_user(const char *url);
struct mg_str  mg_url_pass(const char *url);
const char    *mg_url_uri(const char *url);

struct mg_iobuf {
    unsigned char *buf;      // Pointer to stored data
    size_t         size;     // Total size available
    size_t         len;      // Current number of bytes
    size_t         align;    // Alignment during allocation
};

int    mg_iobuf_init(struct mg_iobuf *, size_t, size_t);
int    mg_iobuf_resize(struct mg_iobuf *, size_t);
void   mg_iobuf_free(struct mg_iobuf *);
size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t);
size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len);

int mg_base64_update(unsigned char p, char *to, int len);
int mg_base64_final(char *to, int len);
int mg_base64_encode(const unsigned char *p, int n, char *to);
int mg_base64_decode(const char *src, int n, char *dst);

typedef struct {
    uint32_t      buf[4];
    uint32_t      bits[2];
    unsigned char in[64];
} mg_md5_ctx;

void mg_md5_init(mg_md5_ctx *c);
void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len);
void mg_md5_final(mg_md5_ctx *c, unsigned char[16]);

typedef struct {
    uint32_t      state[5];
    uint32_t      count[2];
    unsigned char buffer[64];
} mg_sha1_ctx;

void mg_sha1_init(mg_sha1_ctx *);
void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len);
void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *);

struct mg_connection;
typedef void (*mg_event_handler_t)(struct mg_connection *, int ev, void *ev_data, void *fn_data);
void mg_call(struct mg_connection *c, int ev, void *ev_data);
void mg_error(struct mg_connection *c, const char *fmt, ...);

enum {
    MG_EV_ERROR,         // Error                        char *error_message
    MG_EV_OPEN,          // Connection created           NULL
    MG_EV_POLL,          // mg_mgr_poll iteration        uint64_t *uptime_millis
    MG_EV_RESOLVE,       // Host name is resolved        NULL
    MG_EV_CONNECT,       // Connection established       NULL
    MG_EV_ACCEPT,        // Connection accepted          NULL
    MG_EV_TLS_HS,        // TLS handshake succeeded      NULL
    MG_EV_READ,          // Data received from socket    long *bytes_read
    MG_EV_WRITE,         // Data written to socket       long *bytes_written
    MG_EV_CLOSE,         // Connection closed            NULL
    MG_EV_HTTP_MSG,      // HTTP request/response        struct mg_http_message *
    MG_EV_HTTP_CHUNK,    // HTTP chunk (partial msg)     struct mg_http_message *
    MG_EV_WS_OPEN,       // Websocket handshake done     struct mg_http_message *
    MG_EV_WS_MSG,        // Websocket msg, text or bin   struct mg_ws_message *
    MG_EV_WS_CTL,        // Websocket control msg        struct mg_ws_message *
    MG_EV_MQTT_CMD,      // MQTT low-level command       struct mg_mqtt_message *
    MG_EV_MQTT_MSG,      // MQTT PUBLISH received        struct mg_mqtt_message *
    MG_EV_MQTT_OPEN,     // MQTT CONNACK received        int *connack_status_code
    MG_EV_SNTP_TIME,     // SNTP time received           uint64_t *epoch_millis
    MG_EV_USER           // Starting ID for user events
};

struct mg_dns {
    const char           *url;    // DNS server URL
    struct mg_connection *c;      // DNS server connection
};

struct mg_addr {
    uint16_t port;       // TCP or UDP port in network byte order
    uint32_t ip;         // IP address in network byte order
    uint8_t  ip6[16];    // IPv6 address
    bool     is_ip6;     // True when address is IPv6 address
};

struct mg_mgr {
    struct mg_connection *conns;                  // List of active connections
    struct mg_dns         dns4;                   // DNS for IPv4
    struct mg_dns         dns6;                   // DNS for IPv6
    int                   dnstimeout;             // DNS resolve timeout in milliseconds
    bool                  use_dns6;               // Use DNS6 server by default, see #1532
    unsigned long         nextid;                 // Next connection ID
    unsigned long         timerid;                // Next timer ID
    void                 *userdata;               // Arbitrary user data pointer
    uint16_t              mqtt_id;                // MQTT IDs for pub/sub
    void                 *active_dns_requests;    // DNS requests in progress
    struct mg_timer      *timers;                 // Active timers
    int                   epoll_fd;               // Used when MG_EPOLL_ENABLE=1
    void                 *priv;                   // Used by the MIP stack
    size_t                extraconnsize;          // Used by the MIP stack
#if MG_ENABLE_FREERTOS_TCP
    SocketSet_t ss;    // NOTE(lsm): referenced from socket struct
#endif
};

struct mg_connection {
    struct mg_connection *next;                  // Linkage in struct mg_mgr :: connections
    struct mg_mgr        *mgr;                   // Our container
    struct mg_addr        loc;                   // Local address
    struct mg_addr        rem;                   // Remote address
    void                 *fd;                    // Connected socket, or LWIP data
    unsigned long         id;                    // Auto-incrementing unique connection ID
    struct mg_iobuf       recv;                  // Incoming data
    struct mg_iobuf       send;                  // Outgoing data
    mg_event_handler_t    fn;                    // User-specified event handler function
    void                 *fn_data;               // User-specified function parameter
    mg_event_handler_t    pfn;                   // Protocol-specific handler function
    void                 *pfn_data;              // Protocol-specific function parameter
    char                  data[MG_DATA_SIZE];    // Arbitrary connection data
    void                 *tls;                   // TLS specific data
    unsigned              is_listening : 1;      // Listening connection
    unsigned              is_client : 1;         // Outbound (client) connection
    unsigned              is_accepted : 1;       // Accepted (server) connection
    unsigned              is_resolving : 1;      // Non-blocking DNS resolution is in progress
    unsigned              is_connecting : 1;     // Non-blocking connect is in progress
    unsigned              is_tls : 1;            // TLS-enabled connection
    unsigned              is_tls_hs : 1;         // TLS handshake is in progress
    unsigned              is_udp : 1;            // UDP connection
    unsigned              is_websocket : 1;      // WebSocket connection
    unsigned              is_mqtt5 : 1;          // For MQTT connection, v5 indicator
    unsigned              is_hexdumping : 1;     // Hexdump in/out traffic
    unsigned              is_draining : 1;       // Send remaining data, then close and free
    unsigned              is_closing : 1;        // Close and free the connection immediately
    unsigned              is_full : 1;           // Stop reads, until cleared
    unsigned              is_resp : 1;           // Response is still being generated
    unsigned              is_readable : 1;       // Connection is ready to read
    unsigned              is_writable : 1;       // Connection is ready to write
};

void mg_mgr_poll(struct mg_mgr *, int ms);
void mg_mgr_init(struct mg_mgr *);
void mg_mgr_free(struct mg_mgr *);

struct mg_connection *mg_listen(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data);
struct mg_connection *mg_connect(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data);
struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, mg_event_handler_t fn, void *fn_data);
void                  mg_connect_resolved(struct mg_connection *);
bool                  mg_send(struct mg_connection *, const void *, size_t);
size_t                mg_printf(struct mg_connection *, const char *fmt, ...);
size_t                mg_vprintf(struct mg_connection *, const char *fmt, va_list *ap);
bool                  mg_aton(struct mg_str str, struct mg_addr *addr);
int                   mg_mkpipe(struct mg_mgr *, mg_event_handler_t, void *, bool udp);

// These functions are used to integrate with custom network stacks
struct mg_connection *mg_alloc_conn(struct mg_mgr *);
void                  mg_close_conn(struct mg_connection *c);
bool                  mg_open_listener(struct mg_connection *c, const char *url);
struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, unsigned flags, void (*fn)(void *), void *arg);

// Low-level IO primives used by TLS layer
enum {
    MG_IO_ERR   = -1,
    MG_IO_WAIT  = -2,
    MG_IO_RESET = -3
};

long mg_io_send(struct mg_connection *c, const void *buf, size_t len);
long mg_io_recv(struct mg_connection *c, void *buf, size_t len);

struct mg_http_header {
    struct mg_str name;     // Header name
    struct mg_str value;    // Header value
};

struct mg_http_message {
    struct mg_str         method, uri, query, proto;       // Request/response line
    struct mg_http_header headers[MG_MAX_HTTP_HEADERS];    // Headers
    struct mg_str         body;                            // Body
    struct mg_str         head;                            // Request + headers
    struct mg_str         chunk;                           // Chunk for chunked encoding,  or partial body
    struct mg_str         message;                         // Request + headers + body
};

// Parameter for mg_http_serve_dir()
struct mg_http_serve_opts {
    const char   *root_dir;         // Web root directory, must be non-NULL
    const char   *ssi_pattern;      // SSI file name pattern, e.g. #.shtml
    const char   *extra_headers;    // Extra HTTP headers to add in responses
    const char   *mime_types;       // Extra mime types, ext1=type1,ext2=type2,..
    const char   *page404;          // Path to the 404 page, or NULL by default
    struct mg_fs *fs;               // Filesystem implementation. Use NULL for POSIX
};

// Parameter for mg_http_next_multipart
struct mg_http_part {
    struct mg_str name;        // Form field name
    struct mg_str filename;    // Filename for file uploads
    struct mg_str body;        // Part contents
};

int                   mg_http_parse(const char *s, size_t len, struct mg_http_message *);
int                   mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
void                  mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
void                  mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
void                  mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm);
struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data);
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url, mg_event_handler_t fn, void *fn_data);
void           mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm, const struct mg_http_serve_opts *);
void           mg_http_serve_file(struct mg_connection *,
                                  struct mg_http_message *hm,
                                  const char             *path,
                                  const struct mg_http_serve_opts *);
void           mg_http_reply(struct mg_connection *, int status_code, const char *headers, const char *body_fmt, ...);
struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name);
struct mg_str  mg_http_var(struct mg_str buf, struct mg_str name);
int            mg_http_get_var(const struct mg_str *, const char *name, char *, size_t);
int            mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
size_t         mg_url_encode(const char *s, size_t n, char *buf, size_t len);
void           mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t);
bool           mg_http_match_uri(const struct mg_http_message *, const char *glob);
long           mg_http_upload(struct mg_connection   *c,
                              struct mg_http_message *hm,
                              struct mg_fs           *fs,
                              const char             *path,
                              size_t                  max_size);
void           mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
struct mg_str  mg_http_get_header_var(struct mg_str s, struct mg_str v);
size_t         mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
int            mg_http_status(const struct mg_http_message *hm);
void           mg_hello(const char *url);

void mg_http_serve_ssi(struct mg_connection *c, const char *root, const char *fullpath);

struct mg_tls_opts {
    const char   *ca;         // CA certificate file. For both listeners and clients
    const char   *crl;        // Certificate Revocation List. For clients
    const char   *cert;       // Certificate
    const char   *certkey;    // Certificate key
    const char   *ciphers;    // Cipher list
    struct mg_str srvname;    // If not empty, enables server name verification
    struct mg_fs *fs;         // FS API for reading certificate files
};

void   mg_tls_init(struct mg_connection *, const struct mg_tls_opts *);
void   mg_tls_free(struct mg_connection *);
long   mg_tls_send(struct mg_connection *, const void *buf, size_t len);
long   mg_tls_recv(struct mg_connection *, void *buf, size_t len);
size_t mg_tls_pending(struct mg_connection *);
void   mg_tls_handshake(struct mg_connection *);

#if MG_ENABLE_MBEDTLS
#include <mbedtls/debug.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/ssl.h>

struct mg_tls {
    char               *cafile;    // CA certificate path
    mbedtls_x509_crt    ca;        // Parsed CA certificate
    mbedtls_x509_crt    cert;      // Parsed certificate
    mbedtls_ssl_context ssl;       // SSL/TLS context
    mbedtls_ssl_config  conf;      // SSL-TLS config
    mbedtls_pk_context  pk;        // Private key context
};
#endif

#if MG_ENABLE_OPENSSL

#include <openssl/err.h>
#include <openssl/ssl.h>

struct mg_tls {
    SSL_CTX *ctx;
    SSL     *ssl;
};
#endif

#define WEBSOCKET_OP_CONTINUE 0
#define WEBSOCKET_OP_TEXT     1
#define WEBSOCKET_OP_BINARY   2
#define WEBSOCKET_OP_CLOSE    8
#define WEBSOCKET_OP_PING     9
#define WEBSOCKET_OP_PONG     10

struct mg_ws_message {
    struct mg_str data;     // Websocket message data
    uint8_t       flags;    // Websocket message flags
};

struct mg_connection *mg_ws_connect(struct mg_mgr *,
                                    const char        *url,
                                    mg_event_handler_t fn,
                                    void              *fn_data,
                                    const char        *fmt,
                                    ...);
void                  mg_ws_upgrade(struct mg_connection *, struct mg_http_message *, const char *fmt, ...);
size_t                mg_ws_send(struct mg_connection *, const void *buf, size_t len, int op);
size_t                mg_ws_wrap(struct mg_connection *, size_t len, int op);
size_t                mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...);
size_t                mg_ws_vprintf(struct mg_connection *c, int op, const char *fmt, va_list *);

struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data);
void                  mg_sntp_request(struct mg_connection *c);
int64_t               mg_sntp_parse(const unsigned char *buf, size_t len);

#define MQTT_CMD_CONNECT     1
#define MQTT_CMD_CONNACK     2
#define MQTT_CMD_PUBLISH     3
#define MQTT_CMD_PUBACK      4
#define MQTT_CMD_PUBREC      5
#define MQTT_CMD_PUBREL      6
#define MQTT_CMD_PUBCOMP     7
#define MQTT_CMD_SUBSCRIBE   8
#define MQTT_CMD_SUBACK      9
#define MQTT_CMD_UNSUBSCRIBE 10
#define MQTT_CMD_UNSUBACK    11
#define MQTT_CMD_PINGREQ     12
#define MQTT_CMD_PINGRESP    13
#define MQTT_CMD_DISCONNECT  14
#define MQTT_CMD_AUTH        15

enum {
    MQTT_OK,
    MQTT_INCOMPLETE,
    MQTT_MALFORMED
};

struct mg_mqtt_opts {
    struct mg_str user;            // Username, can be empty
    struct mg_str pass;            // Password, can be empty
    struct mg_str client_id;       // Client ID
    struct mg_str will_topic;      // Will topic
    struct mg_str will_message;    // Will message
    uint8_t       will_qos;        // Will message quality of service
    uint8_t       version;         // Can be 4 (3.1.1), or 5. If 0, assume 4.
    uint16_t      keepalive;       // Keep-alive timer in seconds
    bool          will_retain;     // Retain last will
    bool          clean;           // Use clean session, 0 or 1
};

struct mg_mqtt_message {
    struct mg_str topic;    // Parsed topic
    struct mg_str data;     // Parsed message
    struct mg_str dgram;    // Whole MQTT datagram, including headers
    uint16_t      id;       // Set for PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH
    uint8_t       cmd;      // MQTT command, one of MQTT_CMD_*
    uint8_t       qos;      // Quality of service
    uint8_t       ack;      // Connack return code. 0 - success
};

struct mg_connection *mg_mqtt_connect(struct mg_mgr *,
                                      const char                *url,
                                      const struct mg_mqtt_opts *opts,
                                      mg_event_handler_t         fn,
                                      void                      *fn_data);
struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, mg_event_handler_t fn, void *fn_data);
void                  mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts);
void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic, struct mg_str data, int qos, bool retain);
void mg_mqtt_sub(struct mg_connection *, struct mg_str topic, int qos);
int  mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *);
void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags, uint32_t len);
void mg_mqtt_ping(struct mg_connection *);
void mg_mqtt_pong(struct mg_connection *);
void mg_mqtt_disconnect(struct mg_connection *);

// Mongoose sends DNS queries that contain only one question:
// either A (IPv4) or AAAA (IPv6) address lookup.
// Therefore, we expect zero or one answer.
// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
struct mg_dns_message {
    uint16_t       txnid;        // Transaction ID
    bool           resolved;     // Resolve successful, addr is set
    struct mg_addr addr;         // Resolved address
    char           name[256];    // Host name
};

struct mg_dns_header {
    uint16_t txnid;    // Transaction ID
    uint16_t flags;
    uint16_t num_questions;
    uint16_t num_answers;
    uint16_t num_authority_prs;
    uint16_t num_other_prs;
};

// DNS resource record
struct mg_dns_rr {
    uint16_t nlen;      // Name or pointer length
    uint16_t atype;     // Address type
    uint16_t aclass;    // Address class
    uint16_t alen;      // Address length
};

void   mg_resolve(struct mg_connection *, const char *url);
void   mg_resolve_cancel(struct mg_connection *);
bool   mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, bool is_question, struct mg_dns_rr *);

#ifndef MG_JSON_MAX_DEPTH
#define MG_JSON_MAX_DEPTH 30
#endif

// Error return values - negative. Successful returns are >= 0
enum {
    MG_JSON_TOO_DEEP  = -1,
    MG_JSON_INVALID   = -2,
    MG_JSON_NOT_FOUND = -3
};

int mg_json_get(struct mg_str json, const char *path, int *toklen);

bool  mg_json_get_num(struct mg_str json, const char *path, double *v);
bool  mg_json_get_bool(struct mg_str json, const char *path, bool *v);
long  mg_json_get_long(struct mg_str json, const char *path, long dflt);
char *mg_json_get_str(struct mg_str json, const char *path);
char *mg_json_get_hex(struct mg_str json, const char *path, int *len);
char *mg_json_get_b64(struct mg_str json, const char *path, int *len);

// JSON-RPC request descriptor
struct mg_rpc_req {
    struct mg_rpc **head;        // RPC handlers list head
    struct mg_rpc  *rpc;         // RPC handler being called
    mg_pfn_t        pfn;         // Response printing function
    void           *pfn_data;    // Response printing function data
    void           *req_data;    // Arbitrary request data
    struct mg_str   frame;       // Request, e.g. {"id":1,"method":"add","params":[1,2]}
};

// JSON-RPC method handler
struct mg_rpc {
    struct mg_rpc *next;                // Next in list
    struct mg_str  method;              // Method pattern
    void (*fn)(struct mg_rpc_req *);    // Handler function
    void *fn_data;                      // Handler function argument
};

void mg_rpc_add(struct mg_rpc **head,
                struct mg_str   method_pattern,
                void (*handler)(struct mg_rpc_req *),
                void *handler_data);
void mg_rpc_del(struct mg_rpc **head, void (*handler)(struct mg_rpc_req *));
void mg_rpc_process(struct mg_rpc_req *);

// Helper functions to print result or error frame
void mg_rpc_ok(struct mg_rpc_req *, const char *fmt, ...);
void mg_rpc_vok(struct mg_rpc_req *, const char *fmt, va_list *ap);
void mg_rpc_err(struct mg_rpc_req *, int code, const char *fmt, ...);
void mg_rpc_verr(struct mg_rpc_req *, int code, const char *fmt, va_list *);
void mg_rpc_list(struct mg_rpc_req *r);

struct mip_if;    // MIP network interface

struct mip_driver {
    bool (*init)(struct mip_if *);                           // Initialise driver
    size_t (*tx)(const void *, size_t, struct mip_if *);     // Transmit frame
    size_t (*rx)(void *buf, size_t len, struct mip_if *);    // Receive frame (poll)
    bool (*up)(struct mip_if *);                             // Up/down status
};

// Receive queue - single producer, single consumer queue.  Interrupt-based
// drivers copy received frames to the queue in interrupt context. mip_poll()
// function runs in event loop context, reads from the queue
struct queue {
    uint8_t        *buf;
    size_t          len;
    volatile size_t tail, head;
};

#define MIP_ARP_ENTRIES 5                             // Number of ARP cache entries. Maximum 21
#define MIP_ARP_CS      (2 + 12 * MIP_ARP_ENTRIES)    // ARP cache size

// Network interface
struct mip_if {
    uint8_t            mac[6];                // MAC address. Must be set to a valid MAC
    uint32_t           ip, mask, gw;          // IP address, mask, default gateway
    struct mg_str      rx;                    // Output (TX) buffer
    struct mg_str      tx;                    // Input (RX) buffer
    bool               enable_dhcp_client;    // Enable DCHP client
    bool               enable_dhcp_server;    // Enable DCHP server
    struct mip_driver *driver;                // Low level driver
    void              *driver_data;           // Driver-specific data
    struct mg_mgr     *mgr;                   // Mongoose event manager
    struct queue       queue;                 // Set queue.len for interrupt based drivers

    // Internal state, user can use it but should not change it
    uint64_t now;                      // Current time
    uint64_t timer_1000ms;             // 1000 ms timer: for DHCP and link state
    uint64_t lease_expire;             // Lease expiration time
    uint8_t  arp_cache[MIP_ARP_CS];    // Each entry is 12 bytes
    uint16_t eport;                    // Next ephemeral port
    uint16_t dropped;                  // Number of dropped frames
    uint8_t  state;                    // Current state
#define MIP_STATE_DOWN  0              // Interface is down
#define MIP_STATE_UP    1              // Interface is up
#define MIP_STATE_READY 2              // Interface is up and has IP
};

void   mip_init(struct mg_mgr *, struct mip_if *);
void   mip_free(struct mip_if *);
void   mip_qwrite(void *buf, size_t len, struct mip_if *ifp);
size_t mip_qread(void *buf, struct mip_if *ifp);
// conveniency rx function for IRQ-driven drivers
size_t mip_driver_rx(void *buf, size_t len, struct mip_if *ifp);

extern struct mip_driver mip_driver_stm32;
extern struct mip_driver mip_driver_w5500;
extern struct mip_driver mip_driver_tm4c;

// Drivers that require SPI, can use this SPI abstraction
struct mip_spi {
    void *spi;                          // Opaque SPI bus descriptor
    void (*begin)(void *);              // SPI begin: slave select low
    void (*end)(void *);                // SPI end: slave select high
    uint8_t (*txn)(void *, uint8_t);    // SPI transaction: write 1 byte, read reply
};

#ifdef MIP_QPROFILE
enum {
    QP_IRQTRIGGERED = 0,    // payload is number of interrupts so far
    QP_FRAMEPUSHED,         // available space in the frame queue
    QP_FRAMEPOPPED,         // available space in the frame queue
    QP_FRAMEDONE,           // available space in the frame queue
    QP_FRAMEDROPPED,        // number of dropped frames
    QP_QUEUEOVF             // profiling queue is full, payload is number of frame drops
};

void qp_mark(unsigned int type, int len);
void qp_log(void);    // timestamp, type, payload
void qp_init(void);
#else
#define qp_mark(a, b)
#endif

struct mip_driver_stm32_data {
    // MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
    //    HCLK range    DIVIDER    mdc_cr VALUE
    //    -------------------------------------
    //                                -1  <-- tell driver to guess the value
    //    60-100 MHz    HCLK/42        0
    //    100-150 MHz   HCLK/62        1
    //    20-35 MHz     HCLK/16        2
    //    35-60 MHz     HCLK/26        3
    //    150-216 MHz   HCLK/102       4  <-- value for Nucleo-F* on max speed
    //    216-310 MHz   HCLK/124       5
    //    110, 111 Reserved
    int mdc_cr;    // Valid values: -1, 0, 1, 2, 3, 4, 5
};

struct mip_driver_tm4c_data {
    // MDC clock divider. MDC clock is derived from SYSCLK, must not exceed 2.5MHz
    //    SYSCLK range   DIVIDER   mdc_cr VALUE
    //    -------------------------------------
    //                                -1  <-- tell driver to guess the value
    //    60-100 MHz    SYSCLK/42      0
    //    100-150 MHz   SYSCLK/62      1  <-- value for EK-TM4C129* on max speed
    //    20-35 MHz     SYSCLK/16      2
    //    35-60 MHz     SYSCLK/26      3
    //    0x4-0xF Reserved
    int mdc_cr;    // Valid values: -1, 0, 1, 2, 3
};

#ifdef __cplusplus
}
#endif
#endif    // MONGOOSE_H