module yamalib.serialize;

private import ytl.vector;
private import y4d_aux.filesys;
private import y4d_aux.widestring;
private import ytl.y4d_result;

private import yamalib.log.log;


abstract class Archive {
	void serialize(inout Serialize);
}
/// シリアライズクラス
class Serialize {

	bool	isStoring() { return storing; }
	void	setStoring(bool b) { storing = b; dataPos = 0; if (b) getData().clear(); }

	vector!(ubyte) getData() { return vData; }


	Serialize opShl(inout int n) {
		if (isStoring()){
		//	保存するんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
			int nSize = int.sizeof;
			ubyte* pByte = cast(ubyte*)&n;
			for(int i=0;i<nSize;i++){
				getData().push_back(pByte[i]);
			}
		} else {
		//	ストリームから取り出すんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
			int nSize = int.sizeof;
			ubyte* pByte = cast(ubyte*)&n;
			for(int i=0;i<nSize;i++){
				pByte[i] = getData()[dataPos++];
			}
		}
		return this;
	}

	Serialize opShl(inout long n) {
		if (isStoring()){
		//	保存するんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
			int nSize = long.sizeof;
			ubyte* pByte = cast(ubyte*)&n;
			for(int i=0;i<nSize;i++){
				getData().push_back(pByte[i]);
			}
		} else {
		//	ストリームから取り出すんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
			int nSize = long.sizeof;
			ubyte* pByte = cast(ubyte*)&n;
			for(int i=0;i<nSize;i++){
				pByte[i] = getData()[dataPos++];
			}
		}
		return this;
	}

	Serialize opShl(inout bool b) {
		if (isStoring()){
		//	保存するんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
//			int nSize = bool.sizeof;

			ubyte tmp = cast(ubyte) (b ? 1 : 0);
			int nSize = tmp.sizeof;
			ubyte* pByte = cast(ubyte*)&tmp;
			for(int i=0;i<nSize;i++){
				getData().push_back(pByte[i]);
			}
		} else {
		//	ストリームから取り出すんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
//			int nSize = bool.sizeof;
			ubyte tmp;
			int nSize = tmp.sizeof;
			ubyte* pByte = cast(ubyte*)&tmp;
			for(int i=0;i<nSize;i++){
				pByte[i] = getData()[dataPos++];
			}
			
			b = cast(bool) (tmp == 1);
		}
		return this;
	}

	Serialize opShl(inout ubyte by) {
		if (isStoring()){
		//	保存するんかいな？
			getData().push_back(by);
		} else {
		//	取り出すんかいな？
			by = getData()[dataPos];
			dataPos++;
		}
		return this;
	}

	/// boolの配列
	Serialize opShl(inout bool[] b) {
	   int size;
		if (isStoring()){
			//	保存するんかいな？
			size = b.length;
			this << size;
			
			ubyte by;
			for(int i=0;i<size;i++){
				by = cast(ubyte) (b[i] ? 1 : 0);
				this << by;
			}
		} else {
		//	ストリームから取り出すんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
	   		this << size;
	   		
	   		// 入力されたbool配列が小さければリサイズする
	   		if ( b.length < size ) {
	   			b.length = size;
	   		}
	   		
	   		for(int i=0; i<size; ++i) {
	   			b[i] = getData()[dataPos++] == 1 ? true : false;
	   		}
		}
		return this;
	}

	/// longの配列
	Serialize opShl(inout long[] lngVals) {
	   int size;
	   
		if (isStoring()){
			//	保存するんかいな？
			size = lngVals.length;
			this << size;
			
			for(int i=0;i<size;i++){
				this << lngVals[i];
			}
		} else {
		//	ストリームから取り出すんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
	   		this << size;
	   		
	   		// 入力されたbool配列が小さければリサイズする
	   		if ( lngVals.length < size ) {
	   			lngVals.length = size;
	   		}
	   		
	   		for(int i=0; i<size; ++i) {
	   			this << lngVals[i];
	   		}
		}
		return this;
	}

	/// intの配列
	Serialize opShl(inout int[] intVals) {
	   int size;
	   
		if (isStoring()){
			//	保存するんかいな？
			size = intVals.length;
			this << size;
			
			for(int i=0;i<size;i++){
				this << intVals[i];
			}
		} else {
		//	ストリームから取り出すんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
	   		this << size;
	   		
	   		// 入力されたbool配列が小さければリサイズする
	   		if ( intVals.length < size ) {
	   			intVals.length = size;
	   		}
	   		
	   		for(int i=0; i<size; ++i) {
	   			this << intVals[i];
	   		}
		}
		return this;
	}
	
	
	Serialize opShl(inout char[] str) {
	   int size;
		if (isStoring()){
			//	保存するんかいな？
			size = str.length;
			this << size;
			for(int i=0;i<size;i++){
	   			getData().push_back(str[i]);
			}
		} else {
		//	ストリームから取り出すんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
	   		this << size;
	   		for(int i=0; i<size; ++i) {
	   			str ~= getData[dataPos++];
	   		};
		}
		return this;
	}

	/// マルチバイト文字も
	Serialize opShl(inout wchar[] str) {
	   int size;
		wchar[] cc;
		
		if (isStoring()){
			//	保存するんかいな？
/+
			cc = toMBS(str);
			this << cc;
+/			
			size = wchar.sizeof * str.length;
			this << size;
			if (size == 0) return this;
			ubyte* pByte = cast(ubyte*) &str[0];
			for(int i=0;i<size;i++){
	   			getData().push_back(pByte[i]);
			}
		} else {
			//	ストリームから取り出すんかいな？
			//	サイズ不明と仮定したコーディング＾＾；
	   		this << size;
	   		ubyte[] buf;
	   		for(int i=0; i < size; ++i) {
	   			buf ~= getData[dataPos++];
	   		};
			str = cast(wchar[]) buf.dup;
		}
		return this;
	}

	Serialize opShl(inout vector!(ubyte) data) {
		int n;
		if (isStoring()) {
			n = data.size();
			this << n;	// サイズを保存
		} else {
			this << n;	// 長さを復元
			data.resize(n);
		}
		foreach (inout ubyte by;data) {
			this << by;
		}

		return this;
	}

	Serialize opShl(inout vector!(int) data) {
		int n;
		if (isStoring()) {
			n = data.size();
			this << n;	// サイズを保存
		} else {
			this << n;	// 長さを復元
			data.resize(n);
		}
		foreach (inout int n;data) {
			this << n;
		}

		return this;
	}

	// 文字列のvectorも
	Serialize opShl(inout vector!(char[]) data) {
		int n;
		if (isStoring()) {
			n = data.size();
			this << n;	// サイズを保存
		} else {
			this << n;	// 長さを復元
			data.resize(n);
		}
		foreach (inout char[] str;data) {
			this << str;
		}

		return this;
	}


	// アーカイブ
	Serialize opShl(inout Archive data) {
		data.serialize(this);
		return this;
	}

	/// 自分自身も
	Serialize opShl(inout Serialize data) {
		vector!(ubyte) dat = data.getData();
		this << dat;
		this << data.storing;
		this << data.dataPos;

		return this;
	}

	/// ファイルに書き出す
	y4d_result save(char[] filename){
		//	ストリームのファイルへの保存
		ubyte[] data;
		for(int i=0; i<getData().size(); ++i) {
			data ~= getData[i];
		}
		return FileSys.writeSimple(filename,cast(void[])data);
	}

	/// ファイルから読み込む
	bool load(char[] filename){	//	ストリームのファイルからの復元
		Log.printLook("%s#load %s" ,this.toString(), filename);
	//	ストリームからのファイルの読み出し
		getData().clear();
		ubyte[] data = cast(ubyte[]) FileSys.read(filename);
		if (data is null) {
			Log.printLook("%s#load %s data is null..." ,this.toString(), filename);
			return false;
		}
		if (0 == data.length) {
			Log.printLook("%s#load %s data length is zero..." ,this.toString(), filename);
			return false;
		}
		
		for(int i=0; i<data.length; ++i) {
			getData.push_back(data[i]);
		}
		return FileSys.isExist(filename);
	}

	///　コンストラクタ
	this() {
		vData = new vector!(ubyte);
		storing = true;
	}
protected:
	vector!(ubyte) vData;	//	データのテンポラリストリーム
	bool		storing; 	//　ストリームに保存中なのか、取り出し中なのか？
	int			dataPos;	//　ストリームからデータを取り出すとき、

}