#ifndef RINGQUEUE_H
#define RINGQUEUE_H

#include <cstdlib>
#include <utility>
#include <mutex>
#include <condition_variable>
// #include <atomic>

/**
 * @brief 这里采用零公摊的方式,设置多大的空间,就有多大的空间可以使用
 *        1、m_rear指向最新入队的元素的下一个位置,就是下个将要入队的元素位置
 *        2、m_front指向需要出队的第一个元素
 *        3、环形队列自带互斥锁
 *        
 * 注意:
 *      使用时要注意,不带NoBlock的都是阻塞函数
 *
 * 判断队列满:
 *        m_rear == m_front,并且此时都不等于 -1
 *
 * 判断队列空:
 *        m_rear == m_front,并且都等于 -1
 *
 * 获取队列大小:
 *       基本原则就是m_rear后面跟着的是有效值,m_front后面跟着的是已经出队的大小
 *       m_rear > m_front,返回 m_rear - m_front
 *       m_front > m_rear,返回 m_capacity - (m_front - m_rear)
 *       m_rear == m_front,且不等于-1,返回 m_capacity
 *       m_rear == m_front,且等于-1,返回 0
 * 
 * @tparam T 模版类型
 */

template<typename T>
class RingQueue
{    

public:
    RingQueue();
    RingQueue(long size);
    ~RingQueue();

    /* 入队,默认是阻塞入队 */
    void push(const T& value);
    void push(T&& value);
    bool push_NoBlock(const T& value);
    bool push_NoBlock(T&& value);

    /* 出队,删除队列的首个元素
     * 注意,如果存储的是指针,需要手动释放该指针指向的内存区域,不然会造成内存泄漏 */
    void pop();

    /* 获取队列中第一个值),但是不出队
     * 阻塞的方式获取,如果队列为空,会一直阻塞住,直到获取到数据为止 */
    T front();
    /* 非阻塞的方式获取,队列为空返回false */
    bool front_NoBlock(T& t);

    /* 获取对立第一个数据,获取完立刻出队
     * 如果队列为空,会阻塞住,直到有数据为止 */
    T&& front_pop();
    // T&& front_pop_rvalue();
    bool front_pop_NoBlock(T& t);
    
    /* 设置队列大小 */
    void setQueueCapacity(long size);
    /* 获取队列大小,队列中有效值的大小 */
    long QueueSize();
    /* 获取队列容量 */
    long QueueCapacity();
    /* 判断队列是否为空 */
    bool isEmpty();
    /* 判断队列是否已满 */
    bool isFull();
    /* 清空队列 */
    void clearQueue();
    /* 退出所有可能的阻塞函数 */
    void exit();

private:
    bool m_isExit = false;                  /* 是否退出,这个标识位是为了退出阻塞住的函数 */
    std::mutex m_mutex;                     /* 互斥锁 */
    T* m_queue = nullptr;                   /* 队列 */
    long m_capacity = 0;     /* 队列容量 */
    long m_front = 0;        /* 队头 */
    long m_rear = 0;         /* 队尾 */
    std::condition_variable m_cond_NoFull;      /* 非满条件变量 */
    std::condition_variable m_cond_NoEmpty;     /* 非空条件变量 */
};


/* =====================================================================
 * ***************************** 函数实现 *****************************
 * ===================================================================== */

/* 这个构造函数需要调用 setQueueSize 设置环形队列的大小 */
template<typename T>
RingQueue<T>::RingQueue() : m_capacity(0) , m_front(-1), m_rear(-1)
{
    
}

template<typename T>
RingQueue<T>::RingQueue(long capacicy) : m_capacity(capacicy)
{
    m_front = -1;
    m_rear = -1;
    m_queue = new T[m_capacity];
}

template<typename T>
RingQueue<T>::~RingQueue()
{
    if(m_queue != nullptr)
    {
        delete[] m_queue;
        m_queue = nullptr;
    }
}


/* 清空队列 */
template<typename T>
void RingQueue<T>::clearQueue()
{
    m_mutex.lock();
    if(m_queue != nullptr)
    {
        delete[] m_queue;
        m_queue = nullptr;
    }
    m_front = -1;
    m_rear = -1;
    m_mutex.unlock();
}


/*************** 入队 *******************/
template<typename T>
void RingQueue<T>::push(const T& value)
{
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_cond_NoFull.wait(lock, [this](){
            return (!isFull() || m_isExit);
        });
        if(m_isExit)
        {
            return;
        }

        if(m_rear == -1)
        {
            m_front = 0;
            m_rear = 0;
        }

        m_queue[m_rear] = value;
        m_rear = (m_rear + 1) % m_capacity;
    }
    m_cond_NoEmpty.notify_all();
}

template<typename T>
void RingQueue<T>::push(T&& value)
{
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_cond_NoFull.wait(lock, [this](){
            return (!isFull() || m_isExit);
        });
        if(m_isExit)
        {
            return;
        }

        if(m_rear == -1)
        {
            m_front = 0;
            m_rear = 0;
        }

        m_queue[m_rear] = std::move(value);
        m_rear = (m_rear + 1) % m_capacity;
    }

    m_cond_NoEmpty.notify_all();
}

/**
 * @brief 非阻塞的方式入队,如果队列已满,直接返回
 * 
 */
 template<typename T>
bool RingQueue<T>::push_NoBlock(const T& value)
{
    {
        // std::unique_lock<std::mutex> lock(m_mutex, std::defer_lock);
        // if(!lock.try_lock())
        // {
        //     return false;
        // }
        std::lock_guard<std::mutex> lock(m_mutex);
        /* 先检查队列是否还有剩余空间 */
        if(isFull())
        {
            return false;
        }
        else if(m_rear == -1)
        {
            m_front = 0;
            m_rear = 0;
        }

        m_queue[m_rear] = value;
        m_rear = (m_rear + 1) % m_capacity;
    }
    m_cond_NoEmpty.notify_all();
    return true;
}

template<typename T>
bool RingQueue<T>::push_NoBlock(T&& value)
{
    {
        // std::unique_lock<std::mutex> lock(m_mutex, std::defer_lock);
        // if(!lock.try_lock())
        // {
        //     return false;
        // }
        std::lock_guard<std::mutex> lock(m_mutex);
        /* 先检查队列是否还有剩余空间 */
        if(isFull())
        {
            return false;
        }
        else if(m_rear == -1)
        {
            m_front = 0;
            m_rear = 0;
        }

        m_queue[m_rear] = std::move(value);
        m_rear = (m_rear + 1) % m_capacity;
    }
    m_cond_NoEmpty.notify_all();
    return true;
}


/**
 * @brief 出队,删除队列的首个元素
 *        注意,如果存储的是指针,需要手动释放该指针指向的内存区域,不然会造成内存泄漏
 * 
 * @tparam T 
 */
template<typename T>
void RingQueue<T>::pop()
{
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        if(isEmpty())
        {
            return;
        }
        m_front = (m_front + 1) % m_capacity;
        if(m_front == m_rear)
        {
            m_front = -1;
            m_rear = -1;
        }
    }
    m_cond_NoFull.notify_all();
}


/* 获取队列中第一个值,但是不出队
 * 阻塞的方式获取,如果队列为空,会一直阻塞住,直到获取到数据为止 */
template<typename T>
T RingQueue<T>::front()
{
    T retValue;
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_cond_NoEmpty.wait(lock, [this](){
            return (!isEmpty() || m_isExit);
        });
        if(m_isExit)
        {
            return retValue;
        }
        retValue = m_queue[m_front];
    }
    return retValue;
}


/* 获取队列中第一个值,但是不出队,非阻塞的方式获取 */
template<typename T>
bool RingQueue<T>::front_NoBlock(T& t)
{
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        if(isEmpty())
        {
            return false;
        }
        t = m_queue[m_front];
    }
    return true;
}

/* 获取对立第一个数据,获取完立刻出队
 * 如果队列为空,会阻塞住,直到有数据为止 */
template<typename T>
T&& RingQueue<T>::front_pop()
{
    T ret;
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_cond_NoEmpty.wait(lock, [this](){
            return (!isEmpty() || m_isExit);
        });
        if(m_isExit)
        {
            return std::move(ret);
        }
        ret = std::move(m_queue[m_front]);
        m_front = (m_front + 1) % m_capacity;
        if(m_front == m_rear)
        {
            m_front = -1;
            m_rear = -1;
        }
    }
    m_cond_NoFull.notify_all();
    return std::move(ret);
}


template<typename T>
bool RingQueue<T>::front_pop_NoBlock(T& t)
{
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        if(isEmpty())
        {
            return false;
        }
        t = std::move(m_queue[m_front]);
        m_front = (m_front + 1) % m_capacity;
        if(m_front == m_rear)
        {
            m_front = -1;
            m_rear = -1;
        }
    }
    m_cond_NoFull.notify_all();
    return true;
}


/**
 * @brief 设置队列大小
 *        注意:使用这个设置,如果队列中存储的是指针,指针的内存区域需要在调用这个函数之前释放,不然可能会造成
 *              内存泄漏
 * 
 * @tparam T 
 * @param size 
 */
template<typename T>
void RingQueue<T>::setQueueCapacity(long size)
{
    if(m_queue != nullptr)
    {
        delete[] m_queue;
        m_queue = nullptr;
    }

    m_capacity = size;
    m_front = -1;
    m_rear = -1;
    m_queue = new T[m_capacity];
}

/* 获取队列中有效值的大小 */
template<typename T>
long RingQueue<T>::QueueSize()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    if(m_rear == -1)
    {
        return 0;
    }
    else if(m_rear > m_front)
    {
        return m_rear - m_front;
    }
    else if(m_rear < m_front)
    {
        return m_capacity - ( m_front - m_rear );
    }
    /* 这时候是队列满 */
    return m_capacity;
}

/* 获取队列容量 */
template<typename T>
long RingQueue<T>::QueueCapacity()
{
    std::lock_guard<std::mutex> lock(m_mutex);
    return m_capacity;
}

/**
 * @brief 判断队列是否为空
 * 
 * @tparam T 
 * @return true 
 * @return false 
 */
template<typename T>
bool RingQueue<T>::isEmpty()
{
    if((m_front == m_rear) && (m_front == -1))
    {
        return true;
    }

    return false;
}

/**
 * @brief 判断队列是否已满,这里判断依赖入队和出队后的队头和队尾指针的位置
 *        1、队头和队尾指针相等,但是队尾指针不等于-1,表示队列已满
 * 
 * @tparam T 
 * @return true 
 * @return false 
 */
template<typename T>
bool RingQueue<T>::isFull()
{
    /* 如果m_rear或者m_front不等于-1,说明此时里面有内容
     * 同时m_front == m_rear,队列就满了 */
    if(m_front == m_rear && m_rear != -1)
    {
        return true;
    }
    
    return false;
}

/* 退出所有可能的阻塞函数 */
template<typename T>
void RingQueue<T>::exit()
{
    m_isExit = true;
    m_cond_NoFull.notify_all();
    m_cond_NoEmpty.notify_all();
}



#endif /* RINGQUEUE_H */