module y4d_aux.direnumerator;

private import std.path;
private import std.file;
private import y4d_aux.filesys;

///	ディレクトリ内のファイルを列挙するためのクラス。
/**
<PRE>
使用例)
	DirEnumerator d = new DirEnumerator;
	d.setDir("temp/");
	foreach(char[] filename;d)
		printf("%.*s\n",filename);
</PRE>

<PRE>
	//	c:\doxygen\yaneSDK4D以下のすべてのファイルを
	//	unicode -> mbsに変換するサンプル
	DirEnumerator d = new DirEnumerator;
	d.setDir(r"c:\doxygen\yaneSDK4D");
	foreach(char[] filename;d){
		printf("now convert %.*s\n",filename);
		void[] v = FileSys.read(filename);
		if (v) {
			void[] v2 = cast(void[])(toMBS((wchar[])v));
			FileSys.write(filename,v2);
		}
	}
	return 0;
</PRE>

zipファイルの中身も自動的に見に行くようにもできる。
<PRE>
	DirEnumerator d = new DirEnumerator;
	d.setZip(true);
	foreach(char[] file;d)
		printf("%.*s\n",toMBS(file));
</PRE>

*/
class DirEnumerator {

	///	検索するディレクトリ名を設定
	/**
		最後は'\'でも'/'でも、そうでなくとも良い。
	*/
	void	setDir(char[] dirname_) {
		dirname = dirname_;
	}

	///	サブフォルダも検索対象にする(default:true)
	void	setSubDir(bool bSubDir_) {
		bSubDir = bSubDir_;
	}

	///	setSubDirで設定した内容を取得。
	bool	isSubDir() { return bSubDir; }

	///	ファイルのみを対象とする(default:true)
	void	setFileOnly(bool bFileOnly_) {
		bFileOnly = bFileOnly_;
	}

	///	setFileOnlyで設定した内容を取得。
	bool	isFileOnly() { return bFileOnly; }

	///	zipファイルは仮想的にディレクトリとみなすのか?
	/**
		test.zipのなかにa.txt b.txtがあれば、これは
		test/a.txt , test/b.txtというファイルとみなす機能。

		defaultではfalse
	*/
	void	setZip(bool bZip_) {
		bZip = bZip_;
	}

	///	zipファイルは仮想的にディレクトリとみなすのかのフラグ取得
	bool	isZip() { return bZip; }

	///	nameはファイル名か？ 
	/**
		存在しないときも例外は発生しない。単にfalseが返る。
	*/
	static bool isFile(char[] name) {
		try { 
			return cast(bool) (std.file.isfile(name)!=0);
		} catch (std.file.FileException){
			return false;
		}
	}

	/// nameはディレクトリ名か？
	/**
		存在しないときも例外は発生しない。単にfalseが返る。
	*/
	static bool isDir(char[] name) {
		try {
			return cast(bool) (std.file.isdir(name)!=0);
		} catch (std.file.FileException){
			return false;
		}
	}

	this() {
		bSubDir = true;
		bFileOnly = true;
		bZip = false;
	}

	///	foreachに対応させるためのもの
	int opApply(int delegate(inout char[] filename) dg) {
		char[] pathname = FileSys.concatCwd(dirname);
//printf("%*s\n",pathname);

		int result = 0;
		char[][] dirs = listdir(pathname);
		for (int i = 0; i < dirs.length; ++i)
		{
			char[] file = FileSys.concat(dirname,dirs[i]);
			if (isFile(file)) {
				//	zipファイルかのチェック
				if (bZip && std.string.tolower(std.path.getExt(file)) == "zip"){
				//	これはフォルダ扱いになるわけだな..
					if (!isSubDir()) continue; // 次行ってみよー

					//	foreachの実装のなかでforeachを呼び出していいようだ
					DirEnumerator d = new FileArchiverZip.ZipDirEnumerator(dirname,file);
					d.setSubDir(isSubDir());
					d.setFileOnly(isFileOnly());
					d.setZip(isZip());

					foreach(char[] subfile;d){
						result = dg(subfile);
						if (result)	break;
					}

					continue;
				}
				result = dg(file);
				if (result)	break;
			}
			if (isDir(file)) {
				if (!isFileOnly()) {
					result = dg(file);
					if (result)	break;
				}
				if (isSubDir()){
				//	foreachの実装のなかでforeachを呼び出していいようだ
					scope DirEnumerator d = new DirEnumerator;
					d.setSubDir(isSubDir());
					d.setFileOnly(isFileOnly());
					d.setZip(isZip());
					d.setDir(file);

					foreach(char[] subfile;d){
						result = dg(subfile);
						if (result)	break;
					}
				}
			}
			//	このあとアーカイバも検索するんか？
		}
		return result;
	}

protected:
	char[] dirname;
	bool	bSubDir;
	bool	bFileOnly;
	bool	bZip;
}
