#ifndef TACTILEIPC3D_SERIAL_QUEUE_H
#define TACTILEIPC3D_SERIAL_QUEUE_H

#include <QQueue>
#include <QMutex>
#include <QWaitCondition>
#include <QByteArray>
#include <QtGlobal>

#include "../data_frame.h"

template <typename T>
class SerialQueue {
public:
    explicit SerialQueue(int maxSize = 2048)
        : m_maxSize(maxSize) {}

    void setMaxSize(int maxSize) {
        QMutexLocker locker(&m_mutex);
        m_maxSize = qMax(1, maxSize);
    }

    void push(const T& item) {
        QMutexLocker locker(&m_mutex);
        if (m_stopped)
            return;
        if (m_maxSize > 0 && m_queue.size() >= m_maxSize) {
            // TODO:待实现内容(指定队列溢出策略，例如丢弃最新/丢弃最旧/阻塞等待)
            m_queue.dequeue();
        }
        m_queue.enqueue(item);
        m_hasData.wakeOne();
    }

    bool pop(T* out, int timeoutMs = -1) {
        QMutexLocker locker(&m_mutex);
        if (timeoutMs < 0) {
            while (m_queue.isEmpty() && !m_stopped)
                m_hasData.wait(&m_mutex);
        } else {
            if (m_queue.isEmpty() && !m_stopped)
                m_hasData.wait(&m_mutex, timeoutMs);
        }
        if (m_queue.isEmpty() || m_stopped)
            return false;
        if (out)
            *out = m_queue.dequeue();
        else
            m_queue.dequeue();
        return true;
    }

    bool tryPop(T* out) {
        QMutexLocker locker(&m_mutex);
        if (m_queue.isEmpty() || m_stopped)
            return false;
        if (out)
            *out = m_queue.dequeue();
        else
            m_queue.dequeue();
        return true;
    }

    void clear() {
        QMutexLocker locker(&m_mutex);
        m_queue.clear();
    }

    void stop() {
        QMutexLocker locker(&m_mutex);
        m_stopped = true;
        m_hasData.wakeAll();
    }

    void reset() {
        QMutexLocker locker(&m_mutex);
        m_stopped = false;
        m_queue.clear();
    }

    int size() const {
        QMutexLocker locker(&m_mutex);
        return m_queue.size();
    }

private:
    mutable QMutex m_mutex;
    QWaitCondition m_hasData;
    QQueue<T> m_queue;
    int m_maxSize = 0;
    bool m_stopped = false;
};

using PacketQueue = SerialQueue<QByteArray>;
using FrameQueue = SerialQueue<DataFrame>;

#endif // TACTILEIPC3D_SERIAL_QUEUE_H
