#ifndef _FF_CIRCLEQUEUE_H__
#define _FF_CIRCLEQUEUE_H__

#include <iostream>
#include <memory>
#include <queue>
#include <string>
#include <stdio.h>
#include <string.h>
#include <pthread.h>

#include "QueueStructDefine.h"


using namespace std;

template<typename T>
class FF_CircleQueue 
{
public:
    /**
     * @brief Construct a new ff circlequeue object
     * 
     * @param Size Circle Queue元素数量
     * @param ElemArray 元素数组
     */
    FF_CircleQueue(int Size, T ElemArray);
    ~FF_CircleQueue();


    /**
     * @brief 慢队列 入队
     * 
     * @param Elem 
     * @return int 
     */
    int EnFullQueue(const T& Elem);

    /**
     * @brief 满队列 出队
     * 
     * @param Elem 
     * @return int 
     */
    int DeFullQueue(T& Elem);

    /**
     * @brief 满队列尝试出对，如果此时队列为空，返回false，有数据返回true
     * 
     * @param Elem 
     * @return true 
     * @return false 
     */
    bool Try_DeFullQueue(T& Elem);

    /**
     * @brief 
     * 
     * @param Elem 
     * @return int 
     */
    int Peek_DeFullQueue(T& Elem); ///< 获取数据，但不出队

    /**
     * @brief 
     * 
     * @param Elem 
     * @return int 
     */
    int TryPeek_DeFullQueue(T& Elem);


    /**
     * @brief 空队列 入队
     * 
     * @param Elem 
     * @return int 
     */
    int EnFreeQueue(const T& Elem);
    /**
     * @brief 空队列 出队
     * 
     * @param Elem 
     * @return int 
     */
    int DeFreeQueue(T& Elem);
    /**
     * @brief 空队列尝试出对，有数据为true，无数据返回false
     * 
     * @param Elem 
     * @return true 
     * @return false 
     */
    bool Try_DeFreeQueue(T& Elem);

    /**
     * @brief 
     * 
     * @return true 
     * @return false 
     */
    bool IsValid();

    /**
     * @brief 获取各队列所持有元素数量
     * 
     * @return unsigned int 
     */
    unsigned int GetFullQueueSize();
    unsigned int GetFreeQueueSize();

private:
    void SetValid()
    {
        qValid = true;
    }

    void SetInvalid()
    {
        qValid = false;
    }
    bool qValid;

    int NumElem;

    /**
     * @brief 空队列，及其锁，条件变量
     * 
     */
    std::queue<T>   free_queue_;
    pthread_mutexattr_t free_mutAttr_;
    pthread_mutex_t free_mutex_;
    pthread_cond_t  free_condition_ = PTHREAD_COND_INITIALIZER;

    /**
     * @brief 满队列，及其锁，条件变量
     * 
     */
    std::queue<T>   full_queue_;
    pthread_mutexattr_t full_mutAttr_;
    pthread_mutex_t full_mutex_;
    pthread_cond_t  full_condition_ = PTHREAD_COND_INITIALIZER;

};


/**
 * Implementation of Circle Queue, Designed by Derek.
 */


/**
 * @brief Construct a new queuemanager object
 * @tparam T 
 * @param Size 
 * @param ElemArray 
 */
template<typename T>
FF_CircleQueue<T>::FF_CircleQueue(int Size, T ElemArray)
{
    qValid = false;
    NumElem = 0;
    /// Prevent invalid queue element
    if(Size==0 || ElemArray==NULL){
        /// NULL
    }
    memset(&free_mutAttr_, 0, sizeof(pthread_mutexattr_t));
    pthread_mutexattr_init(&free_mutAttr_);
    pthread_mutexattr_settype(&free_mutAttr_, PTHREAD_MUTEX_RECURSIVE_NP);
    pthread_mutex_init(&free_mutex_, &free_mutAttr_);

    memset(&full_mutAttr_, 0, sizeof(pthread_mutexattr_t));
    pthread_mutexattr_init(&full_mutAttr_);
    pthread_mutexattr_settype(&full_mutAttr_, PTHREAD_MUTEX_RECURSIVE_NP);
    pthread_mutex_init(&full_mutex_, &full_mutAttr_);

    NumElem = Size; ///< Element size

    for(int i=0; i<NumElem; i++){
        free_queue_.push(&ElemArray[i]);
    }

    SetValid();
}

/**
 * @brief Destroy the queuemanager object
 * @tparam T 
 */
template<typename T>
FF_CircleQueue<T>::~FF_CircleQueue()
{
    pthread_mutex_destroy(&free_mutex_);
    pthread_mutex_destroy(&full_mutex_);
    
    /// NULL
}

/**
 * @brief 
 * @param Elem 
 * @return int 
 */
template<typename T>
int FF_CircleQueue<T>::EnFullQueue(const T& Elem) ///< 入队
{
    pthread_mutex_lock(&full_mutex_);
    full_queue_.push(Elem);
    pthread_mutex_unlock(&full_mutex_); ///< 主动释放
    
    pthread_cond_signal(&full_condition_);
    return 0;
}

/**
 * @brief 
 * @param Elem 
 * @return int 
 */
template<typename T>
int FF_CircleQueue<T>::DeFullQueue(T& Elem) ///< 出队
{
    pthread_mutex_lock(&full_mutex_);
    while (full_queue_.empty()) {
        pthread_cond_wait(&full_condition_, &full_mutex_);
    }

    Elem = full_queue_.front();
    full_queue_.pop();
    pthread_mutex_unlock(&full_mutex_);

    return 0;
}

/**
 * @brief 
 * @param Elem 
 * @return int 
 */
template<typename T>
int FF_CircleQueue<T>::Peek_DeFullQueue(T& Elem) ///< 获取数据，但不出队
{
    // *Elem = full_queue_.peek();

    return 0;
}

/**
 * @brief 
 * @tparam T 
 * @param Elem 
 * @return int 
 */
template<typename T>
bool FF_CircleQueue<T>::Try_DeFullQueue(T& Elem)
{
    pthread_mutex_lock(&full_mutex_);

    if (full_queue_.empty()) {
       pthread_mutex_unlock(&full_mutex_);
      return false;
    }

    Elem = full_queue_.front();
    full_queue_.pop();
    pthread_mutex_unlock(&full_mutex_);
    return true;
}

/**
 * @brief 
 * @tparam T 
 * @param Elem 
 * @return int 
 */
template<typename T>
int FF_CircleQueue<T>::TryPeek_DeFullQueue(T& Elem)
{
    //bool ret = false;

    pthread_mutex_lock(&full_mutex_);

    if (full_queue_.empty()) {
       pthread_mutex_unlock(&full_mutex_);
      return -1;
    }

    Elem = full_queue_.front();
    pthread_mutex_unlock(&full_mutex_);

    return 0;
}


/**
 * @brief 
 * @param Elem 
 * @return int 
 */
template<typename T>
int FF_CircleQueue<T>::EnFreeQueue(const T& Elem)
{
    pthread_mutex_lock(&free_mutex_);
    free_queue_.push(Elem);
    pthread_mutex_unlock(&free_mutex_); ///< 主动释放

    pthread_cond_signal(&free_condition_);

    return 0;
}

/**
 * @brief 
 * @param Elem 
 * @return int 
 */
template<typename T>
int FF_CircleQueue<T>::DeFreeQueue(T& Elem)
{   
    pthread_mutex_lock(&free_mutex_);
    
    while (free_queue_.empty()) {
        pthread_cond_wait(&free_condition_, &free_mutex_);
    }

    Elem = free_queue_.front();
    free_queue_.pop();
    pthread_mutex_unlock(&free_mutex_);
    
    return 0;
}

/**
 * @brief 
 * @tparam T 
 * @param Elem 
 * @return int 
 */
template<typename T>
bool FF_CircleQueue<T>::Try_DeFreeQueue(T& Elem)
{
    pthread_mutex_lock(&free_mutex_);
    if (free_queue_.empty()) {
       pthread_mutex_unlock(&free_mutex_);
      return false;
    }

    Elem = free_queue_.front();
    free_queue_.pop();
    pthread_mutex_unlock(&free_mutex_);
    return true;
}

/**
 * @brief Check if queue manager is valid
 * @return true 
 * @return false 
 */
template<typename T>
bool FF_CircleQueue<T>::IsValid()
{
    return qValid;
}

/**
 * @brief Get the number of elements in the fullqueue
 * @tparam T 
 * @return int 
 */
template<typename T>
unsigned int FF_CircleQueue<T>::GetFullQueueSize()
{
    return full_queue_.size();
}

/**
 * @brief Get the number of elements in the freequeue
 * @tparam T 
 * @return int 
 */
template<typename T>
unsigned int FF_CircleQueue<T>::GetFreeQueueSize()
{
    return free_queue_.size();
}

template class FF_CircleQueue<FFQBuffer*>;  ///< TEST
template class FF_CircleQueue<ListenerQBuffer*>;  ///< TEST
template class FF_CircleQueue<VideoQBuffer*>;  ///< TEST

// int fun();

///////////////////////////////////////////////////////////////////////////////////

#endif ///_FF_QUEUE_MANAGER_H__