/******************************************************************************
 * file    lib/management/fifo.c 
 * author  YuLiang
 * version 1.0.0
 * date    21-Feb-2023
 * brief   This file provides all the fifo operation functions.
 *
 ******************************************************************************
 * Attention
 *
 * 
© COPYRIGHT(c) 2021 LandPower
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of LandPower nor the names of its contributors may be used to 
 *      endorse or promote products derived from this software without specific
 *      prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/
/* Includes ------------------------------------------------------------------*/
//#ifdef HAVE_CONFIG_H
#include "config.h"
//#endif
/* 标准C库头文件. */
/* 用户代码头文件. */
#include "cmd.h"
#include "fifo.h"
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static array_t *fifo = NULL;
/* Private function prototypes -----------------------------------------------*/
/* Internal functions --------------------------------------------------------*/
/* 显示 fifo 的使用情况. */
CMD(show_fifo,
    show_fifo_cmd,
    "show fifo",
    "Show\n"
    "Fifo\n")
{
    uint32_t id = 0;
    fifo_t *fifo_node = NULL;
    
    for (id = 0; id < array_active(fifo); id++)
    {
        fifo_node = array_lookup(fifo, id);
        if (NULL == fifo_node)
        {
            continue;
        }
        fifo_show(id);
    }
    return CMD_SUCCESS;
}
/* Interface functions -------------------------------------------------------*/
/* 初始化 fifo 全局结构 */
int32_t fifo_init(void)
{
    fifo = array_init(16, MTYPE_FIFO);
    if (!fifo)
    {
        log_err(LOG_FIFO, "ERROR at array init!");
        return E_MEM;
    }
    cmd_install_element(COMMON_NODE, &show_fifo_cmd);
    return E_NONE;
}
/* 创建 1 个 fifo. */
int32_t fifo_create(char* name, uint32_t size)
{
    fifo_t *fifo_node = NULL;
    /* 初始化. */
    fifo_node = XMALLOC(MTYPE_FIFO, sizeof(fifo_t));
    if (!fifo_node)
    {
        return E_MEM;
    }
    snprintf(fifo_node->name, FIFO_NAME_LEN, "%s", name);
    fifo_node->size = size;
    /* 申请互斥锁, 用于通知读线程读取有效. */
    if (sem_init(&fifo_node->sem, 0, 0) != 0)
    {
        XFREE(MTYPE_FIFO, fifo_node);
        DBG(DBG_M_FIFO_ERR, "%s ERROR at sem init return %s!\r\n", name, safe_strerror(errno));
        return E_SYS_CALL;
    }
    /* 申请信号量, 防止多个线程同时操作 fifo. */
    if (pthread_mutex_init(&fifo_node->mutex, NULL) != 0)
    {
        XFREE(MTYPE_FIFO, fifo_node);
        sem_destroy(&fifo_node->sem);
        DBG(DBG_M_FIFO_ERR, "%s ERROR at sem init return %s!\r\n", name, safe_strerror(errno));
        return E_SYS_CALL;
    }
    /* 申请 fifo 空间. */
    fifo_node->data = XMALLOC(MTYPE_FIFO, sizeof(void *) * size);
    if (!fifo_node->data)
    {
        XFREE(MTYPE_FIFO, fifo_node);
        sem_destroy(&fifo_node->sem);
        pthread_mutex_destroy(&fifo_node->mutex);
        return E_MEM;
    }
    /* 添加到全局结构体. */
    return array_append(fifo, fifo_node, MTYPE_FIFO);
}
/* 往 fifo 中写入一条数据. */
int32_t fifo_write(uint32_t id, void *data, int32_t len)
{
    int32_t index = 0;
    fifo_t *fifo_node = array_get(fifo, id);
    void *temp = NULL;
    /* 参数检查. */
    if (!fifo_node)
    {
        DBG_Q(DBG_M_FIFO_ERR, "#7\r\n");
        return E_NOT_FOUND;
    }
    /* 申请数据空间. */
    temp = XMALLOC_Q(MTYPE_FIFO, len);
    if (!temp)
    {
        return E_MEM;
    }
    memcpy(temp, data, len);
    pthread_mutex_lock(&fifo_node->mutex);
    /* 判断 fifo 是否满了. */
    index = fifo_node->cur + 1;
    if (index == fifo_node->size)
    {
        index = 0;
    }
    if (index == fifo_node->valid)
    {
        DBG_Q(DBG_M_FIFO_ERR, "#8 %d\r\n", id);
        XFREE(MTYPE_FIFO, temp);
        pthread_mutex_unlock(&fifo_node->mutex);
        return E_MEM;
    }
    /* 数据加入 fifo. */
    fifo_node->data[fifo_node->cur] = temp;
    fifo_node->used++;
    if (fifo_node->used > fifo_node->max)
    {
        fifo_node->max = fifo_node->used;
    }
    fifo_node->cur = index;
    sem_post(&fifo_node->sem);
    pthread_mutex_unlock(&fifo_node->mutex);
    
    return len;
}
/* 从 fifo 中读取数据, 注意, 只能一个进程进行读取. */
int32_t fifo_read(uint32_t id, void **data)
{
    fifo_t *fifo_node = array_get(fifo, id);
    /* 参数检查. */
    if (!fifo_node)
    {
        DBG(DBG_M_FIFO_ERR, "Fifo %d is not found!\r\n", id);
        return E_NOT_FOUND;
    }
    /* 等待有效数据, 如果有效, 返回数据. */
    while (fifo_node->valid == fifo_node->cur) 
    {
        sem_wait(&fifo_node->sem);
    }
    *data = fifo_node->data[fifo_node->valid];
    
    return E_NONE;
}
/* 释放 fifo 数据, 因为节省了一次内存申请, 所以必须手动释放 fifo 数据. */
int32_t fifo_push(uint32_t id)
{
    uint32_t index = 0;
    fifo_t *fifo_node = array_get(fifo, id);
    /* 检查参数. */
    if (!fifo_node)
    {
        DBG(DBG_M_FIFO_ERR, "Fifo %d is not found!\r\n", id);
        return E_NOT_FOUND;
    }
    /* 释放数据. */
    if (fifo_node->valid != fifo_node->cur)
    {
        pthread_mutex_lock(&fifo_node->mutex);
        
        XFREE(MTYPE_FIFO, fifo_node->data[fifo_node->valid]);
        fifo_node->data[fifo_node->valid] = NULL;
        index = fifo_node->valid + 1;
        if (index == fifo_node->size)
        {
            index = 0;
        }
        fifo_node->used--;
        fifo_node->valid = index;
        pthread_mutex_unlock(&fifo_node->mutex);
    }
    return E_NONE;
}
/* 显示 fifo 的使用情况. */
void fifo_show(uint32_t id)
{
    fifo_t *fifo_node = NULL;
    fifo_node = array_lookup(fifo, id);
    if (NULL == fifo_node)
    {
        return;
    }
    
    printh("%-32s %-2d %-2d %-2d %-2d %-2d %-2d\r\n", fifo_node->name, id, fifo_node->size, fifo_node->cur,
        fifo_node->valid, fifo_node->used, fifo_node->max);
}
/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/