/**
@brief memory stream

@note

*/
#ifndef DKUTIL_MEMORY_MEMORY_STREAM_HPP
#define DKUTIL_MEMORY_MEMORY_STREAM_HPP

#include <dkutil_c/dkc.h>
#include <dkutil/policy/allocate.hpp>
#include <dkutil/memory/interface.hpp>
#include <dkutil/buffer.hpp>

namespace dkutil{

	
namespace policy{

class memory_stream_operate_policy{
	byte_buffer mBuffer;
	seek_arg_type mOffset;
	void throw_error(const char *s)const
	{
		DKUTIL_THROW_OR_NOTICE(
			stream_error(s)
		);
	}
public:
	typedef std::size_t size_type;
	//typedef FILE * handle_type;

	BOOST_STATIC_CONSTANT(int,invalid_handle = NULL);
	
	memory_stream_operate_policy(){
		mfp = NULL;
	}
	
	~memory_stream_operate_policy(){
		close();
	}


	seek_arg_type tell()const{
		return mNowOffset;
	}
	/*size_t read(const void *buf,size_t size){
		check_fp();
		return fread(const_cast<void *>(buf),1,size,mfp);
	}
	size_t write(const void *buf,size_t size){
		check_fp();
		return fwrite(const_cast<void *>(buf),1,size,mfp);
	}*/
	size_t size_collect(size_t size)
	{
		seek_arg_type rs = size + mNowOffset;
		if(rs > (seek_arg_type)mBuffer.size()){
			rs = mBuffer.size() - mNowOffset;
		}else{
			rs = size;
		}
		dkcmFORCE_ASSERT(rs < LONGLONG_MAX);
		return (size_t)rs;
	}
	size_t read(void *buf,size_t size){
		uint8 *tp = mBuffer.get();
		size_t rs = size_collect(size);
		memcpy(buf,&tp[mNowOffset],rs);
		return rs;
	}
	size_t ref(void *buf,size_t size){
		synchronized swimming;
		check_fp();
		long t = 0;
		size_t rs;
		t = tell();
		rs = read(buf,size);
		if(false==seek(t,seek_begin)){
			throw_error("memory_stream_operate_policy::ref() seek error");
		}
		return rs;
	}
	size_t write(const void *buf,size_t size){
		uint8 *tp = mBuffer.get();
		size_t rs = size_collect(size);
		memcpy(&tp[mNowOffset],buf,rs);
		return rs;
	}
	bool eof()const{
		return mBuffer.size() <= mNowOffset;
	}
	bool error()const{
		return false;
	}
	bool seek(seek_arg_type offset, int origin ){
		seek_arg_type temp;
		switch(origin){
		case seek_begin:
			if(offset < 0 || temp > size()){
				return false;
			}
			mNowOffset = offset;
			break;
		case seek_current:
			temp = mNowOffset + offset;
			if(temp < 0 || temp > size()){
				return false;
			}
			mNowOffset += offset;
			break;
		case seek_end:
			temp = mNowOffset - offset;
			if(temp < 0 || temp > size()){
				return false;
			}
			mNowOffset -= offset;
			break;
		default:
			return false;
		}
		return true;
	}
	bool seek(size_type offset){
		if(offset > LONGLONG_MAX){
			throw_error("memory_stream_operate_policy::seek(size_type) offset argument value overflow"));
		}
		return seek(offset,seek_begin);
	}
	size_type size(){
		return mBuffer.size();

	}


};



}//end of policy namespace

typedef memory_stream_interface<policy::memory_stream_operate_policy> memory_stream;


class CMemoryStreamAdapter : public IMemoryStream
{
public:
	typedef size_t size_type;
	typedef CMemoryStreamAdapter self_type;
protected:
	DKC_MEMORYSTREAM_ADAPTER *mp;
public:
	CMemoryStreamAdapter(void *buff,size_type size){
		mp = NULL;
		reset(buff,size);
	}
	CMemoryStreamAdapter(const self_type &x)
	{
		operator=(x);
	}
	self_type &operator=(const self_type &x){
		clear();
		mp = dkcAllocMemoryStreamAdapterCopy(x.mp);
		if(!mp) DKUTIL_THROW_OR_NOTICE(stream_error("CMemoryStreamAdapter::operator=() error"));
		return *this;
	}
	virtual ~CMemoryStreamAdapter(){
		clear();
	}
	bool reset(void *buff,size_type size){
		clear();
		mp = dkcAllocMemoryStreamAdapter((unsigned char *)buff,size);
		if(!mp) return false;
		return true;
	}
	virtual bool write(const void *pd,size_t size,size_t *write_size)
	{
		
		int r = dkcMemoryStreamAdapterWrite(
			mp,pd,size);
	
		if(DKUTIL_FAILED(r)){
			*write_size = 0;
			return false;
		}
		*write_size = size;
		return true;
	}
	virtual bool read(void *pd,size_t size,size_t *read_size)
	{
	
		int r = dkcMemoryStreamAdapterRead(mp,pd,size,read_size);
		
		if(DKUTIL_FAILED(r)){
			//*read_size = 0;
			return false;
		}
		//*read_size = 0;
		return true;
	}
	
	/*!
	@param offset[in] origin指定から何バイト、シークするか。
	@param origin[in] 
	@note
	動作はfseekと同じです。（爆）
	originも実はfseekと同じ指定子をぶち込んでも動いちゃったりします＾＾；
	*/
	///シークする。
	virtual bool seek(seek_arg_type offset,int origin)
	{
		if(INT_MAX < offset) return false;
		int r = dkcMemroyStreamAdapterSeek(mp,(int)offset,origin);
		return DKUTIL_SUCCEEDED_BOOL(r);
	}
	///今のバッファの最初からのオフセットを返す。
	virtual seek_arg_type tell()const{

		return dkcMemoryStreamAdapterTell(mp);
	}
	/*!
	@note
	serialize_interfaceのポインタをぶち込めば、
	シリアライズしたり、デシリアライズしたりしてくれます。
	*/
	///シリアライズする。
	//virtual bool serialize(serialize_interface *) = 0;
	///内部バッファへのポインタを頂く（無効領域を含む。危険。
	///type()での戻り値がmemory_stream_typeの時のみに御使用ください。
	virtual const void *data()const{
		return mp->mBuffer;
		}
	///内部バッファのサイズ。
	virtual size_type size()const{
		return mp->mSize;
		}
	///内部バッファをリサイズする。
	virtual void resize(size_type size){
		
		DKUTIL_THROW_OR_NOTICE(stream_error("CLimitMemoryAdapterStream::resize() not defined function"));
		
	}
	///内部バッファをクリアする。
	virtual void clear(){
		//dkcMemoryStreamAdapterClear(mp);
		dkcFreeMemoryStreamAdapter(&mp);
	}
	///ストリームのタイプを取得
	virtual int type()const
	{
		return memory_stream_type;
	}
	virtual bool eof()const{
		return size() <= tell();
	}
	virtual bool good()const{
		return NULL != mp;
	}
};

class CLimitMemoryStream : public IMemoryStream{
public:
	typedef size_t size_type;
	typedef CLimitMemoryStream self_type;
	BOOST_STATIC_CONSTANT(size_t,default_init_size = 1024 * 64);
	BOOST_STATIC_CONSTANT(size_t,default_limit_size=UINT_MAX);
protected:
	DKC_MEMORYSTREAM *mp;
	size_t mLimit;
	
	///リミットサイズを算出する
	size_t limit_size(size_t size)
	{
		uint64 t = mp->mSize + size;
		size_t ws = size;
		if(t > (uint64)mLimit){
			dkcmNOT_ASSERT(UINT_MAX < (uint64)(t - (uint64)mLimit));
			ws = (size_t)(t - mLimit);
		}
		return ws;
	}
public:
	CLimitMemoryStream(size_type limit = default_limit_size){
		mp = NULL;
		mLimit = 0;
		reset(limit);
	}
	CLimitMemoryStream(const self_type &x)
	{
		operator=(x);
	}
	self_type &operator=(const self_type &x){
		clear();
		mp = dkcAllocMemoryStreamCopy(x.mp);
		if(!mp) DKUTIL_THROW_OR_NOTICE(stream_error("CLimitMemoryStream::operator=() error"));
		return *this;
	}
	virtual ~CLimitMemoryStream(){
		clear();
	}
	bool reset(size_type limit){
		clear();
		mLimit = limit;
		mp = dkcAllocMemoryStream(default_init_size);
		if(!mp) return false;
		return true;
	}
	virtual bool write(const void *pd,size_t size,size_t *write_size)
	{
		size_t ws = limit_size(size);
		int r = dkcMemoryStreamDynamicWrite(
			mp,pd,ws);
	
		if(DKUTIL_FAILED(r)){
			*write_size = 0;
			return false;
		}
		*write_size = ws;
		return true;
	}
	virtual bool read(void *pd,size_t size,size_t *read_size)
	{
		size_t ws = limit_size(size);
		int r = dkcMemoryStreamRead(
			mp,pd,ws,read_size);
		
		if(DKUTIL_FAILED(r)){
			return false;
		}
		return true;
	}
	
	/*!
	@param offset[in] origin指定から何バイト、シークするか。
	@param origin[in] 
	@note
	動作はfseekと同じです。（爆）
	originも実はfseekと同じ指定子をぶち込んでも動いちゃったりします＾＾；
	*/
	///シークする。
	virtual bool seek(seek_arg_type offset,int origin)
	{
		if(mLimit < offset) return false;
		int r = dkcMemoryStreamSeek(mp,(int)offset,origin);
		return DKUTIL_SUCCEEDED_BOOL(r);
	}
	///今のバッファの最初からのオフセットを返す。
	virtual seek_arg_type tell()const{

		return dkcMemoryStreamNowOffset(mp);
	}
	/*!
	@note
	serialize_interfaceのポインタをぶち込めば、
	シリアライズしたり、デシリアライズしたりしてくれます。
	*/
	///シリアライズする。
	//virtual bool serialize(serialize_interface *) = 0;
	///内部バッファへのポインタを頂く（無効領域を含む。危険。
	///type()での戻り値がmemory_stream_typeの時のみに御使用ください。
	virtual const void *data()const{
		return mp->mBuffer;
		}
	///内部バッファのサイズ。
	virtual size_type size()const{
		return mp->mSize;
		}
	///内部バッファをリサイズする。
	virtual void resize(size_type size){
		int r = dkcMemoryStreamResize(mp,size);
		if(DKUTIL_FAILED(r))
		{
			DKUTIL_THROW_OR_NOTICE(stream_error("CLimitMemoryStream::resize() error"));
		}
	}
	///内部バッファをクリアし開放する。
	virtual void clear(){
		dkcFreeMemoryStream(&mp);
		//dkcMemoryStreamClear(mp);
	}
	///ストリームのタイプを取得
	virtual int type()const
	{
		return memory_stream_type;
	}
	virtual bool eof()const{
		return size() <= tell();
	}
	virtual bool good()const{
		return NULL != mp;
	}
	
};

}//end of namespace




#endif