#include "os/os.h"
#include "os_util.h"
#include "semphr.h"

os_state os_sem_create(os_sem_t *sem, size_t init_value, size_t max_value)
{
    OS_ASS_HDL(!os_sem_is_valid(sem), sem->handle);

    sem->handle = xSemaphoreCreateCounting(max_value, init_value);
    if (sem->handle == NULL)
    {
        OS_ERR("err %p\r\n", sem->handle);
        return OS_FAIL;
    }

    return OS_OK;
}

os_state os_sem_delete(os_sem_t *sem)
{
    OS_ASS_HDL(os_sem_is_valid(sem), sem->handle);

    vSemaphoreDelete(sem->handle);
    sem->handle = NULL;
    return OS_OK;
}

os_state os_sem_take(os_sem_t *sem, os_time_t wait_ms)
{
    BaseType_t ret;
    BaseType_t taskWoken;

    OS_ASS_HDL(os_sem_is_valid(sem), sem->handle);

    if (os_is_isr_context())
    {
        if (wait_ms != 0)
        {
            OS_ERR("%s() in ISR, wait %u ms\n", __func__, wait_ms);
            return OS_E_ISR;
        }
        taskWoken = pdFALSE;
        ret = xSemaphoreTakeFromISR(sem->handle, &taskWoken);
        if (ret != pdPASS)
        {
            OS_DBG("%s() fail @ %d\n", __func__, __LINE__);
            return OS_E_TIMEOUT;
        }
        portYIELD_FROM_ISR(taskWoken);
    }
    else
    {
        ret = xSemaphoreTake(sem->handle, os_calc_msec_to_ticks(wait_ms));
        if (ret != pdPASS)
        {
            OS_DBG("%s() fail @ %d, %u ms\n", __func__, __LINE__, wait_ms);
            return OS_E_TIMEOUT;
        }
    }

    return OS_OK;
}

os_state os_sem_release(os_sem_t *sem)
{
    BaseType_t ret;
    BaseType_t taskWoken;

    OS_ASS_HDL(os_sem_is_valid(sem), sem->handle);

    if (os_is_isr_context())
    {
        taskWoken = pdFALSE;
        ret = xSemaphoreGiveFromISR(sem->handle, &taskWoken);
        if (ret != pdPASS)
        {
            OS_DBG("%s() fail @ %d\n", __func__, __LINE__);
            return OS_FAIL;
        }
        portYIELD_FROM_ISR(taskWoken);
    }
    else
    {
        ret = xSemaphoreGive(sem->handle);
        if (ret != pdPASS)
        {
            OS_DBG("%s() fail @ %d\n", __func__, __LINE__);
            return OS_FAIL;
        }
    }

    return OS_OK;
}

bool os_sem_is_valid(os_sem_t *sem)
{
    if (sem && sem->handle)
    {
        return true;
    }
    else
    {
        return false;
    }
}
