/**
 * @file        vmf_nnm_fifoq_manager.h
 * @brief       for kdp2 fw only - inference structures and functions
 *
 * @copyright   Copyright (c) 2022 Kneron Inc. All rights reserved.
 */

#pragma once

#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_INPUT_NODE_COUNT        5

typedef struct
{
    int num_of_buffer;
    uint32_t buffer_addr[MAX_INPUT_NODE_COUNT];
    uint32_t phy_buffer_addr[MAX_INPUT_NODE_COUNT];
    int length[MAX_INPUT_NODE_COUNT];
} buffer_object_t;

/**
 * @brief Thread function for constantly execute fifo queue
 *
 * @param arg NULL
 * @return void* NULL
 */
void *VMF_NNM_Fifoq_Manager_Enqueue_Image_Thread(void *arg);

/**
 * @brief Init the fifo queue manager
 * @return int 0: success, -1: failed
 */
int VMF_NNM_Fifoq_Manager_Init();

/**
 * @brief Destroy the fifo queue manager
 * @return int 0: success, -1: failed
 */
int VMF_NNM_Fifoq_Manager_Destroy();

/**
 * @brief Wakeup the fifo queue manager before terminate
 * @return int 0: success, -1: failed
 */
int VMF_NNM_Fifoq_Manager_Wakeup();

/**
 * @brief Allocate buffers for fifo queue
 *
 * @param image_count number of image buffers
 * @param image_size size of image buffers
 * @param result_count number of result buffers
 * @param result_size size of image buffers
 * @return int 0: success, -1: failed
 */
int VMF_NNM_Fifoq_Manager_Allocate_Buffer(uint32_t image_count, uint32_t image_size, uint32_t result_count, uint32_t result_size);

/**
 * @brief enqueue one inference object containing one or more images to the "inference-waiting buffer queue"
 *
 * return immediately if queue is not full
 * blocking wait (unless timeout) if queue if full
 * If 1 < total_num_buf, the image buffers will be enqueued after index 0 ~ (total_num_buf - 1) has been stored.
 *
 * @param total_num_buf[in] the total number of buffers should be contain in the list
 * @param index[in] index of the buffer in the list
 * @param buf_addr[in] address of the buffer
 * @param phy_buf_addr[in] physical address of the buffer
 * @param buf_size[in] size of the buffer
 * @param timeout[in] -1: wait forever, 0: no wait.
 * @param preempt[in] preempt this result data
 * @return int 0: success, -1: error, -7: timeout.
 */
int VMF_NNM_Fifoq_Manager_Image_Enqueue(uint32_t total_num_buf, uint32_t index, uint32_t buf_addr, uint32_t phy_buf_addr, int buf_size, int32_t timeout, bool preempt);

/**
 * @brief request one inference object from the "inference-waiting buffer queue"
 *
 * return immediately if queue has inference objects
 * blocking wait (unless timeout) if no inference object is available yet
 *
 * @param bobj[out] buffer object containing one or more buffers to be inference
 * @param timeout[in] -1: wait forever, 0: no wait.
 * @return int 0: success, -1: error, -7: timeout.
 */
int VMF_NNM_Fifoq_Manager_Image_Dequeue(buffer_object_t *bobj, int32_t timeout);

/**
 * @brief retrieve one free-to-use image buffer from the "inference-done image queue"
 *
 * return immediately if queue has free buffers
 * blocking wait (unless timeout) if no free buffer is available
 * if force_grab, and no free buffer is available, it will force grab the earliest-queued buffer from the "inference-waiting buffer queue"
 *
 * @param buf_addr[in] address of the buffer
 * @param phy_buf_addr[in] physical address of the buffer
 * @param buf_size[in] size of the buffer
 * @param timeout[in] -1: wait forever, 0: no wait.
 * @param force_grab[int] whether force grab one buffer from data queue when no free buffer is available
 * @return int 0: success, -1: error, -7: timeout.
 */
int VMF_NNM_Fifoq_Manager_Image_Get_Free_Buffer(uint32_t *buf_addr, uint32_t *phy_buf_addr, int *buf_size, int32_t timeout, bool force_grab);

/**
 * @brief put one free buffer to the "inference-done image queue"
 *
 * return immediately if queue is not full
 * blocking wait (unless timeout) if no free buffer is available
 *
 * @param buf_addr[in] address of the buffer
 * @param phy_buf_addr[in] physical address of the buffer
 * @param buf_size[in] size of the buffer
 * @param timeout[in] -1: wait forever, 0: no wait.
 * @return int 0: success, -1: error, -7: timeout.
 */
int VMF_NNM_Fifoq_Manager_Image_Put_Free_Buffer(uint32_t buf_addr, uint32_t phy_buf_addr, int buf_size, int32_t timeout);

/**
 * @brief enqueue one result data to the "inference-complete result queue"
 *
 * return immediately if queue is not full
 * blocking wait (unless timeout) if queue if full
 *
 * @param result_buf[in] address of the buffer
 * @param result_phy_buf[in] physical address of the buffer
 * @param result_buf_size[in] size of the buffer
 * @param timeout[in] -1: wait forever, 0: no wait.
 * @param preempt[in] preempt this result data
 * @return int 0: success, -1: error, -7: timeout.
 */
int VMF_NNM_Fifoq_Manager_Result_Enqueue(uint32_t result_buf, uint32_t result_phy_buf, int result_buf_size, int32_t timeout, bool preempt);

/**
 * @brief request one inference result from the "inference-complete result queue"
 *
 * return immediately if queue has resutls
 * blocking wait (unless timeout) if no result is available yet
 *
 * @param buf_addr[in] address of the buffer
 * @param phy_buf_addr[in] physical address of the buffer
 * @param buf_size[in] size of the buffer
 * @param timeout[in] -1: wait forever, 0: no wait.
 * @return int 0: success, -1: error, -7: timeout.
 */
int VMF_NNM_Fifoq_Manager_Result_Dequeue(uint32_t *buf_addr, uint32_t *phy_buf_addr, int *buf_size, int32_t timeout);

/**
 * @brief retrieve one free-to-use result buffer from the "free result queue"
 *
 * return immediately if queue has free buffers
 * blocking wait (unless timeout) if no free buffer is available
 * if force_grab, and no free buffer is available, it will force grab the earliest-queued buffer from the "inference-complete result queue"
 *
 * @param buf_addr[in] address of the buffer
 * @param phy_buf_addr[in] physical address of the buffer
 * @param buf_size[in] size of the buffer
 * @param timeout[in] -1: wait forever, 0: no wait.
 * @return int 0: success, -1: error, -7: timeout.
 */
int VMF_NNM_Fifoq_Manager_Result_Get_Free_Buffer(uint32_t *buf_addr, uint32_t *phy_buf_addr, int *buf_size, int32_t timeout);

/**
 * @brief put one free buffer to the "free result queue" (which will be used by inference APP)
 *
 * return immediately if queue is not full
 * blocking wait (unless timeout) if no free buffer is available
 *
 * @param buf_addr[in] address of the buffer
 * @param phy_buf_addr[in] physical address of the buffer
 * @param buf_size[in] size of the buffer
 * @param timeout[in] -1: wait forever, 0: no wait.
 * @return int 0: success, -1: error, -7: timeout.
 */
int VMF_NNM_Fifoq_Manager_Result_Put_Free_Buffer(uint32_t buf_addr, uint32_t phy_buf_addr, int buf_size, int32_t timeout);

/**
 * @brief Clear the data queues and put buffer back to free queues
 */
void VMF_NNM_Fifoq_Manager_Clean_Queues(void);

/**
 * @brief Release the memory of all buffer in result and image queues.
 */
void VMF_NNM_Fifoq_Manager_Release_All_Buffer(void);

/**
 * @brief Set the status of the fifo queue buffer has been allocated
 *
 * @param input_buf_countp[in] Input buffer count for FIFO queue
 * @param input_buf_size[in] Input buffer size for FIFO queue
 * @param result_buf_count[in] Result buffer count for FIFO queue
 * @param result_buf_size[in] Result buffer size for FIFO queue
 */
void VMF_NNM_Fifoq_Manager_Store_Fifoq_Config(uint32_t input_buf_count, uint32_t input_buf_size, uint32_t result_buf_count, uint32_t result_buf_size);

/**
 * @brief Get the status of whether the fifo queue buffer has been allocated
 *
 * @return true the fifo queue buffer has been allocated
 * @return false the fifo queue buffer has NOT been allocated
 */
bool VMF_NNM_Fifoq_Manager_Get_Fifoq_Allocated(void);

/**
 * @brief Get the configuration of FIFO queue buffer
 *
 * @param input_buf_count[out] Input buffer count for FIFO queue
 * @param input_buf_size[out] Input buffer size for FIFO queue
 * @param result_buf_count[out] Result buffer count for FIFO queue
 * @param result_buf_size[out] Result buffer size for FIFO queue
 */
void VMF_NNM_Fifoq_Manager_Get_Fifoq_Config(uint32_t *input_buf_count, uint32_t *input_buf_size, uint32_t *result_buf_count, uint32_t *result_buf_size);

/**
 * @brief enqueue error/status result
 *
 * @param[in] job_id user-defind ID to synchronize with host SW side
 * @param[in] error_code error code that needs to send back
 *
 */
void VMF_NNM_Fifoq_Manager_Status_Code_Enqueue(int job_id, int error_code);

/**
 * @brief suspend image/result FIFO queue:
 * stop to receive image FIFO queue, and clean up the image/result FIFO queue.
 * And FIFO queue will enter to idle status.
 *
 * @return int 0: success, -1: error
 */
int VMF_NNM_Fifoq_Manager_Suspend();

/**
 * @brief resume image/result FIFO queue:
 *
 * @return int 0: success, -1: error
 */
int VMF_NNM_Fifoq_Manager_Resume();
