#ifndef RINGBUFFER_H
#define RINGBUFFER_H

#include <stdint.h>
//
// class ring buffer
//
template<class T>
class RINGBUF
{
public:
  RINGBUF(uint32_t len) {
    
    // capacity is 2^n
    uint32_t n = len - 1;
    uint32_t p = 0;
    for(; n != 0; n >>= 1) {
      p = ( p << 1 ) + 1;
    }
    capacity = p + 1;
    
    // alloc buffer
    pbuf = new T[capacity];
    init();

    // mask
    mask = capacity - 1;
  };
  
  virtual ~RINGBUF() {
    delete[] pbuf;
    pbuf = nullptr;
  }
  
  void init(void)
  {
	  memset(pbuf, 0, sizeof(T)*capacity);
	  num = 0;
	  top = 0;
	  rear = 0;
  }

  uint32_t count(void)
  {
	  return num;
  }

  T pop(void)
  {
	  T ret;
	  
	  if (num == 0) {
		  return ret;
	  }

	  ret = pbuf[top];
	  top = (top + 1) & mask;
	  num--;

	  return ret;
  }

  void push(T &dat)
  {
	  pbuf[rear] = dat;
	  rear = (rear + 1) & mask;
	  if (num < capacity) {
		  num++;
	  }
	  else {
		  top = rear;
	  }
  }

  uint32_t pop_buf(T *dat, uint32_t len)
  {
	  // require len > 0
//	  if (len == 0) return 0;
	  if (len > num) {
		  return 0;
	  }

	  uint32_t tmp_top = top + len;
	  if ((tmp_top & ~mask) == 0) {
		  // not over
		  memcpy(dat, pbuf + top, sizeof(T) * len);
		  top = tmp_top;
	  }
	  else {
		  // over
		  uint32_t head_num = capacity - top;
		  uint32_t tail_num = tmp_top & mask;
		  memcpy(dat, pbuf + top, sizeof(T) * head_num);
		  if (tail_num != 0) {
			  memcpy(dat + head_num, pbuf, sizeof(T) * tail_num);
		  }
		  top = tail_num;
	  }
	  num -= len;
	  return len;
  }

  void push_buf(T *dat, uint32_t len)
  {
//	  if (len == 0) return;
	  uint32_t tmp_rear = rear + len;

	  if ((tmp_rear & ~mask) == 0) {
		  // not over
		  memcpy(pbuf + rear, dat, sizeof(T) * len);
		  rear = tmp_rear;
		  num += len;
	  }
	  else {
		  // over
		  uint32_t tail_num = tmp_rear & mask;
		  uint32_t head_num = capacity - rear;

		  // copy until end
		  memcpy(pbuf + rear, dat, sizeof(T) * head_num);
		  if (tail_num != 0) { // tail_num == 0, dat[head_num] is invalid
			  memcpy(pbuf, dat + head_num, sizeof(T) * tail_num);
		  }
		  rear = tail_num;
		  num += len;
		  if (num > capacity) {
			  num = capacity;
			  top = rear;
		  }
	  }
  }

private:
  uint32_t capacity;
  uint32_t mask;
  uint32_t num;
  uint32_t rear;
  uint32_t top;
  T *pbuf;
 };

#endif
