/**
 * @file tkl_gpio.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_gpio.h"
#include "tuya_slist.h"
#include <stdlib.h>
#include <pthread.h>
#include <string.h>

typedef struct {
    uint32_t pin_id;
    TUYA_GPIO_BASE_CFG_T base_cfg;
    TUYA_GPIO_IRQ_T irq_cfg;
    BOOL_T irq_enable;
    BOOL_T irq_trigger;
    SLIST_HEAD node;
} PIN_T;

SLIST_HEAD(pin_list);

/*=================================外部触发中断=======================================*/

OPERATE_RET mock_gpio_irq_trigger(uint32_t pin_id, TUYA_GPIO_IRQ_E mode, TUYA_GPIO_LEVEL_E level)
{
    PIN_T *pin_node;
    SLIST_HEAD *pos = NULL;

    SLIST_FOR_EACH_ENTRY(pin_node, PIN_T, pos, &pin_list, node)
    {
        if ((pin_id == pin_node->pin_id) && (pin_node->irq_enable)) {
            pin_node->irq_trigger = TRUE;
            pin_node->base_cfg.level = level;

            return OPRT_OK;
        }
    }

    return OPRT_COM_ERROR;
}

static pthread_t pin_irq_task_id;
static void *pin_irq_task(void *args)
{
    PIN_T *pin_node;
    SLIST_HEAD *pos = NULL;

    while (1) {
        SLIST_FOR_EACH_ENTRY(pin_node, PIN_T, pos, &pin_list, node)
        {
            if (pin_node->irq_enable && pin_node->irq_cfg.cb && pin_node->irq_trigger) {
                pin_node->irq_cfg.cb(pin_node->irq_cfg.arg);
                pin_node->irq_trigger = FALSE;
            }
        }
    }
}
// --- END: user defines and implements ---

/**
 * @brief gpio init
 *
 * @param[in] port: gpio port
 * @param[in] cfg:  gpio config
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_gpio_init(uint32_t pin_id, const TUYA_GPIO_BASE_CFG_T *cfg)
{
    // --- BEGIN: user implements ---
    PIN_T *pin_node;
    SLIST_HEAD *pos = NULL;

    SLIST_FOR_EACH_ENTRY(pin_node, PIN_T, pos, &pin_list, node)
    {
        if (pin_id == pin_node->pin_id) {
            pin_node->base_cfg.mode = cfg->mode;
            pin_node->base_cfg.direct = cfg->direct;
            pin_node->base_cfg.level = cfg->level;

            return OPRT_OK;
        }
    }

    pin_node = (PIN_T *)malloc(sizeof(PIN_T));
    if (pin_node == NULL) {
        return OPRT_MALLOC_FAILED;
    }
    memset(pin_node, 0x00, sizeof(PIN_T));
    pin_node->pin_id = pin_id;
    pin_node->base_cfg.mode = cfg->mode;
    pin_node->base_cfg.direct = cfg->direct;
    pin_node->base_cfg.level = cfg->level;
    tuya_slist_add_tail(&pin_list, &pin_node->node);

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

/**
 * @brief gpio deinit
 *
 * @param[in] port: gpio port
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_gpio_deinit(uint32_t pin_id)
{
    // --- BEGIN: user implements ---
    return OPRT_NOT_SUPPORTED;
    // --- END: user implements ---
}

/**
 * @brief gpio write
 *
 * @param[in] port: gpio port
 * @param[in] level: gpio output level value
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_gpio_write(uint32_t pin_id, TUYA_GPIO_LEVEL_E level)
{
    // --- BEGIN: user implements ---
    PIN_T *pin_node;
    SLIST_HEAD *pos = NULL;

    SLIST_FOR_EACH_ENTRY(pin_node, PIN_T, pos, &pin_list, node)
    {
        if (pin_id == pin_node->pin_id) {
            pin_node->base_cfg.level = level;

            return OPRT_OK;
        }
    }

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

/**
 * @brief gpio read
 *
 * @param[in] port: gpio port
 * @param[out] level: gpio output level
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_gpio_read(uint32_t pin_id, TUYA_GPIO_LEVEL_E *level)
{
    // --- BEGIN: user implements ---
    PIN_T *pin_node;
    SLIST_HEAD *pos = NULL;

    SLIST_FOR_EACH_ENTRY(pin_node, PIN_T, pos, &pin_list, node)
    {
        if (pin_id == pin_node->pin_id) {
            *level = pin_node->base_cfg.level;

            return OPRT_OK;
        }
    }

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

/**
 * @brief gpio irq init
 * NOTE: call this API will not enable interrupt
 *
 * @param[in] port: gpio port
 * @param[in] cfg:  gpio irq config
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_gpio_irq_init(uint32_t pin_id, const TUYA_GPIO_IRQ_T *cfg)
{
    // --- BEGIN: user implements ---
    PIN_T *pin_node;
    SLIST_HEAD *pos = NULL;

    pthread_create(&pin_irq_task_id, NULL, pin_irq_task, NULL);

    SLIST_FOR_EACH_ENTRY(pin_node, PIN_T, pos, &pin_list, node)
    {
        if (pin_id == pin_node->pin_id) {
            pin_node->irq_cfg.mode = cfg->mode;
            pin_node->irq_cfg.cb = cfg->cb;
            pin_node->irq_cfg.arg = cfg->arg;

            return OPRT_OK;
        }
    }

    pin_node = (PIN_T *)malloc(sizeof(PIN_T));
    if (pin_node == NULL) {
        return OPRT_MALLOC_FAILED;
    }
    memset(pin_node, 0x00, sizeof(PIN_T));
    pin_node->pin_id = pin_id;
    pin_node->irq_cfg.mode = cfg->mode;
    pin_node->irq_cfg.cb = cfg->cb;
    pin_node->irq_cfg.arg = cfg->arg;
    tuya_slist_add_tail(&pin_list, &pin_node->node);

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

/**
 * @brief gpio irq enable
 *
 * @param[in] port: gpio port
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_gpio_irq_enable(uint32_t pin_id)
{
    // --- BEGIN: user implements ---
    PIN_T *pin_node;
    SLIST_HEAD *pos = NULL;

    SLIST_FOR_EACH_ENTRY(pin_node, PIN_T, pos, &pin_list, node)
    {
        if (pin_id == pin_node->pin_id) {
            pin_node->irq_enable = TRUE;

            return OPRT_OK;
        }
    }

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

/**
 * @brief gpio irq disable
 *
 * @param[in] port: gpio port
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_gpio_irq_disable(uint32_t pin_id)
{
    // --- BEGIN: user implements ---
    PIN_T *pin_node;
    SLIST_HEAD *pos = NULL;

    SLIST_FOR_EACH_ENTRY(pin_node, PIN_T, pos, &pin_list, node)
    {
        if (pin_id == pin_node->pin_id) {
            pin_node->irq_enable = FALSE;

            return OPRT_OK;
        }
    }

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