/**
@todo
構造案：
dktl/buffer.hppをベースクラス
scoped_buffer : boost::noncopyableにする。
scoped_bufferの冗長なメンバを廃止する。
徐々にscoped_bufferからchar_bufferやbyte_bufferに移行する。
*/
//#error 案をみる事
#ifndef DKUTIL_SCOPED_BUFFER_HPP
#define DKUTIL_SCOPED_BUFFER_HPP



#include <dkutil/macro.hpp>
#include <dkutil/policy/allocate.hpp>
#include <dkutil/policy/array_to_string.hpp>
#include <string>
#include <stdexcept>

namespace dkutil{



/**
@note
thread safe : no

*/
template<class IMPL,typename TYPE_T=char>
class scoped_buffer_base{
public:
	typedef std::size_t size_type ;
	typedef TYPE_T element_type;
	typedef IMPL policy_type;
	typedef scoped_buffer_base<IMPL,TYPE_T> self_type;
private:
	element_type *mptr;
	size_type  msize;
	policy_type ma;
	/*!
	@param buf[in] 元バッファ
	@param size[in] 欲しいサイズ
	*/
	bool Allocate(element_type *buf,size_type size){
		if(size==0){
			mptr= NULL;
			msize = 0;
			return false;
		}
		//element_type *tmp=(element_type*)DKUTIL_SAFE_REALLOC(buf,size);
		element_type *tmp=(element_type *)ma.realloc(buf,size);
		if(!tmp){
			std::string es = "scoped_buffer_base::Allocate() Output Memory";
			es += to_string_uint32(size);
			es += "byte";
			DKUTIL_THROW_OR_NOTICE(std::logic_error(es));
			//DK_TRUE_ASSERT_OR_THROW(!tmp,std::logic_error("メモリが足らん！"));
		}
		//０初期化
		//memset(tmp,0,size);
		mptr = tmp;
		msize = size;
		return true;
	}
public:
	scoped_buffer_base(size_type size=0){
		//try{
			Allocate(NULL,size);
		//catch(...){
		//	clear();
		//	throw;
		//}
	}
	scoped_buffer_base(const element_type *p,size_type size__){
		Allocate(NULL,size__);
		if(isValid()){
			dkcmFORCE_NOT_ASSERT( DKUTIL_FAILED(dkmemcpy(get(),size(),p,size__) ) );

		}
	}
	virtual ~scoped_buffer_base(){
		clear();
	}
	///バッファの先頭からdataをsizeバイトコピーする。（memcpyいちいち記述するの面倒なため＾＾；
	void copy(const element_type *data,size_type siz){
		if(size() < siz){
			if(false==reset(siz)){
				DKUTIL_THROW_OR_NOTICE(std::runtime_error("scoped_buffer_base::copy reset allocate error"));
				//DKUTIL_THROW_OR_NOTICE(std::length_error("scoped_buffer_base::copy length error"));
			}
		}
		memcpy(get(),data,siz);
	}
	///ポインタを得る
	element_type *get(){
		return mptr;
	}
	const element_type *get()const{
		return mptr;
		}
	///get()のwrapper
	element_type *data(){
		return get();
	}
	const element_type *data()const{
		return get();
		}
	///@see size()
	size_type capacity()const{
		return msize;
	}
	/*!
	@note
	<h4>なぜSTLのように今使っている領域のサイズを返さないようにしたのか？</h4>
	なぜなら、scoped_bufferは確保したサイズすべてのバッファを使っていると見なす為です。
	後ろの方を使っていなくてもバッファとして確保しているわけですからそこらへんの管理まではしないものです。

	また、それらの管理まで行き届いたクラスは･･･

	*/


	///STLのように今使っているサイズではなく、確保したバッファのサイズが返る
	///つまり size() == capacity() である。（スマン、分かりにくくて･･･Effective C++ではこう言うのいけないって書いていたけどね･･･)
	size_type size()const{
		return msize;
	}

	void clear(){
		DKUTIL_SAFE_FREE(mptr);
		msize=0;
	}
	///有効かどうか･･･
	bool isValid()const{
		return mptr != NULL;
	}
	///isValid()のラッパー　スマンなぁ〜
	bool good()const{
		return isValid();
	}
	///memoryをclearしてサイズを変更するみたいな？
	bool reset(size_type size)
  {
		//DK_TRUE_ASSERT_OR_THROW(size==0,std::invalid_argument("scoped_buffer reset"));
		clear();
		if(size==0){
			return false;
		}
		return Allocate(NULL,size);
  }

	///いわいるreallocみたいな？
	void resize(size_type size){
		Allocate(mptr,size);
	}
	///引数のsizeの容量より小さかったらメモリを確保
	void if_resize(size_type size){
		if(msize >= size){
			return;
		}
		resize(size);
	}
	inline element_type & operator [](size_type s){
		{//配列は０から始まる
			if(s >= msize){
				DKUTIL_THROW_OR_NOTICE(std::overflow_error("scoped_buffer overflowなり"));
			}
		}
		return (get())[s];
	}
	inline const element_type & operator [](size_type s)const{
		{//配列は０から始まる
			if(s >= msize){
				DKUTIL_THROW_OR_NOTICE(std::overflow_error("scoped_buffer overflowなり"));
			}
		}
		return (get())[s];
	}
	friend bool operator ==(const self_type &x,const self_type &y){
#	ifdef DEBUG
		const element_type *a = x.get();
		const element_type *b = y.get();
		size_t xs = x.size();
		size_t ys = y.size();
		return DKUTIL_SUCCEEDED(dkmemcmp(a,xs,b,ys));
#	else
		return DKUTIL_SUCCEEDED(dkmemcmp(x.get(),x.size(),y.get(),y.size()));
#	endif
	}
	friend bool operator != (const self_type &x,const self_type &y){
		return !(x==y);
		//return DKUTIL_FAILED_BOOL(dkmemcmp(x.get(),x.size(),y.get(),y.size()));
	}
	self_type &operator=(const self_type &x){
		reset(x.size());
		dkmemcpy(get(),size(),x.get(),x.size());
		return (*this);
	}
	///non thread safe
	std::string to_string()const{

		policy::array_to_string<element_type> a;
		size_t le = a.less_length(get(),size());
		return a.convert(get(),le,true);
	}
	



};

///malloc系を使うscopedバッファ
typedef scoped_buffer_base<policy::allocate_policy_malloc,char> scoped_buffer_char;
typedef scoped_buffer_char scoped_buffer;

typedef scoped_buffer_base<policy::allocate_policy_malloc,BYTE> scoped_buffer_byte;


#ifdef WIN32
///GlobalAllocを使うscopedバッファ
typedef scoped_buffer_base<policy::allocate_policy_globalalloc,char> scoped_globalalloc_buffer;

typedef scoped_buffer_base<policy::allocate_policy_globalalloc,BYTE> scoped_globalalloc_buffer_byte;
#endif
/*
///non thread safe
template<class T1,class T2>
inline std::string to_string(const scoped_buffer_base<T1,T2> &x){
	typedef scoped_buffer_base<T1,T2> obj;
	std::string s;
	obj::size_type i = 0;
	obj::element_type e = 'a';
	obj::size_type se = x.size();
	
	for(;i<se;i++)
	{
		
		if('\0' == e && '\0'==x[i]){
			throw std::runtime_error("scoped_buffer to_string function: cannot convert");
		}
		s.append(x[i]);
		
		e = x[i];
	}

	return s;

}
*/

}//end of dkutil namespace



#endif
