/*
 * queue.h
 *
 *  Created on: 2021Äê11ÔÂ2ÈÕ
 *      Author: graydon
 */

#ifndef QUEUE_H_
#define QUEUE_H_

#include <string.h>
#include <iostream>
#include "typedefs.h"

template <typename type>
class Queue
{
private:
	u32 size;
	type*  dataPtr;
	u32 wpos;
	u32 rpos;
	u32 count;
public:
	Queue(u32 elemNum);
	Queue(const Queue<type>&);
	~Queue();
	u32 Pop(type& data);
	u32 Pop(type* data,type** dataptr, u32 size);
	u32 Push(type& data);
	u32 Push(type* data, u32 size);
	inline u32 Count() { return count; }
	inline u32 Space() { return size - count; }
	uvoid Clear();
	void Destroy();
	Queue<type>& operator=(const Queue<type>&);
};


template<typename type>
inline Queue<type>::Queue(u32 elemNum)
{
	dataPtr = new type[elemNum];
	size = elemNum;
	rpos = wpos = 0;
	count = 0;
}

template<typename type>
inline Queue<type>::Queue(const Queue<type>&q)
{
	dataPtr = new type[q.size];
	memcpy(dataPtr, q.dataPtr, q.size * sizeof(type));
	rpos = q.rpos;
	wpos = q.wpos;
	count = q.count;
	size = q.size;
}

template<typename type>
inline Queue<type>::~Queue()
{
	Destroy();
}

template<typename type>
inline uvoid Queue<type>::Clear()
{
	rpos = wpos = 0;
	count = 0;
}

template<typename type>
inline u32 Queue<type>::Pop(type& data)
{
	if (count < 1) {
		return 0;
	}

	data = dataPtr[rpos++];
    if (rpos >= size) {
    	rpos = 0;
	}
	count -= 1;

	return 1;
}

template<typename type>
inline u32 Queue<type>::Pop(type * data, type ** dataptr, u32 num)
{
	if (count < num) {
		return 0;
	}

	if (rpos + num > size) {
		//swap
		u32 diff = size - rpos;
		memcpy(data, dataPtr + rpos, diff * sizeof(type));
		memcpy(data + diff, dataPtr, (num - diff) * sizeof(type));
		rpos = num - diff;
		if (dataptr) *dataptr = data;
	}
	else {
		if (dataptr) *dataptr = dataPtr + rpos;
		else memcpy(data, dataPtr+rpos, num*sizeof(type));
		rpos += num;
	}
	count -= num;


	return num;
}

template<typename type>
inline u32 Queue<type>::Push(type * data, u32 num)
{
	if (size - count < num) {
		return 0;
	}

	if (wpos + num > size) {
		int diff = size - wpos;
		memcpy(dataPtr + wpos, data, diff * sizeof(type));
		memcpy(dataPtr, data + diff, (num - diff) * sizeof(type));
		wpos = num - diff;
	}
	else {
		memcpy(dataPtr + wpos, data, num * sizeof(type));
		wpos += num;
		if (wpos >= size) {
			wpos = 0;
		}
	}
	count += num;

	return num;
}

template<typename type>
inline u32 Queue<type>::Push(type& data)
{
	if (size - count < 1) {
		return 0;
	}

	dataPtr[wpos++] = data;
	if (wpos >= size) {
		wpos = 0;
	}
	count += 1;

	return 1;
}


template<typename type>
inline void Queue<type>::Destroy()
{
	if (dataPtr) {
		delete[] dataPtr;
		dataPtr = 0;
	}
}

template<typename type>
inline Queue<type>& Queue<type>::operator=(const Queue<type> &q)
{
	if (&q != this) {
		dataPtr = new type[q.size];
		memcpy(dataPtr, q.dataPtr, q.size*sizeof(type));
		rpos = q.rpos;
		wpos = q.wpos;
		count = q.count;
		size = q.size;
	}
	return *this;
}
#endif /* QUEUE_H_ */