/*!
@auther d金魚
@since 2003/08/30
*/


#ifndef _dKingyoUtilClass_global_h_
#define _dKingyoUtilClass_global_h_


#include "dkutilDefined.h"



namespace dkutil{

///temporary_bufferの実態はこれだ！(爆
typedef boost::scoped_array<char> temporary_buffer;



///boost::scoped_arrayのパクリ･･･(どうか、このクラスを廃止の方向にしないで下さい。
///@note boost::scoped_array<char>はnew char[]がめんどい！
class scoped_buffer : protected boost::noncopyable{
	typedef size_t size_type ;
	char *mptr;
	size_type  msize;
	void Allocate(char *buf,size_type size){
		if(size==0){
			mptr= NULL;
			msize = 0;
			return;
		}
		char *tmp=(char*)DKUTIL_SAFE_REALLOC(buf,size);
		if(!tmp){
			DK_TRUE_ASSERT_OR_THROW(!tmp,std::logic_error("メモリが足らん！"));
		}
		//０初期化
		memset(tmp,0,size);
		mptr = tmp;
		msize = size;
	}
public:
	scoped_buffer(size_type size=0){
		Allocate(NULL,size);
	}
	~scoped_buffer(){
		clear();
	}
	///ポインタを得る
	char *get()const{return mptr;}
	///get()のwrapper
	char *data()const{return get();}
	size_type capacity()const{return msize;}
	void clear(){DKUTIL_SAFE_FREE(mptr);msize=0;}
	///有効かどうか･･･
	bool isValid()const{return mptr != NULL;}
	///memoryをclearしてサイズを変更するみたいな？
	void reset(size_type size)
  {
		DK_TRUE_ASSERT_OR_THROW(size==0,std::invalid_argument("scoped_buffer reset"));
		clear();
		Allocate(NULL,size);
  }
	size_type size()const{return msize;}
	///いわいるreallocみたいな？
	void resize(size_type size){
		Allocate(mptr,size);
	}
	inline char operator [](size_type s){
		{//配列は０から始まる
			DK_TRUE_ASSERT_OR_THROW(s >= msize,std::overflow_error("scoped_buffer overflowなり"));
		}
		return get()[s];
	}

};


/*!
なんてネーミングだ！英語が分からないからです･･･すみません。
vectorッぽい事が出来ます。
*/
template<class T>
class offset_pusher : protected boost::noncopyable{
public:
	typedef std::size_t size_type;
private:
	T *mbuf;
	size_type moffset;
	size_type msize;
protected:
	void SetOffset(size_type o){	moffset = o;}
	size_type GetOffset(){return moffset;}
	void OffsetPlus(size_type plus){ moffset += plus;}
	
public:

	offset_pusher(const T *buff,size_type size) : mbuf(const_cast<T *>(buff)){
		moffset=0;
		msize = size;
	}
	T *now()const{return &mbuf[moffset];}
	T *get()const{
		return mbuf;
	}
	///@return 今使用しているTの数を返す
	size_type size()const{return moffset;}
	///@return 入れることのできるTの数を返す
	size_type capacity()const{return msize;}
	/*!
	@param item[in] 入れるもの
	@param num[in] itemの数
	@return edk_SUCCEEDED==成功 othrer==不成功 DKUTIL_SUCCEEDED()系マクロで確かめられます。
	@throw 最初からすでに入らないと分かりきっている場合はstd::out_of_rangeを投げます。
	*/
	///プッシュするだけ･･･
	int  push_back(const T *item,size_type num){
		int result=edk_SUCCEEDED;
		if(msize == moffset && //オフセットとbufferが同じ
			msize != 0)//buffer が０でない
		{
			throw std::out_of_range("すでにバッファは使いきっちまったっつーの!!");
		}
		if(msize - moffset < num){
			result = edk_Not_Satisfactory;
			num = msize - moffset;
		}
		{
			size_type i=0;
			for(;i<num;i++){
				mbuf[moffset + i] = item[i];
			}
			moffset += i;
		}
		return result;
	}
	void reset(const T *buff,size_type size){
		mbuf = const_cast<T *>(buff);
		moffset=0;
		msize = size;
	}
};


/*!
いわいるバッファー　クラス。boost::arrayやstd::vectorでは出来ないと思われるmemcpy系処理を担う
前まで、std::vectorを継承していたが、
vectorはmemcpy的な事をするのに非常に時間がかかるため、断念。

@note
<b>規格：</b>
<ul>
<li>空いているバッファは０で埋め尽くされる。
</ul>
*/
template<typename T,typename A=std::allocator<T> >
class buffer_base{
public:
	typedef char value_type;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;
	typedef size_t size_type;
	typedef T * pointer;
	typedef T& reference;
	typedef const T &const_reference;
	typedef const T *const_pointer;
private:
	T *buff;//バッファ
	size_type mcapacity;//許容容量(数。byte単位ではない)
	size_type msize;//使用済みサイズ(数。byte単位ではない)
	bool mEmpty;//エンプティか？
	void _construct(){
		mEmpty=true;buff=NULL;mcapacity=0;msize=0;
	}
	A mAlloc;
	///バッファ初期かヘルパ
	void init_buf(void *buf,size_type num){
		::memset(buf,0,sizeof(T) * num);
	}
protected:
	void SetEmpty(bool e){	mEmpty = e;}
	void SetSize(size_type s){ msize = s;}
	void SetCapacity(size_type c){mcapacity = c;}
	/*
	struct buffer_SerializeStruct{
		size_type Capacity;
		size_type Size;
		bool Empty;
		buffer_SerializeStruct(
			size_type gCapacity,size_type gSize,bool gEmpty) : 
		Capacity(gCapacity) , Size(gSize) , Empty(gEmpty){
		}	
	};
	std::size_t GetSerializeSize()const{
		size_type lsize = sizeof(buffer_SerializeStruct); 
		lsize += mcapacity + private_::serialize_offset_pusher::get_top_size();
		return lsize;
	}
	bool GetSerializeData(char *buf,std::size_t bufsize)const{
		size_type lsize = GetSerializeSize();
		if(bufsize < lsize){
			return false;
		}
		
		
		buffer_SerializeStruct dat(mcapacity,msize,mEmpty);
		private_::serialize_offset_pusher pusher(buf,bufsize);
		pusher.push_back((const char *)&dat,sizeof(dat));
		
		if(DKUTIL_FAILED(pusher.push_back((const char *)data(),capacity())))
		{
			 DEBUGASSERT(!"んなはずあるか！");
		}
		
		return true;
	}

	bool SetSerialize(char *buf,std::size_t size){
		clear();
		_construct();
	
		DK_TRUE_ASSERT_OR_THROW(
			(char)isLittleEndian() != buf[0],
			std::logic_error("Endianが違うからシリアライズできないなり(え、エンディアンの違いを意識しない機構を用意しろだって！？")
		);
		//ずらす･･･
		buf += private_::serialize_offset_pusher::get_top_size();
		
		//datにアっタッチする
		buffer_SerializeStruct *dat;
		dat = (buffer_SerializeStruct *)&buf[0];

		SetEmpty(dat->Empty);
		SetCapacity(dat->Capacity);
		SetSize(dat->Size);
		if(false==SetBuff(&buf[sizeof(buffer_SerializeStruct)],dat->Capacity)){
			DEBUGASSERT(!"そんなぁ！");
		}
		return true;
	}
	*/
public:


	buffer_base(){_construct();}
	buffer_base(size_type size){
		_construct();
		resize(size);
	}
	///こぷぃ〜こんすとらくたぁ〜。　私って結構これ、忘れるんですよ。
	buffer_base(const buffer_base &data){
		_construct();
		if(data.isValid()){
			reserve(data.mcapacity);
			if(!data.empty()){
				SetBuff(data.buff,data.mcapacity);
			}
		}
	}

	virtual ~buffer_base(){
		clear();
	}
	///メモリ領域からなにからすべてを消し去る。
	void clear(){
		if(isValid()){
			mAlloc.deallocate(buff);
			buff=NULL;
		}
		mEmpty=true;
		mcapacity=0;
		msize=0;
	}
	///今までのバッファをすべて開放して、確保しなおす、前のバッファの内容は破棄される。
	int reserve(size_type num){
		if(num==0) return edk_FAILED;
		clear();
		buff = mAlloc.allocate(num);
		if(!buff) return edk_FAILED;
		init_buf(buff,num);
		mcapacity = num;
		return edk_SUCCEEDED;
	}
	///reserve()のwrapper
	int resize(size_type size){
		return reserve(size);
	}
	/*!
	@param data[in] コピーするデータ
	@param num[in] dataの数
	@return false == 失敗
	*/
	///バッファにコピーする。numがcapacity()より大きければこの関数は失敗する
	bool SetBuff(const T *data,size_type num){
		if(! isValid())
		{//バッファ確保していなかったら確保
			reserve(num);
		}else{//既にバッファ確保済みならバッファを初期化
			init_buf(buff,num);
		}
		bool b=DKUTIL_SUCCEEDED(dkmemcpy(buff,mcapacity * sizeof(T),data,sizeof(T) * num));
		if(!b){return false;}
		mEmpty=false;
		msize = num;//コピー済みサイズ保存

		return true;
	}
	/*!
	@param buffsize[in] 内部で使用するバッファのサイズ
	@param str[in] 文字列
	@throw sizeof(T) != sizeof(char) だったら std::logic_errorを投げる。
	@return もし、capacity()内に収まらなかったら、収まらないぶんは切り捨てられる。(その時の戻り値はfalse
	*/
	///もちろんNULL文字も考慮されている
	bool SetString(size_type buffsize,const char *str,...){
		if(sizeof(T) != sizeof(char)) throw std::logic_error("buffer_base/ sizeof(T) != sizeof(char)");
		
		temporary_buffer b(new char[buffsize]);
		SET_VA_LIST(b.get(),buffsize,str);
		size_type len = strlen(b.get());
		if( (int)(capacity() - len) <= 0)
		{//lenがcapacity()を超えていたら･･･
			b.get()[capacity() - 1] = NULL;
			len = capacity();
		}
		else
		{//超えていないのなら			
			
		}
		return SetBuff(b.get(),len);
	}
	bool SetString(const char *str,...){
		if(sizeof(T) != sizeof(char))
		{
			//sizeof(bool)==sizeof(char)だけど、
			//ん〜。typeidは嫌だし〜〜。SetStringだけのためにねぇ〜。
			throw std::logic_error("buffer_base/ sizeof(T) != sizeof(char)");
		}
		size_type buffsize = strlen(str);
		buffsize += 256;
		temporary_buffer b(new char[buffsize]);
		/*va_list VaList;
		va_start( VaList , str ) ;
		_vsnprintf( b.get() ,buffsize, str , VaList ) ;
		va_end( VaList ) ;*/
		SET_VA_LIST(b.get(),buffsize,str);
		size_type len = strlen(b.get());
		if( (int)(capacity() - len) <= 0)
		{//lenがcapacity()を超えていたら･･･
			b.get()[capacity() - 1] = NULL;
			len = capacity();
		}
		else
		{//超えていないのなら			
			
		}
		return SetBuff(b.get(),strlen(b.get()));
		/*buffer_base b;
		b.reserve(buffsize + 1);
		SET_VA_LIST(b.data(),b.capacity(),str);
		return SetBuff(b.data(),strlen(b.data()));*/
	}
	bool isValid()const{
		if(!buff){return false;}
		return true;
	}
	bool empty()const{
		return mEmpty;
	}
	///既に使用済みのTの１個分の領域の数を得ます。( std::vectorと同じ^^;
	size_type size()const{return msize;}
	///内部で確保しているTの１個分の領域の数を得ます。( std::vectorと同じ^^;
	size_type capacity()const{return mcapacity;}
	///size() * sizeof(T)よってbyte単位のサイズが得れる。
	size_type byte_size()const{return size() * sizeof(T);}
	///capacity() * sizeof(T)よって、byte単位のサイズが得れる。
	size_type byte_capacity()const{return capacity() * sizeof(T);}
	
	///危険な香りの data()... 内部のポインタ配列を戴くので非常に危険。使用に注意
	pointer data(){return buff;}
	const_pointer data()const {return buff;}
	///std::vectorのatと同じです。
	reference at(size_type s){
		if(!isValid()) throw std::logic_error("buffer_base/ not initialized");
		if(size() < s) throw std::out_of_range("buffer_base::at / out of range");
		return buff[s];
	}
	const_reference at(size_type s)const{
		if(!isValid()) throw std::logic_error("buffer_base/ not initialized");
		if(size() < s) throw std::out_of_range("buffer_base::at / out of range");
		return buff[s];
	}
	///at()のWrapperです。
	reference operator[](size_type s){	return at(s);}
	const_reference operator[](size_type s)const{	return at(s);}
	
	friend bool operator==(buffer_base<T,A> t1,buffer_base<T,A> t2){
		return DKUTIL_SUCCEEDED(dkmemcmp(t1.data(),t1.size(),t2.data(),t2.size()));
	}

};


///byte_bufferの実態はこれだ！(苦笑
typedef buffer_base<char> byte_buffer;

/*!
@param WORKSPACE_SIZE 元から確保するバッファのサイズ(byte単位
@param BUFFER_T byte_bufferクラスと同じ仕様のバッファクラス(普通はこのままで事足りるでしょう。
@note
場合によってはヒープから確保しないバッファ･･･なんてバカなんだ。私は･･･
仕様用途:タスクワークの元とか<br>
処理内容:BUFFER_T型のバッファクラスを使って動的確保か、
バッファサイズが十分ならばcharのバッファを使って処理コストを下げる!??<br>
主張:なんか、byte_buffer,boost::arrayやstd::vectorとかぶっているけど、
memcpyができる事を中心とした、ちと違うクラスって事で、認識しといてください。
*/
template<std::size_t WORKSPACE_SIZE = 64,class BUFFER_T=byte_buffer>
class WorkSpaceBase{
public:
	typedef BUFFER_T BUFFER_TYPE;
	enum{
		enuWorkSize = WORKSPACE_SIZE,
	};
#	ifdef _MSC_VER
	typedef BUFFER_T::size_type size_type;
	typedef BUFFER_T::pointer pointer;
	typedef BUFFER_T::const_pointer const_pointer;
#else
	typedef size_t size_type;
	typedef char *pointer;
	typedef const pointer const_pointer;
#endif

private://めんどくさいし〜。
	char WorkData[enuWorkSize];

	BUFFER_T mDWorkSpace;
	void _construct(){

		NULL_CHAR_ARRAY(WorkData);
		mDWorkSpace.clear();
	}
public:


	///constructor 
	WorkSpaceBase(){
		_construct();
	}
	///copy constructor
	WorkSpaceBase(const WorkSpaceBase<WORKSPACE_SIZE,BUFFER_T> &data){
		_construct();
		if(data.mDWorkSpace.empty()){
			SetBuff(data.WorkData,enuWorkSize);
		}else{
			mDWorkSpace = data.mDWorkSpace;
		}
	}
	//destructor
	virtual ~WorkSpaceBase(){
		clear();
	}
	///ワークスペースを開放する。（ ReserveWorkOnce()を呼び出している場合 ）
	void clear(){
		mDWorkSpace.clear();
		NULL_CHAR_ARRAY(WorkData);
	}
	/*!
	@return すでにこの関数を呼び出している場合、clear()を呼び出さないとこの関数は失敗する。
	また、WORKSPACE_SIZE分内部で確保しているのでそれ以下の場合はfalseである。
	*/
	bool reserve(size_type size){
		if(size <= enuWorkSize || (!mDWorkSpace.empty())){
			return false;
		}
		mDWorkSpace.resize(size);
		return true;
	}
	///reserveのWrapper
	bool resize(size_type size){
		return reserve(size);
	}
	///ワークスペースのアドレスをゲットする（後は各自、キャストしてください。
	pointer data(){
		if(mDWorkSpace.empty()){
			return WorkData;
		}else{
			return mDWorkSpace.data();
		}
	}
	const_pointer data()const{
		if(mDWorkSpace.empty()){
			return WorkData;
		}else{
			return mDWorkSpace.data();
		}
	}
	///バッファの許容サイズをゲットする。
	size_type capacity()const{
		if(mDWorkSpace.empty()){
			return enuWorkSize;
		}else{
			return mDWorkSpace.size();
		}
	}

	/*!
	@param src[in] コピー元 buffer
	@param size[in] srcのサイズ
	@return false = 失敗 
	*/
	///srcのメモリ領域をバッファにコピーする。
	bool SetBuff(const char *src,size_type size){
		int r;
		if(!mDWorkSpace.isValid()){
			NULL_CHAR_ARRAY(WorkData);
			r = dkmemcpy(WorkData,capacity(),src,size);
			return DKUTIL_SUCCEEDED(r);
		}else{
			return	mDWorkSpace.SetBuff(src,size);
		}
		//return false;
	}

	bool SetString(size_type buffsize,const char *str,...){
		
		temporary_buffer b(new char[buffsize]);
		SET_VA_LIST(b.get(),buffsize,str);
		return SetBuff(b.get(),strlen(b.get()));
	}
	bool SetString(const char *str,...){
		
		size_type buffsize = strlen(str);
		buffsize += 256;
		temporary_buffer b(new char[buffsize]);
		SET_VA_LIST(b.get(),buffsize,str);
		return SetBuff(b.get(),strlen(b.get()));
	}
	
	
};

///カッチョ悪い<>な定義を無くすため
typedef WorkSpaceBase<> WorkSpace;


}//end of dkutil namespace
#endif//end of include once
