/**
@file dkcStream.hpp
@brief dkutil_cのdkcStream.hのラッパー
*/
#ifndef DKUTIL_FILE_SYSTEM_DKC_STREAM_HPP
#define DKUTIL_FILE_SYSTEM_DKC_STREAM_HPP

#include <dkutil/filesystem/interface.hpp>
#include <dkutil/filesystem/utility.hpp>


namespace dkutil{

class CdkcStream : public IFileStream{
	DKC_STREAM *mp;
	///ファイル名
	std::string mfilename;

	bool mIs64Bit;
	
	void check_object__()const{
		if(NULL==mp){
			DKUTIL_THROW_OR_NOTICE(std::runtime_error("CdkcStream inner stream object is NULL"));
		}
	}
		/*
	struct default_read_functor{
		bool operator()(IFileStream *ps,void *pd,size_t size,size_t *readsize){
			return ps->read(pd,size,readsize);
		}
	};
	struct default_write_functor{
		bool operator()(IFileStream *ps,void *pd,size_t size){
			return ps->write(pd,size);
		}
	};*/
	/**
	@param p[in] データへのポインタ
	@param size[in] バイト単位のサイズ
	*/
	template<
		typename T,
		class ReadWriteFunctor
	>
	void serialize_helper(T *p,size_t size,ReadWriteFunctor func){
		//T t = c;//コピー
		bool r;//戻り値

		//メイン処理
		if(is_read_mode())
		{
					//エラー処理
			if(error()){
				goto ErrorException;
			}
			
			size_t readsize;
			//r = read(&t,sizeof(t),&readsize);
			r = func(this,p,size,&readsize);

			if(readsize != size && eof()==false){
				goto OperateException;
			}

		}else{
			//r = write(&t,sizeof(t));
			r = func(this,p,size);
			
			if(false==r){
				if(error()){
					goto ErrorException;
				}else{
			
					goto OperateException;
				}
			}
		}
		

		//Update
		//c = t;
		return;
	OperateException:
		DKUTIL_THROW_OR_NOTICE(filesystem_error("IFileStream::operator <<(...) operate exception"));
		return;
	ErrorException:
		DKUTIL_THROW_OR_NOTICE(filesystem_error("IFileStream::operator <<(...) error exception"));
		return;
	}
protected:

	struct functor__s8{
		friend CdkcStream;
		bool operator()(CdkcStream *ptr,void *pd,size_t size){
			bool r;
			int tr = dkcStreamWrite(ptr->getObj(),pd,size);
			r = DKUTIL_SUCCEEDED_BOOL(tr);
			return r;
		}
		bool operator()(CdkcStream *ptr,void *pd,size_t size,size_t *readsize){
			bool r;
			int tr = dkcStreamRead(ptr->getObj(),pd,size,readsize);
			r = DKUTIL_SUCCEEDED_BOOL(tr);
			return r;
		}
	};
	struct functor__s16{
		friend CdkcStream;
		bool operator()(CdkcStream *ptr,void *pd,size_t size){
			bool r;
			int tr = dkcStreamWrite16(ptr->getObj(),pd,size);
			r = DKUTIL_SUCCEEDED_BOOL(tr);
			return r;
		}
		bool operator()(CdkcStream *ptr,void *pd,size_t size,size_t *readsize){
			bool r;
			int tr = dkcStreamRead16(ptr->getObj(),pd,size,readsize);
			r = DKUTIL_SUCCEEDED_BOOL(tr);
			return r;
		}
	};
	struct functor__s32{
		friend CdkcStream;
		bool operator()(CdkcStream *ptr,void *pd,size_t size){
			bool r;
			int tr = dkcStreamWrite32(ptr->getObj(),pd,size);
			r = DKUTIL_SUCCEEDED_BOOL(tr);
			return r;
		}
		bool operator()(CdkcStream *ptr,void *pd,size_t size,size_t *readsize){
			bool r;
			int tr = dkcStreamRead32(ptr->getObj(),pd,size,readsize);
			r = DKUTIL_SUCCEEDED_BOOL(tr);
			return r;
		}
	};
	struct functor__s64{
		friend CdkcStream;
		bool operator()(CdkcStream *ptr,void *pd,size_t size){
			bool r;
			int tr = dkcStreamWrite64(ptr->getObj(),pd,size);
			r = DKUTIL_SUCCEEDED_BOOL(tr);
			return r;
		}
		bool operator()(CdkcStream *ptr,void *pd,size_t size,size_t *readsize){
			bool r;
			int tr = dkcStreamRead64(ptr->getObj(),pd,size,readsize);
			r = DKUTIL_SUCCEEDED_BOOL(tr);
			return r;
		}
	};
	virtual void serialize8(void *pd,size_t num){
		serialize_helper(pd,1 * num,functor__s8());
	}
	virtual void serialize16(void *pd,size_t num){
		serialize_helper(pd,2 * num,functor__s16());
	}
	virtual void serialize32(void *pd,size_t num){
		serialize_helper(pd,4 * num,functor__s32());
	}
	virtual void serialize64(void *pd,size_t num){
		serialize_helper(pd,8 * num,functor__s64());
	}
public:
	typedef IFileStream base_type;
	typedef base_type::size_type size_type;
	typedef base_type::size_type64 size_type64;
	typedef CdkcStream self_type;
	
	seek_arg_type limit()const{
		if(mp->mMode & edkcStreamInitFile64){
			return LONGLONG_MAX;
		}
		return INT_MAX;
	}
	DKC_STREAM *getObj(){
		return mp;
	}
	CdkcStream() : base_type(read_mode | binary_mode){
		mp = NULL;	
		mIs64Bit = false;
	}
	///@param is64bit[in] trueで64bitモードに固定する。
	CdkcStream(bool is64bit) : base_type(read_mode | binary_mode){
		mp = NULL;
		mIs64Bit = is64bit;
	}
	CdkcStream(const char *filename,UINT flag) : base_type(flag){
		mp = NULL;
		mIs64Bit = false;

		reset(filename,flag);
		
	}
	virtual ~CdkcStream(){
		clear();
	}
	///内部バッファをクリアする。
	virtual void clear(){
		if(mp){
			mfilename.clear();
			dkcFreeStream(&mp);
		}
	}
	virtual bool reset(const char *filename,UINT flag){
		clear();
		if(mIs64Bit){
			return reset64(filename,flag);
		}
		char f[4]="";
		
		if(false==detail::flag_to_fopen_mode(f,sizeof(f),flag)){
			return false;
		}
		//初期化がFALSEになっているけど大丈夫か？
		mp = dkcAllocStreamEncouragementFileType(filename,f,FALSE);
		if(NULL==mp){
			return false;
		}
		mfilename = filename;
		base_type::setOpenModeFlag(flag);
		return true;
	}
	/**
	64bit modeでの初期化
	*/
	bool reset64(const char *filename,UINT flag){
		clear();
		//初期化がFALSEになっているけど大丈夫か？
		mp = dkcAllocStreamFile64Type(edkcStreamWin32PromoteFlag,filename,flag);
		if(NULL==mp){
			return false;
		}
		mfilename = filename;
		base_type::setOpenModeFlag(flag);
		return true;
	}
	/*virtual ISerializeStream& operator << (ISerializeStream &c){
		*this << 

	}*/
	
	///ファイルのサイズ
	virtual size_type64 filesize()const{
		size_type64 ull;
		dkutil::GetFileSize(mfilename,&ull);
		return ull;
	}
	///バッファをフラッシュ((ファイルに完全に書きこむ))する。
	virtual bool flush(){
		return DKUTIL_SUCCEEDED_BOOL(dkcStreamFlush(mp));
	}
	virtual bool good()const{
		return (mp != NULL);
	}
	/*!
	@param buff[out] バッファー
	@param size[in] バッファのサイズ
	@param readsize[in] 実際に読み込んだサイズ
	@return falseならエラー。（バッファが足りない等）
	*/
	///読み込む
	virtual bool read(void *buff,size_t size,size_t *readsize){
		check_object__();
		int r = dkcStreamRead(mp,buff,size,readsize);
		return DKUTIL_SUCCEEDED_BOOL(	r	);
	}

	///書き込む(エンディアンチェンジは無し) @see IStream::write()
	virtual size_t write(const void *buff,size_t size){
		check_object__();
		int r = dkcStreamWrite(mp,buff,size);
		return size;
		//return DKUTIL_SUCCEEDED_BOOL(r);
	}
	virtual bool write(const void *buff,size_t size,size_t *write_size){
		check_object__();
		int r = dkcStreamWrite(mp,buff,size);
		*write_size = size;
		return DKUTIL_SUCCEEDED_BOOL(r);
	}
	/*!
	@param buff[out] バッファー
	@param size[in] バッファのサイズ
	@param readsize[in] 実際に読み込んだサイズ
	@return falseならエラー。
	@note
	read()との違い：
	ref()は内部のオフセットカウントを変更しない。
	*/
	///中身を見る。
	virtual bool ref(void *buff,size_t size,size_t *readsize){
		check_object__();
		int r = dkcStreamRef(mp,buff,size,readsize);
		
		return DKUTIL_SUCCEEDED_BOOL(r);
	}
	/*!
	@param offset[in] origin指定から何バイト、シークするか。
	@param origin[in] 
	@note
	動作はfseekと同じです。（爆）
	originも実はfseekと同じ指定子をぶち込んでも動いちゃったりします＾＾；
	*/
	///シークする。
	virtual bool seek(seek_arg_type offset,int origin){
		check_object__();
		if(LONG_MAX < offset)
			DKUTIL_THROW_OR_NOTICE(filesystem_error("CdkcStream::seek offset overflow"));

		return DKUTIL_SUCCEEDED_BOOL(
			dkcStreamSeek(mp,offset,origin)
		);
	}
	///今のバッファの最初からのオフセットを返す。
	virtual seek_arg_type tell()const{
		check_object__();
		return 	dkcStreamTell(mp);
		
	}
	virtual bool eof()const{
		check_object__();
		return TRUE==dkcStreamEOF(mp);
	}
	virtual bool error()const{
		check_object__();
		return TRUE==dkcStreamError(mp);
	}
	/*!
	@note
	serialize_interfaceのポインタをぶち込めば、
	シリアライズしたり、デシリアライズしたりしてくれます。
	*/
	///シリアライズする。
	//virtual bool serialize(serialize_interface *) = 0;
	///@see IStream::data()
	virtual const void *data()const{
		return NULL;
	}
	///内部バッファのサイズ。
	virtual size_type size()const{
		return 0;
	}
	///内部バッファをリサイズする。
	virtual void resize(size_type size){
		return;
	}

	virtual int type()const{
		return file_stream_type;
	}
	virtual const char *filename()const{
		return mfilename.c_str();
	}
	
	
/*
	virtual ISerializeStream& operator << (char &c){
		serialize_helper(c);
		return *this;
	}
	virtual ISerializeStream& operator << (short &s){
		serialize_helper(s);
		return *this;
	}
	virtual ISerializeStream& operator << (int &n){
		serialize_helper(n);
		return *this;
	}
	virtual ISerializeStream& operator << (long &l){
		serialize_helper(l);
		return *this;
	}
	virtual ISerializeStream& operator << (LONGLONG &ll){
		serialize_helper(ll);
		return *this;
	}
	virtual ISerializeStream& operator << (BYTE &uc){
		serialize_helper(uc);
		return *this;
	}
	virtual ISerializeStream& operator << (USHORT &us){
		serialize_helper(us);
		return *this;
	}
	virtual ISerializeStream& operator << (UINT &n){
		serialize_helper(n);
		return *this;
	}
	virtual ISerializeStream& operator << (ULONG &n){
		serialize_helper(n);
		return *this;
	}
	virtual ISerializeStream& operator << (ULONGLONG &ll){
		serialize_helper(ll);
		return *this;
	}
	virtual ISerializeStream& operator << (bool &b){
		serialize_helper(b);
		return *this;
	}
*/
	
	
};


}//end of dkutil namespace



#endif



