#ifndef _in_buff_hpp_
#define _in_buff_hpp_

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "clock.h"

struct buff_t
{
private:
	char*ptr_;
	int len_;
	int owner_;
public:
	buff_t(const buff_t&);
	buff_t() :ptr_(0) ,len_(0) ,owner_(0)
	{
	}

	buff_t(char*ptr,int len,bool owner=false)
		:ptr_(ptr) ,len_(len) ,owner_(owner?1:0)
	{
	}

	char*ptr()const
	{
		return ptr_;
	}

	bool empty()const
	{
		return len_==0;
	}

	int size()const
	{
		return len_;
	}

	int len()const
	{
		return len_;
	}

	void set(int len)
	{
		len_=len;
	}

	void reset(void*ptr,int len,bool owner)
	{
		if(owner_) ::free(ptr_);

		ptr_=(char*)ptr;
		len_=len;
		owner_=owner?1:0;
	}

	void clear()
	{
		reset(0,0,0);
	}

	~buff_t()
	{
		if(owner_)
		{
			::free(ptr_);
		}
	}
};

struct buf_base
{
private:
	buf_base(const buf_base&);
protected:
	char*m_base;
	int  m_mask,m_size;
	int64_t m_ppos,m_gpos;
public:

	buf_base(int size)
	{
		m_size=256;
		while(m_size<size)
			m_size<<=1;

		m_base=(char*)::malloc(m_size);
		m_mask=m_size-1;

		m_ppos=m_gpos=0;
	}

	void grow(int add_size)
	{
		int size=m_size;
		while(size<m_size+add_size)
			size<<=1;

		m_gpos=pos(m_gpos);
		m_ppos=pos(m_ppos);
		if(m_ppos<m_gpos)
			m_ppos+=size;

		m_base=(char*)::realloc(m_base,size);
		m_size=size;
		m_mask=m_size-1;
	}

	~buf_base()
	{
		::free(m_base);
	}

	int size()const
	{
		return m_size;
	}

	inline int pos(int64_t p)const
	{
		return (int)p&m_mask;
	}

	int len_data() const
	{
		return m_ppos-m_gpos;
	}

	int len_free() const
	{
		return size()-len_data();
	}

	int operator[](int i)const
	{
		if(i>len_data())
			return -1;

		return (int)(uint8_t)m_base[pos(i+m_gpos)];
	}

	uint8_t& operator[](int i)
	{
		return *(uint8_t*)&m_base[pos(i+m_gpos)];
	}
};

struct in_buff:buf_base
{
	in_buff(int size)
		:buf_base(size)
	{
	}

	~in_buff()
	{
	}

	buff_t alloc()
	{
		const int ppos=pos(m_ppos);
		const int gpos=pos(m_gpos);
		int len=gpos<=ppos ?size()-ppos :gpos-ppos;

		return len==0?buff_t(0,0):buff_t(m_base+ppos,len);
	}

	void commit(int len)
	{
		m_ppos+=len;
	}

	void free(int len)
	{
		m_gpos+=len;
	}

	buff_t peek()
	{
		const int ppos=pos(m_ppos);
		const int gpos=pos(m_gpos);

		return buff_t(m_base+gpos,gpos<=ppos?ppos-gpos:size()-gpos);
	}

/*
	char*peek(char*buf,int len)
	{
		if(len>len_data())
			return 0;

		buff_t b=peek();

		if(b.len()>=len)
			return b.ptr();

		memcpy(buf,b.ptr(),b.len());
		memcpy(buf+b.len(),m_base,len-b.len());
		return buf;
	}
	*/

	buff_t get(int len)
	{
		if(len>len_data())
			return buff_t(0,0);

		buff_t b=peek();
		if(b.len()>=len)
		{
			free(len);
			return b;
		}
		else
		{
			buff_t r ((char*)malloc(len),len,1);
			memcpy(r.ptr(),b.ptr(),b.len());
			memcpy(r.ptr()+b.len(),m_base,len-b.len());
			free(len);
			return r;
		}
	}

	buff_t get(const char*chim)
	{
		int len=find(chim);
		if(len==0)
			return buff_t();

		return get(len);
	}

	char*get(char *buf,int len)
	{
		if(len>len_data())
			return 0;

		buff_t b=peek();
		if(b.len()>=len)
		{
			memcpy(buf,b.ptr(),len);
		}
		else
		{
			memcpy(buf,b.ptr(),b.len());
			memcpy(buf+b.len(),m_base,len-b.len());
		}

		free(len);
		return buf;
	}

	void skip(int len)
	{
		free(len);
	}

	int find(const char*chim)const
	{
		int slen=strlen(chim);
		for(int64_t p=m_gpos;p<m_ppos;)
		{
			if(m_base[this->pos(p++)]!=chim[0])
				continue;

			int j=1;
			for(;j<slen;j++)
			{
				if(m_base[pos(p++)]!=chim[j])
					break;
			}

			if(j==slen)
				return p-m_gpos;
		}

		return 0;
	}

};
/*
struct out_buff:buf_base
{
	out_buff(int size)
		:buf_base(size)
	{
	}

	~out_buff()
	{
	}

	bool push(const char*d,int len,unsigned time_out_ms=-1)
	{
		int counter=10000;

		zclock c;
		for(;;)
		{
			int64_t ppos=ztomic::load(&m_ppos);
			int64_t gpos=ztomic::load(&m_gpos);

			if(ppos-gpos+len>size()|| !ztomic::cas(&m_ppos,&ppos,ppos+len))
			{
				if(c.count_ms()>=time_out_ms)
					return false;
				
				counter=yield(counter);
				continue;
			}

			int p=pos(ppos);
			int c=p+len-size();
			if(c<=0)
			{
				memcpy(m_base+p,d,len);
			}
			else
			{
				memcpy(m_base+p,d,len-c);
				memcpy(m_base,d+len-c,c);
			}

			break;
		}

		return true;
	}

	void free(int len)
	{
		ztomic::fetch_add(&m_gpos,len);
	}

	char*peek(int*len)
	{
		const int p=pos(m_ppos);
		const int g=pos(m_gpos);

		*len=g<=p?p-g:size()-g;
		return m_base+g;
	}

};
*/
#endif