import std.stream;
import std.string;
import std.file;
class FileDB{
	static int toSize(char[] v){
		return v[0]<<24|v[1]<<16|v[2]<<8|v[3];
	}
	static char[] toSizeArray(uint size){
		char[] v;
		v.length=4;
		v[0]= cast(char)((size >>24) & 0xff);
		v[1]= cast(char)((size >>16) & 0xff);
		v[2]= cast(char)((size >>8 ) & 0xff);
		v[3]= cast(char)( size       & 0xff);
		return v;
	}

	// 指定したファイル名にデータを１件書き出す。
	// もしも、ファイルが無ければ作る。
	// もしも、件数を超えた場合はファイルを作り直す。
	// ファイルフォーマット
	// 件数4bytes 
	// アドレステーブルサイズ4bytes 
	// ファイル種別1byte+終了アドレステーブル アドレステーブルサイズx5bytes
	// データ(件数分)
	static bool addDBFile(char[] filename, int addressSize, char[]data)
	{
		File file=null;
		char[] dt;
		char flag[];//ファイル種別
		static char f[]=['0'];//ファイル種別格納用配列
		int pos[];
		if(addressSize<1){
			return false;
		}
		int dbsize=1;
		int tablesize=addressSize;
		if(!exists(filename)){
			// ファイルが存在しないので作る。
		}else{
			// ファイルの情報を読み出し変更する。
			file = new File(filename,FileMode.In|FileMode.Out);
			dt.length=4;
			file.read(cast(ubyte[])dt);
			dbsize=toSize(dt);
			file.read(cast(ubyte[])dt);
			tablesize=toSize(dt);
			//printf("tablesize=%d dbsize=%d\n",tablesize,dbsize);
			if(dbsize>=tablesize){//テーブルサイズが大きくなったので書き換えを行う。
				for(int i=0;i<tablesize;i++){
					file.read(cast(ubyte[])f);
					flag ~= f[0];
					file.read(cast(ubyte[])dt);
					pos ~= toSize(dt)+addressSize*5;
				}
				file.seekSet(4+4+dbsize*5);
				dt.length=file.size()-(4+4+dbsize*5);
				file.read(cast(ubyte[])dt);
				file.close();
				file = null;
				tablesize+=addressSize;
			}else{
				file.seekSet(0);
				dt=toSizeArray(dbsize+1);
				file.write(cast(ubyte[])dt);
				file.seekSet(4+4+dbsize*5);
				file.write(cast(ubyte[])f);
				dt=toSizeArray(file.size()+data.length);
				file.write(cast(ubyte[])dt);
				file.seekEnd(0);
				dt.length=0;
			}
			dbsize++;
		}

		if(file===null){
			//printf("create file dbsize=%d tablesize=%d\n",dbsize,tablesize);
			char[] head;
			head = head ~ toSizeArray(dbsize);
			head = head ~ toSizeArray(tablesize);
			int t=0;
			for(int i=0;i<pos.length;i++){
				head ~= flag[i];
				head = head ~ toSizeArray(pos[i]);
				t++;
			}
			head ~= '0';
			head = head ~ toSizeArray(4+4+tablesize*5+dt.length+data.length);
			head.length=4+4+tablesize*5;
			head = head ~ dt ~ data;
			write(filename,cast(void[])head);
			// printf("create file ok\n");
			// 作る。
		}else{
			// 追加する。
			file.write(cast(ubyte[])data);
			file.close();
		}
		return true;
	}
	static bool removeDBFile(char[] filename, int offset)
	{
		if(offset<0)return false;
		char[][] data;
		File file = null;
		try{
			if(!exists(filename)){
				return false;
			}
			file = new File(filename,FileMode.In|FileMode.Out);
			char[] dt;dt.length=4;
			file.read(cast(ubyte[])dt);
			int dbsize=toSize(dt);
			if(offset>dbsize){//開始位置までデータがない。
				return false;
			}
			file.seekSet(4+4+offset*5);
			static char[] f=['d'];
			file.write(cast(ubyte[])f);
			file.close();
		}catch(Exception e){
			try{
				if(file!==null)file.close();
			}catch(Error e2){}
			return false;
		}
		return true;
	}

	// 指定したファイル名から、複数データを読み込む。なければ、返さない。
	static char[][] readDBFile(char[] filename,int offset,int size)
	{
		char[][] data;
		if(!exists(filename) || size<1){
			return data;
		}
		if(offset<0)return data;
		File file = null;
		try{

			file = new File(filename,FileMode.In);
			char[] dt;dt.length=4;
			file.read(cast(ubyte[])dt);
			int dbsize=toSize(dt);
			file.read(cast(ubyte[])dt);
			int tablesize=toSize(dt);
			static char[] f=['0'];
			if(offset>dbsize){//開始位置までデータがない。
				return data;
			}
			if(dbsize-offset < size){//終了位置までデータがない。
				size = dbsize-offset;
			}
			file.seekSet(4+4+offset*5);
			int[] pos;
			char[] flag;
			pos ~= 4+4+tablesize*5;
			for(int i=0;i<size;i++){
				file.read(cast(ubyte[])f);
				flag ~= f[0];
				file.read(cast(ubyte[])dt);
				pos ~= toSize(dt);
			}
			for(int i=0;i<size;i++){
				if(flag[i]=='d'){
					data ~= "";
					continue;
				}
				file.seekSet(pos[i]);
	//printf("pos.length=%d i=%d\n",pos.length,i);
	//printf("pos[i+1]=%d pos[i]=%d pos[i+1]-pos[i]=%d\n",pos[i+1],pos[i],pos[i+1]-pos[i]);
				char[] d;
				d.length=pos[i+1]-pos[i];
				file.read(cast(ubyte[])d);
				data ~= d;
			}
		}catch(Error e){
			return data;
		}finally{
			if(file!==null){
				try{
					file.close();
				}catch(Error e2){
				}
			}
		}
		return data;
	}

	static int readSize(char[] filename)
	{
		int dbsize=0;
		if(!exists(filename)){
			return dbsize;
		}
		File file = null;
		try{
			file = new File(filename,FileMode.In);
			char[] dt;dt.length=4;
			file.read(cast(ubyte[])dt);
			dbsize=toSize(dt);
		}catch(Error e){
			return dbsize;
		}finally{
			if(file!==null){
				try{
					file.close();
				}catch(Error e2){
				}
			}
		}
		return dbsize;
	}
}
version(FILEDB_TEST){
import std.date;
void main()
{
	char[] s;
	static int[] a=[1,256];
	for(int k=0;k<a.length;k++){
		try{remove("filedb.bin");}catch(Exception e){}
printf("test tablesize=%d\n",a[k]);
		int starttime = getUTCtime();
		for(int i=0;i<1000;i++){
			s =  std.string.toString(i);
			FileDB.addDBFile("filedb.bin",a[k],s);

			char[][] data = FileDB.readDBFile("filedb.bin",0,10);
/*
			int size=0;
			try{size=cast(int)getSize("filedb.bin");}catch(Exception e){}
			printf("size=%d filesize=%d\n",data.length,size);
			for(int j=0;j<data.length;j++){
				printf("%.*s\n",data[j]);
			}
*/
		}
		int endtime = getUTCtime();
		printf("time=%d\n",endtime-starttime);
	}
	printf("ok\n");
}
}
