/**
 * @file tkl_semaphore.c
 * @brief this file was auto-generated by tuyaos v&v tools, developer can add implements between BEGIN and END
 *
 * @warning: changes between user 'BEGIN' and 'END' will be keeped when run tuyaos v&v tools
 *           changes in other place will be overwrited and lost
 *
 * @copyright Copyright 2020-2021 Tuya Inc. All Rights Reserved.
 *
 */

// --- BEGIN: user defines and implements ---
#include "tkl_semaphore.h"
#include "tkl_memory.h"
#include "tuya_error_code.h"
#include <semaphore.h>
#include <time.h>
#include <errno.h>

typedef struct {
    sem_t sem;
} TKL_SEM_MANAGE, *P_TKL_SEM_MANAGE;
// --- END: user defines and implements ---

/**
 * @brief Create semaphore
 *
 * @param[out] handle: semaphore handle
 * @param[in] sem_cnt: semaphore init count
 * @param[in] sem_max: semaphore max count
 *
 * @note This API is used for creating semaphore.
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_semaphore_create_init(TKL_SEM_HANDLE *handle, uint32_t sem_cnt, uint32_t sem_max)
{
    // --- BEGIN: user implements ---
    if (!handle) {
        return OPRT_INVALID_PARM;
    }

    P_TKL_SEM_MANAGE sem_manage;
    sem_manage = (P_TKL_SEM_MANAGE)tkl_system_malloc(sizeof(TKL_SEM_MANAGE));
    if (sem_manage == NULL) {
        return OPRT_MALLOC_FAILED;
    }

    int ret;
    ret = sem_init(&sem_manage->sem, 0, sem_cnt);
    if (ret != 0) {
        tkl_system_free(sem_manage);
        *handle = NULL;
        return ret;
    }

    *handle = (TKL_SEM_HANDLE)sem_manage;
    return OPRT_OK;
    // --- END: user implements ---
}

/**
 * @brief Wait semaphore
 *
 * @param[in] handle: semaphore handle
 * @param[in] timeout: wait timeout, SEM_WAIT_FOREVER means wait until get semaphore
 *
 * @note This API is used for waiting semaphore.
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_semaphore_wait(const TKL_SEM_HANDLE handle, uint32_t timeout)
{
    // --- BEGIN: user implements ---
    if (!handle) {
        return OPRT_INVALID_PARM;
    }

    P_TKL_SEM_MANAGE sem_manage;
    sem_manage = (P_TKL_SEM_MANAGE)handle;

    int ret;
    if (timeout == TKL_SEM_WAIT_FOREVER) {
        ret = sem_wait(&sem_manage->sem);
    } else {
        struct timespec ts = {0, 0};

        clock_gettime(CLOCK_REALTIME, &ts);
        ts.tv_sec += (timeout / 1000);
        ts.tv_nsec += ((timeout % 1000) * 1000000);
        if (ts.tv_nsec >= 1000000000) {
            ts.tv_sec += ts.tv_nsec / 1000000000;
            ts.tv_nsec = ts.tv_nsec % 1000000000;
        }

        // ret = sem_timedwait(&pSemManage->sem, &ts);
        while ((ret = sem_timedwait(&sem_manage->sem, &ts)) == -1 && errno == EINTR) {
            continue; /* Restart if interrupted by handler */
        }
    }

    if (0 != ret) {
        return OPRT_OS_ADAPTER_SEM_WAIT_FAILED;
    }

    return OPRT_OK;
    // --- END: user implements ---
}

/**
 * @brief Post semaphore
 *
 * @param[in] handle: semaphore handle
 *
 * @note This API is used for posting semaphore.
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_semaphore_post(const TKL_SEM_HANDLE handle)
{
    // --- BEGIN: user implements ---
    if (!handle) {
        return OPRT_INVALID_PARM;
    }

    P_TKL_SEM_MANAGE sem_manage;
    sem_manage = (P_TKL_SEM_MANAGE)handle;

    int ret;
    ret = sem_post(&(sem_manage->sem));
    if (ret != 0) {
        return OPRT_OS_ADAPTER_SEM_POST_FAILED;
    }

    return OPRT_OK;
    // --- END: user implements ---
}

/**
 * @brief Release semaphore
 *
 * @param[in] handle: semaphore handle
 *
 * @note This API is used for releasing semaphore.
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_semaphore_release(const TKL_SEM_HANDLE handle)
{
    // --- BEGIN: user implements ---
    if (!handle) {
        return OPRT_INVALID_PARM;
    }

    P_TKL_SEM_MANAGE sem_manage;
    sem_manage = (P_TKL_SEM_MANAGE)handle;

    int ret;
    ret = sem_destroy(&(sem_manage->sem));
    tkl_system_free(handle); // 释放信号量管理结构
    if (ret != 0) {
        return OPRT_OS_ADAPTER_SEM_RELEASE_FAILED;
    }

    return OPRT_OK;
    // --- END: user implements ---
}
