/**
 * @file sys_arch.h
 * @brief LwIP system architecture for FreeRTOS
 */

#ifndef __SYS_ARCH_H__
#define __SYS_ARCH_H__

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "lwip/arch.h"
#include <stdlib.h>

/*
 * FreeRTOSConfig.h currently injects lwIP-related helper macros globally.
 * Those overrides break the normal lwIP sys.h/sys_arch contract by replacing
 * function declarations such as sys_now() with object-like macros.
 * Undefine them here so lwIP uses the port's real sys_arch implementation.
 */
#ifdef sys_now
#undef sys_now
#endif

#ifdef sys_arch_protect
#undef sys_arch_protect
#endif

#ifdef sys_arch_unprotect
#undef sys_arch_unprotect
#endif

#ifdef SYS_ARCH_DECL_PROTECT
#undef SYS_ARCH_DECL_PROTECT
#endif

#ifdef SYS_ARCH_PROTECT
#undef SYS_ARCH_PROTECT
#endif

#ifdef SYS_ARCH_UNPROTECT
#undef SYS_ARCH_UNPROTECT
#endif

#ifdef __cplusplus
extern "C" {
#endif

/* Semaphore type */
typedef SemaphoreHandle_t sys_sem_t;

/* Mutex type */
typedef SemaphoreHandle_t sys_mutex_t;

/* Mailbox (message queue) type */
typedef QueueHandle_t sys_mbox_t;

/* Thread type */
typedef TaskHandle_t sys_thread_t;

/* Protection level type */
typedef u32_t sys_prot_t;

/* Null values */
#define SYS_SEM_NULL    ((sys_sem_t)NULL)
#define SYS_MBOX_NULL   ((sys_mbox_t)NULL)
#define SYS_MUTEX_NULL  ((sys_mutex_t)NULL)

/* Use one per-thread semaphore for lwIP netconn/socket API calls. */
#define LWIP_NETCONN_THREAD_SEM_TLS_INDEX  0
#define LWIP_NETCONN_THREAD_SEM_GET() \
    ((sys_sem_t *)pvTaskGetThreadLocalStoragePointer(NULL, LWIP_NETCONN_THREAD_SEM_TLS_INDEX))
#define LWIP_NETCONN_THREAD_SEM_ALLOC()                                             \
    do {                                                                            \
        sys_sem_t *sem = (sys_sem_t *)mem_malloc(sizeof(sys_sem_t));                \
        if (sem != NULL) {                                                          \
            *sem = SYS_SEM_NULL;                                                    \
            if (sys_sem_new(sem, 0) == ERR_OK) {                                    \
                vTaskSetThreadLocalStoragePointer(NULL,                             \
                                                  LWIP_NETCONN_THREAD_SEM_TLS_INDEX,\
                                                  sem);                             \
            } else {                                                                \
                mem_free(sem);                                                      \
            }                                                                       \
        }                                                                           \
    } while (0)
#define LWIP_NETCONN_THREAD_SEM_FREE()                                              \
    do {                                                                            \
        sys_sem_t *sem = LWIP_NETCONN_THREAD_SEM_GET();                             \
        if (sem != NULL) {                                                          \
            sys_sem_free(sem);                                                      \
            mem_free(sem);                                                          \
            vTaskSetThreadLocalStoragePointer(NULL,                                 \
                                              LWIP_NETCONN_THREAD_SEM_TLS_INDEX,    \
                                              NULL);                                \
        }                                                                           \
    } while (0)

/* Check if semaphore/mbox is valid */
#define sys_sem_valid(sem)      ((sem) != NULL && (*(sem)) != SYS_SEM_NULL)
#define sys_sem_set_invalid(sem) do { if ((sem) != NULL) { *(sem) = SYS_SEM_NULL; } } while(0)

#define sys_mbox_valid(mbox)    ((mbox) != NULL && (*(mbox)) != SYS_MBOX_NULL)
#define sys_mbox_set_invalid(mbox) do { if ((mbox) != NULL) { *(mbox) = SYS_MBOX_NULL; } } while(0)

#define sys_mutex_valid(mutex)  ((mutex) != NULL && (*(mutex)) != SYS_MUTEX_NULL)
#define sys_mutex_set_invalid(mutex) do { if ((mutex) != NULL) { *(mutex) = SYS_MUTEX_NULL; } } while(0)

/* System initialization */
void sys_init(void);

/* Get current time in milliseconds */
u32_t sys_now(void);

#ifdef __cplusplus
}
#endif

#endif /* __SYS_ARCH_H__ */
