#ifndef __higinet_zistream_h__
#define __higinet_zistream_h__

#include <stdint.h>
#include <string.h>
#include <vector>
#include <string>
#include <stdexcept>

struct stream_exception: public std::logic_error 
{
	explicit stream_exception(const char*e)
		:std::logic_error(e)	
	{
	}
	
	explicit stream_exception(const std::string&e)
		:std::logic_error(e)	
	{
	}
};

namespace endian
{
	struct flag
	{
#ifndef LITTLE_ENDIAN
		static const bool little=true;
#else
		static const bool little=false;
#endif
	};

	inline void cp_uint8_r(void*d,const void*s,size_t c)
	{
		uint8_t*di=(uint8_t*)d;
		const uint8_t*si=(const uint8_t*)s;

		while(c-->0)
			*di++=*--si;
	}


	inline void cp_uint8(void*d,const void*s,size_t c)
	{
		memcpy(d,s,c);
	}

	template<bool little> struct copy_impl
	{
		void operator()(void*d,const void*s,size_t)const
		{
		}
	};
	
	template<> struct copy_impl<true>
	{
		void operator()(void*d,const void*s,size_t count)const
		{
			cp_uint8 ((uint8_t*)d,(const uint8_t*)s,count);
		}
	};
	
	template<> struct copy_impl<false>
	{
		void operator()(void*d,const void*s,size_t count)const
		{ 
			cp_uint8_r (d, (const uint8_t*)s+count, count);
		}
	};

	inline void copy(void*d,const void*s,size_t count)
	{ 
		copy_impl<endian::flag::little>()(d,s,count);
	}
}

struct skip
{
	int m_count;
	skip(int count):m_count(count){}
};

template<typename T,typename F>
struct as
{
	T t;
	F&f;
	as(F&f_):f(f_){}
};

struct npc
{
	int m_count;
	npc(int c=-1):m_count(c) {}
	void reset(int count=-1){m_count=count;}
	operator int()const {return m_count;}
};

struct zistream
{
private:
	const uint8_t* m_buff;
	size_t m_pos,m_size;
	bool m_owner_buff;
	npc m_np;

	void assert_buff_size(size_t len)
	{
		if(len > m_size)
		{
			char buf[256];
			sprintf(buf, "无更多的数据:%s(size=%d,pos=%d,fetch=%d).", __FUNCTION__,(int)m_size,(int)m_pos,(int)len);
			throw stream_exception(buf);
		}
	}

public:
	const uint8_t*buff()const{return m_buff;}
	const uint8_t*cur_data()const{return m_buff+m_pos;}
	size_t pos()const{return m_pos;}

	void load(void*d,size_t len,size_t pos)
	{
		assert_buff_size(pos+len);
		endian::copy(d,m_buff+pos,len);
	}

	void load(void*d,size_t len)
	{
		load(d,len,m_pos);
		m_pos+=len;
	}
	int32_t load_int8 (size_t pos){int8_t  rc;load(&rc,sizeof(rc),pos);return rc;}
	int32_t load_int16(size_t pos){int16_t rc;load(&rc,sizeof(rc),pos);return rc;}
	int32_t load_int32(size_t pos){int32_t rc;load(&rc,sizeof(rc),pos);return rc;}
	int64_t load_int64(size_t pos){int64_t rc;load(&rc,sizeof(rc),pos);return rc;}

	int32_t load_int8 (){int8_t  rc; load(&rc,sizeof(rc)); return rc;}
	int32_t load_int16(){int16_t rc; load(&rc,sizeof(rc)); return rc;}
	int32_t load_int32(){int32_t rc; load(&rc,sizeof(rc)); return rc;}
	int64_t load_int64(){int64_t rc; load(&rc,sizeof(rc)); return rc;}


	uint32_t load_uint8 (size_t pos){uint8_t  rc;load(&rc,sizeof(rc),pos);return rc;}
	uint32_t load_uint16(size_t pos){uint16_t rc;load(&rc,sizeof(rc),pos);return rc;}
	uint32_t load_uint32(size_t pos){uint32_t rc;load(&rc,sizeof(rc),pos);return rc;}
	uint64_t load_uint64(size_t pos){uint64_t rc;load(&rc,sizeof(rc),pos);return rc;}

	uint32_t load_uint8 (){uint8_t  rc; load(&rc,sizeof(rc)); return rc;}
	uint32_t load_uint16(){uint16_t rc; load(&rc,sizeof(rc)); return rc;}
	uint32_t load_uint32(){uint32_t rc; load(&rc,sizeof(rc)); return rc;}
	uint64_t load_uint64(){uint64_t rc; load(&rc,sizeof(rc)); return rc;}

	std::vector<uint8_t> load_bytes(size_t count)
	{
		std::vector<uint8_t> rc(count,0);

		load(&*rc.begin(), count);

		return std::move(rc);
	}

	std::string load_string(size_t count)
	{
		std::string rc(count,' ');

		load(&*rc.begin(), count);

		return std::move(rc);
	}

	bool   eof() const {return m_pos >= m_size; }
	size_t size()const {return m_size;}
	void   set_size(uint32_t size){m_size=size;}

public:

	zistream(const void*pbuf,size_t buf_size,bool owner_buff=false)
		:m_buff((const uint8_t*)pbuf)
		,m_pos(0)
		,m_size(buf_size)
		,m_owner_buff(owner_buff)
	{
	}

	~zistream()
	{
		if(m_owner_buff && m_buff)
		{
			::free((void*)m_buff);
		}
	}

	friend zistream& operator>>(zistream&is, uint8_t &i) { i=is.load_uint8 ();return is; }
	friend zistream& operator>>(zistream&is, uint16_t&i) { i=is.load_uint16();return is; }
	friend zistream& operator>>(zistream&is, uint32_t&i) { i=is.load_uint32();return is; }
	friend zistream& operator>>(zistream&is, uint64_t&i) { i=is.load_uint64();return is; }

	friend zistream& operator>>(zistream&is, int8_t &i) { i=is.load_int8 ();return is; }
	friend zistream& operator>>(zistream&is, int16_t&i) { i=is.load_int16();return is; }
	friend zistream& operator>>(zistream&is, int32_t&i) { i=is.load_int32();return is; }
	friend zistream& operator>>(zistream&is, int64_t&i) { i=is.load_int64();return is; }


	friend zistream& operator>>(zistream&is, const npc&i) { is.m_np.reset(i);return is;}
	friend zistream& operator>>(zistream&is, void*x) 
	{ 
		if(is.m_np <= 0)
			return is;

		is.load(x,is.m_np);
		is.m_np.reset();

		return is;
	}

	friend zistream& operator>>(zistream&is, const skip&s)
	{ 
		is.assert_buff_size(s.m_count);
		is.m_pos+=s.m_count;

		return is;
	}
};

class zostream
{
private:
	uint8_t*  m_buff;
	size_t    m_pos,m_size;
	int       m_owner_buff;

	void grow(size_t nsize)
	{
		if(m_buff!=nullptr && nsize<m_size)
			return;

		while(m_size<nsize)
			m_size<<=1;
		
		m_buff=(uint8_t*) realloc(m_buff,m_size);
		m_owner_buff=1;
	}

public:
	zostream& save(const void*pbuf,int len,size_t pos)
	{
		grow(len+pos);
		endian::copy(m_buff+pos,pbuf,len);

		if(len+pos>m_pos)
			m_pos=len+pos;

		return *this;
	}

	zostream& save(const void*pbuf,int len)
	{
		grow(len+m_pos);
		endian::copy(m_buff+m_pos,pbuf,len);

		m_pos+=len;

		return *this;
	}

	zostream& save(int8_t   i,size_t pos){return save(&i,sizeof(i),pos);}
	zostream& save(int16_t  i,size_t pos){return save(&i,sizeof(i),pos);}
	zostream& save(int32_t  i,size_t pos){return save(&i,sizeof(i),pos);}
	zostream& save(int64_t  i,size_t pos){return save(&i,sizeof(i),pos);}

	zostream& save(int8_t  i) {return save(&i,sizeof(i));}
	zostream& save(int16_t i) {return save(&i,sizeof(i));}
	zostream& save(int32_t i) {return save(&i,sizeof(i));}
	zostream& save(int64_t i) {return save(&i,sizeof(i));}

	zostream& save(uint8_t   i,size_t pos){return save(&i,sizeof(i),pos);}
	zostream& save(uint16_t  i,size_t pos){return save(&i,sizeof(i),pos);}
	zostream& save(uint32_t  i,size_t pos){return save(&i,sizeof(i),pos);}
	zostream& save(uint64_t  i,size_t pos){return save(&i,sizeof(i),pos);}
	zostream& save(uint8_t  i) {return save(&i,sizeof(i));}
	zostream& save(uint16_t i) {return save(&i,sizeof(i));}
	zostream& save(uint32_t i) {return save(&i,sizeof(i));}
	zostream& save(uint64_t i) {return save(&i,sizeof(i));}

	zostream& save(const std::string&s)
	{
		return save(s.c_str(), s.size());
	}

	zostream& save(const char*s)
	{
		return save(s, strlen(s));
	}

	friend zostream& operator<<(zostream&os, uint8_t i)  { return os.save(i);}
	friend zostream& operator<<(zostream&os, uint16_t i) { return os.save(i);}
	friend zostream& operator<<(zostream&os, uint32_t i) { return os.save(i);}
	friend zostream& operator<<(zostream&os, uint64_t i) { return os.save(i);}
	friend zostream& operator<<(zostream&os, int8_t i)  { return os.save(i);}
	friend zostream& operator<<(zostream&os, int16_t i) { return os.save(i);}
	friend zostream& operator<<(zostream&os, int32_t i) { return os.save(i);}
	friend zostream& operator<<(zostream&os, int64_t i) { return os.save(i);}

	friend zostream& operator<<(zostream&os, const std::string &i) { return os.save(i);}
	friend zostream& operator<<(zostream&os, const char*i) { return os.save(i);}

public:
	zostream(size_t buf_size,void*buf=nullptr)
		:m_buff((uint8_t*)buf)
		,m_pos(0)
		,m_size(buf_size)
	{
		if(m_buff)
		{
			m_owner_buff=0;
		}
	}

	~zostream()
	{
		if(m_owner_buff && m_buff)
			free(m_buff);
	}
	
	const uint8_t*data()const{return m_buff;}const
	uint8_t      *data(){return m_buff;}

	size_t size()const {return m_pos;}
};

#endif