#include "lwip/opt.h"
#include "lwip/arch.h"
#include "lwip/sys.h"
#include "osal.h"

/* Initialize the system architecture */
void sys_init(void)
{
    /* OSAL should be initialized before calling this */
}

/* Semaphore functions */
err_t sys_sem_new(sys_sem_t *sem, u8_t count)
{
    *sem = osal_sem_create("lwip_sem", count);
    if (*sem == NULL)
    {
        return ERR_MEM;
    }
    return ERR_OK;
}

void sys_sem_free(sys_sem_t *sem)
{
    if (sem != NULL && *sem != NULL)
    {
        osal_sem_delete(*sem);
        *sem = NULL;
    }
}

void sys_sem_signal(sys_sem_t *sem)
{
    if (sem != NULL && *sem != NULL)
    {
        osal_sem_release(*sem);
    }
}

u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
    osal_tick_t start_tick = osal_tick_get();
    osal_err_t ret;
    osal_int32_t osal_timeout;

    if (timeout == 0)
    {
        osal_timeout = -1; /* Wait forever */
    }
    else
    {
        osal_timeout = (osal_int32_t)timeout;
    }

    ret = osal_sem_take(*sem, osal_timeout);

    if (ret == OSAL_OK)
    {
        return osal_tick_get() - start_tick;
    }
    else
    {
        return SYS_ARCH_TIMEOUT;
    }
}

/* Check if a sem is valid */
int sys_sem_valid(sys_sem_t *sem)
{
    if (sem != NULL && *sem != NULL)
        return 1;
    return 0;
}

/* Set a sem to invalid */
void sys_sem_set_invalid(sys_sem_t *sem)
{
    if (sem != NULL)
        *sem = NULL;
}

/* Mutex functions */
err_t sys_mutex_new(sys_mutex_t *mutex)
{
    *mutex = osal_mutex_create("lwip_mutex");
    if (*mutex == NULL)
    {
        return ERR_MEM;
    }
    return ERR_OK;
}

void sys_mutex_free(sys_mutex_t *mutex)
{
    if (mutex != NULL && *mutex != NULL)
    {
        osal_mutex_delete(*mutex);
        *mutex = NULL;
    }
}

void sys_mutex_lock(sys_mutex_t *mutex)
{
    if (mutex != NULL && *mutex != NULL)
    {
        osal_mutex_take(*mutex, -1);
    }
}

void sys_mutex_unlock(sys_mutex_t *mutex)
{
    if (mutex != NULL && *mutex != NULL)
    {
        osal_mutex_release(*mutex);
    }
}

/* Mailbox functions */
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{
    /* LwIP messages are pointers */
    *mbox = osal_mq_create("lwip_mbox", sizeof(void*), size);
    if (*mbox == NULL)
    {
        return ERR_MEM;
    }
    return ERR_OK;
}

void sys_mbox_free(sys_mbox_t *mbox)
{
    if (mbox != NULL && *mbox != NULL)
    {
        osal_mq_delete(*mbox);
        *mbox = NULL;
    }
}

void sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
    if (mbox != NULL && *mbox != NULL)
    {
        osal_mq_send(*mbox, &msg, sizeof(void*));
    }
}

err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
    /* OSAL MQ doesn't strictly separate blocking/non-blocking send in current API wrapper, 
       but we assume osal_mq_send is blocking if full. 
       However, sys_mbox_trypost expects non-blocking.
       For now, we just call send. If you need true non-blocking, OSAL needs an update. 
       Assuming OSAL queue has space.
    */
    /* TODO: Implement non-blocking send in OSAL if needed */
    if (mbox != NULL && *mbox != NULL)
    {
        if (osal_mq_send(*mbox, &msg, sizeof(void*)) == OSAL_OK)
        {
            return ERR_OK;
        }
    }
    return ERR_MEM;
}

err_t sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
{
    return sys_mbox_trypost(mbox, msg);
}

u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
    osal_tick_t start_tick = osal_tick_get();
    osal_err_t ret;
    osal_int32_t osal_timeout;
    void *dummy_msg;

    if (msg == NULL)
    {
        msg = &dummy_msg;
    }

    if (timeout == 0)
    {
        osal_timeout = -1;
    }
    else
    {
        osal_timeout = (osal_int32_t)timeout;
    }

    ret = osal_mq_recv(*mbox, msg, sizeof(void*), osal_timeout);

    if (ret == OSAL_OK)
    {
        return osal_tick_get() - start_tick;
    }
    else
    {
        return SYS_ARCH_TIMEOUT;
    }
}

u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
    osal_err_t ret;
    void *dummy_msg;

    if (msg == NULL)
    {
        msg = &dummy_msg;
    }

    ret = osal_mq_recv(*mbox, msg, sizeof(void*), 0); /* 0 timeout */

    if (ret == OSAL_OK)
    {
        return 0;
    }
    else
    {
        return SYS_MBOX_EMPTY;
    }
}

/* Check if an mbox is valid */
int sys_mbox_valid(sys_mbox_t *mbox)
{
    if (mbox != NULL && *mbox != NULL)
        return 1;
    return 0;
}

/* Set an mbox to invalid */
void sys_mbox_set_invalid(sys_mbox_t *mbox)
{
    if (mbox != NULL)
        *mbox = NULL;
}

/* Thread functions */
sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
{
    osal_thread_t t;

    t = osal_thread_create(name, (osal_thread_entry)thread, arg, stacksize, (osal_priority_t)prio);
    
    if (t != NULL)
    {
        osal_thread_start(t);
    }
    
    return t;
}

/* Time functions */
u32_t sys_now(void)
{
    return osal_tick_get();
}
