/*
  basic stream class - it's similar to STL streams
*/

#ifndef BOB_DOM_STREAM_INCLUDED
#define BOB_DOM_STREAM_INCLUDED

#include "../common/indian.h"
#include "../common/ext_stream.h"

/* 
	bob_dom_istream - input template stream
	bob_dom_ostream - output template stream
*/

template <class Ty>
class bob_istream : public ext::stream_base
{
	public:
		typedef size_t size_type;
		typedef int offset_type;
		typedef Ty value_type;
		typedef Ty* value_type_ptr;
		typedef ext::stream_base base;
		typedef const Ty* const_value_type_ptr;
		
		virtual size_type read(value_type_ptr buffer, size_type size)=0;
		virtual bool advance(offset_type off)=0;
		virtual size_type tell() const = 0;
};

template <class Ty>
class bob_dom_ibufferstream : public bob_istream<Ty>
{
	public:
		typedef bob_istream<Ty> base;
		typedef int offset_type;
		
	private:
		const_value_type_ptr m_buffer;
		const_value_type_ptr m_ptr;
		size_type m_size;
		size_type m_avail;
		
	public:
		bob_dom_ibufferstream() { rdbuff(0, 0); clear(); }
		bob_dom_ibufferstream(const_value_type_ptr *buffer, size_type size) { rdbuff(buffer, size); }
		
		const_value_type_ptr rdbuff() const { return m_ptr; }
		void rdbuff(const_value_type_ptr buffer, size_type size) 
		{ 
			m_buffer=m_ptr=buffer; 
			m_avail=m_size=size;
			clear();
		}
		
		void clear(state s=goodbit) 
		{ 
			base::clear(s);
			setstate(m_buffer ? goodbit : badbit); 
		}

		
		size_type read(value_type_ptr buffer, size_type size);
		
		size_type tell() const { return m_ptr - m_buffer; }
		size_type size() const { return m_size; }
		size_type avail() const { return m_avail; }
		
		bool advance(offset_type offset)
		{
			if((offset > (offset_type)avail()) || ((offset_type)tell() + offset < 0))
				setstate(failbit);
			else{
				m_ptr+=offset;
				m_avail-=offset;
			}
			if(avail()==0) 
				setstate(eofbit);
			else
				clear((state)(rdstate() & ~eofbit));
			return good();
		}
		
		void rewind()
		{
			m_ptr=m_buffer;
			m_avail=m_size;
			clear();
		}		
		
		
		bob_dom_ibufferstream& operator ++()
		{
			advance(1);
			return *this;
		}
		
		bob_dom_ibufferstream& operator --()
		{
			advance(-1);
			return *this;
		}
		
		bob_dom_ibufferstream& operator +=(offset_type offset)
		{
			advance(offset);
			return *this;
		}
		
		bob_dom_ibufferstream& operator -=(offset_type offset)
		{
			advance(offset * -1);
			return *this;
		}
};

template <class Ty>
typename bob_dom_ibufferstream<Ty>::size_type bob_dom_ibufferstream<Ty>::read(value_type_ptr buffer, size_type s)
{
	if(!good()) return -1;
	if(avail() < s){
		setstate(failbit);
		return -1;
	}
	else{
		memcpy(buffer, rdbuff(), s);
		advance((offset_type)s);
		return s;
	}
}

typedef bob_dom_ibufferstream<unsigned char> bob_dom_ibytestream;

inline 
bob_dom_ibytestream& operator >> (bob_dom_ibytestream& is, int& right)
{
	is.read((bob_dom_ibytestream::value_type_ptr)&right, sizeof(right));
	right=be2le((unsigned int)right);
	return is;
}

inline
bob_dom_ibytestream& operator >> (bob_dom_ibytestream& is, short& right)
{
	is.read((bob_dom_ibytestream::value_type_ptr)&right, sizeof(right));
	right=be2le((unsigned short) right);
	return is;
}

#include <memory.h>

inline
bob_dom_ibytestream& operator >> (bob_dom_ibytestream& is, char*& right)
{
	bob_dom_ibytestream::const_value_type_ptr data=is.rdbuff();
	bob_dom_ibytestream::const_value_type_ptr pos=(bob_dom_ibytestream::const_value_type_ptr)memchr(data, 0, is.avail());
	if(pos){
		bob_dom_ibytestream::offset_type s=(bob_dom_ibytestream::offset_type)(pos - data + 1);
		right=new char[s];
		memcpy(right, data, s);
		is.advance(s);
	}
	else 
		right=0;
	return is;
}

template <class Ty>
class bob_ostream : public ext::stream_base
{
	public:
		typedef Ty value_type;
		typedef Ty* value_type_ptr;
		typedef const Ty const_value_type;
		typedef const Ty* const_value_type_ptr;
		typedef size_t size_type;
		
		// flags for formating output
		static const hex=1;
		
		virtual size_type write(const_value_type_ptr data, size_type size)=0;
		virtual bool put(const_value_type &ch)=0;
		
		virtual size_t tell()=0;
};



#endif // !defined(BOB_DOM_STREAM_INCLUDED)