module yamalib.auxil.tempfilemanagerr;

private import std.string;
private import std.file;
private import std.path;
private import std.stream;
private import std.utf;

private import y4d_aux.filesys;
private import y4d_aux.widestring;
private import y4d_aux.direnumerator;
private import y4d_aux.lineparser;
private import y4d_math.rand;
private import ytl.vector;

private import yamalib.log.log;


class TmpFileManager {
public:
	/// テンポラリフォルダのパスを返す
	static char[] getTmpPath() { return tmpPath; }

	/// メモリデータを指定したパスに書き込む
	static bool write(char[] filename, void[] data, char[] addPath) {
		char[] fullPath = tmpPath;
		
		// 全角に対応させる
		filename = toMBS(filename);
		addPath = toMBS(addPath);

		// 環境のテンポラリフォルダに使いするpathの指定が有る場合
		if(addPath) {
			char[][] addPathList;

			// 階層修飾子がなければ追加
			if(std.string.find(addPath,"\\")>=0) {
				addPath ~= "\\";
			}
			// フォルダ名配列を取得
			addPathList = std.string.split(addPath,"\\");

			// カスケード的にパスを追加
			foreach (inout char[] path; addPathList) {
				fullPath ~= path ~ "\\";

				if (!std.file.exists(fullPath)) {
					try {
						std.file.mkdir(fullPath);
						if (LOG_OUT) {
							Log.print("TmpFileManager#write make dir : %s", fullPath);
						}
					} catch(FileException e) {
						if (LOG_OUT) {
							Log.printError("FileException TmpFileManager#write : Can't create Directory:%s",fullPath);
						} else {
							Log.printError("FileException TmpFileManager#write : Can't create Directory");
						}
						return false;
					} // try
				} // if
			}// foreach
		}	// if

		// ファイル書き出し
		if ( FileSys.writeSimple(fullPath ~ filename, data) ) {
			// 失敗した...
			if (LOG_OUT) {
				Log.printError("TmpFileManager#write : write Error!:%s",fullPath ~ filename);
			} else {
				Log.printError("TmpFileManager#write : write Error!");
			}
			return false;
		}

		if (LOG_OUT) {
			Log.print("TmpFileManager#write : wrote:%s",fullPath ~ filename);
		}

		return true;
	}
	
	/// テンポラリを削除する
	static void clear() {
		// テンポラリが作成されていなければ、何もしない
		if ( !std.file.exists(tmpPath) ) {
			return;
		}
		
		// tmpファイルの除去
		DirEnumerator dir = new DirEnumerator;

		dir.setDir(tmpPath);
		// まずファイルだけ消す
		foreach(inout char[] file; dir) {
			if (TESTING) {
				if (LOG_OUT) 
					printf("finalize : rm %*s", file);
			} else {
				std.file.remove(file);
			}
		}

		vector!(char[]) vDirectory = new vector!(char[]);

		// ディレクトリの列挙
		dir.setFileOnly(false);
		foreach(inout char[] d; dir) {
			if ( TESTING ) {
				if ( DirEnumerator.isFile(d) ) {
					continue;
				}
			}
			vDirectory.push_back(d);
		}
		
		// ディレクトリを深いところから削除していく
		vector!(char[]).reverse_iterator it = new vector!(char[]).reverse_iterator;
		it = vDirectory.rbegin();
		while(it != vDirectory.rend()) {
			if ( TESTING ) {
				printf("finalize : rmdir(sub) %*s", it());
			} else {
				std.file.rmdir(it());
				if (LOG_OUT) {
					printf("finalize : rmdir(sub) %*s", it());
				}
				++it;
			}
		}

		// ルートtmpディレクトリの削除
		if ( TESTING ) {
			printf("finalize : rmdir(root) %*s", tmpPath);
		} else {
			std.file.rmdir(tmpPath);
		}
	}

	/// コンストラクタ
	this() {
	}

	/// 静的コンストラクタ
	static this() {
		char[] path  = "DUMMY_DIR";
//		for(int i; i<12; ++i) {
//			path ~= hexdigits[rnd.get(hexdigits.length)];
//		}

		try {
			tmpPath = toUTF8( toWCS(FileSys.getTmpPath()) ) ~ path ~ "\\";
//			tmpPath = toUTF8( FileSys.getTmpPath() ) ~ path ~ "\\";
		} catch (Exception e) {
			tmpPath = std.file.getcwd() ~ "\\tmp";
		}

		if ( !std.file.exists(tmpPath) ) {
			std.file.mkdir(tmpPath);
		}
	}


	/// 静的デストラクタ
	static ~this() {
		clear();
	}

private:
	static final bool TESTING = false;
	static final bool LOG_OUT = false;	// テンポラリのログは見られたくないので別フラグ

protected:
	static final char[] tmpPath;	// テンポラリフォルダのフルパス名
}


class TmpZipFileManager : TmpFileManager {
public:
	/// Zipファイル読み込み用の定義ファイルを渡す
	void setDefFile(char[] filename) {
		defFilename = filename;
	}

	static bool loadDefFile(char[] filename) {
		void[] mem = FileSys.read(filename);
		if(!mem) return false;
		bool res = loadDefRW(mem);
		return res;
	}

	
	static bool loadDefRW(void[] memory) {
		ubyte[] mem = cast(ubyte[])memory;
		std.stream.MemoryStream m = new std.stream.MemoryStream(mem);
		LineParser lp = new LineParser;
		while (!m.eof) {
			char[] linebuf = m.readLine();
			lp.setLine(linebuf);
			char[] filename = lp.getStr();
			if (!filename) continue; // ダメやん
			char[] addPath = lp.getStr();

			if (write(filename,addPath)) {
				return false;
			}
		}

		return true;
	}

	/// 階層を調べてＺＩＰ展開用リストファイルを作る
	static void createList(char[] filename,char[] path) {
		if (std.file.exists(filename)) {
			std.file.remove(filename);
		}

		int dirDepth = std.string.count(path,"\\");
		char[][] fullPathFilename;
		char[][] baseName;
		DirEnumerator dir = new DirEnumerator;
		dir.setDir(path);
		foreach (inout char[] file; dir) {
			if (std.string.tolower(std.path.getExt(file)) == "zip") {
				fullPathFilename ~= file;
				baseName ~= std.file.getBaseName(file);
			}
		}

		char[] tmp;
		foreach (int i,inout char[] file; fullPathFilename) {
			int count;
			foreach(int j,inout char c; file) {
				if (c == '\\') {
					count++;
					if (dirDepth==count) {
						tmp = file[j+1..file.length] ~ ",";
						std.file.append(filename,tmp);
						std.file.append(filename,tmp[0..tmp.length-(baseName[i].length+1)]);
						std.file.append(filename,"\r\n");
						break;
					}
				}
			}
		}
	}

	/// zip書庫を丸ごと展開します
	static bool write(char[] filename,char[] addPath) {
		DirEnumerator dir = arc.getEnumerator(filename);
		void[] mem;
		int i;
		foreach ( inout char[] c; dir) {
			Log.print("dir enum :%s %s\n",++i, c);
//			if (arc.read(filename, c, true, mem)) {
//				Log.print("test! %*s\n", c);
//				if (!TmpFileManager.write(c,mem,addPath)) {
//					return false;
//				}
//			}
		}

		return true;
	}

	/// 存在するリストファイルをフルパスに書き換える
	static void updateListFile() {
		DirEnumerator dir = new DirEnumerator;
		dir.setDir(tmpPath);

		foreach (inout char[] file; dir) {
			if (std.string.tolower(std.path.getExt(file)) == "lst") {
				// フルパスに書き換える
				ubyte[] mem = cast(ubyte[])(FileSys.read(file));
				if (!mem) return; // 読み込みエラー
				std.file.remove(file);
				std.stream.MemoryStream m = new std.stream.MemoryStream(mem);
				LineParser lp = new LineParser;
				while (!m.eof) {
					char[] outline;
					char[] linebuf = m.readLine();
					lp.setLine(linebuf);
					char[] filename = lp.getStr();
					if (!filename) continue; // ダメやん

					// ファイルをフルパスにする
					if (!FileSys.isAbs(filename)) {
						outline ~= tmpPath ~ filename;
					} else {
						outline ~= filename;
					}
					
					std.string.replace(outline,"/","\\");
//					std.string.replace(outline,"\\","\\\\");
//					Log.print("filename:%*s\n",outline);

					// オプションを読み出す
					while (!lp.isEnd()) {
						char[] str = lp.getStr();
						if(str)
							outline ~= "," ~ str;
					}
					outline ~= "\r\n";	// 改行処理

					std.file.append(file,cast(void[])outline);
				}	// while
			} else if (std.string.tolower(std.path.getExt(file)) == "sdf") {
				// スプライト定義ファイル
				ubyte[] mem = cast(ubyte[])(FileSys.read(file));
				if (!mem) return; // 読み込みエラー
				std.file.remove(file);
				std.stream.MemoryStream m = new std.stream.MemoryStream(mem);
				LineParser lp = new LineParser;
				while (!m.eof) {
					char[] outline;
					char[] linebuf = m.readLine();
					lp.setLine(linebuf);
					if (lp.isMatch("#Plane")) {
						outline ~= "#Plane " ~ lp.getStr() ~ ",";	//#Plane x,
						char[] filename = lp.getStr();
						// ファイルをフルパスにする
						if (!FileSys.isAbs(filename)) {
							outline ~= "\"" ~ tmpPath ~ filename ~ "\"";
						} else {
							outline ~= "\"" ~ filename ~ "\"";
						}
					} else {
						outline ~= linebuf;
					}
					outline ~= "\r\n";	// 改行処理
					std.file.append(file,cast(void[])outline);
				}
			}
		}
	}
	
	/// コンストラクタ
	this() {
	}

	/// 静的コンストラクタ
	static this() {
		arc = new FileArchiverZip;
	}


	/// 静的デストラクタ
	static ~this() {
/+		
		// tmpファイルの除去
		DirEnumerator dir = new DirEnumerator;

		dir.setDir(tmpPath);
		// まずファイルだけ消す
		foreach(inout char[] file; dir) {
			std.file.remove(file);
		}

		vector!(char[]) vDirectory = new vector!(char[]);

		// ディレクトリの書き出し
		dir.setFileOnly(false);
		foreach(inout char[] d; dir) {
			vDirectory.push_back(d);
		}
		// ディレクトリを深いところから削除していく
		vector!(char[]).reverse_iterator it = new vector!(char[]).reverse_iterator;
		it = vDirectory.rbegin();
		while(it != vDirectory.rend()) {
			std.file.rmdir(it());
			++it;
		}

		// ルートtmpディレクトリの削除
//		std.file.rmdir(tmpPath);
+/
//		Log.print("tmp file(s) and directory removed!");
	}

private:
	/// LSTファイルのパスをフルパスに書き換えます
	static void updatePathLst(char[] file) {
		assert( std.string.tolower(std.path.getExt(file)) == "lst" );
		// フルパスに書き換える
		ubyte[] mem = cast(ubyte[])(FileSys.read(file));
		if (!mem) return; // 読み込みエラー
		std.file.remove(file);
		std.stream.MemoryStream m = new std.stream.MemoryStream(mem);
		LineParser lp = new LineParser;
		while (!m.eof) {
			char[] outline;
			char[] linebuf = m.readLine();
			lp.setLine(linebuf);
			char[] filename = lp.getStr();
			if (!filename) continue; // ダメやん

			// ファイルをフルパスにする
			if (!FileSys.isAbs(filename)) {
				outline ~= tmpPath ~ filename;
			}
			std.string.replace(outline,"/","\\");
			std.string.replace(outline,"\\","\\\\");

			// オプションを読み出す
			while (!lp.isEnd()) {
				char[] str = lp.getStr();
				if(str)
					outline ~= "," ~ str;
			}
			outline ~= "\r\n";	// 改行処理

			std.file.append(file,cast(void[])outline);
		}	// while
	}
	
	/// スプライト定義ファイルを書き換えます
	static void updatePathSdf(char[] file) {
		assert( std.string.tolower(std.path.getExt(file)) == "sdf" );
		// スプライト定義ファイル
		ubyte[] mem = cast(ubyte[])(FileSys.read(file));
		if (!mem) return; // 読み込みエラー
		std.file.remove(file);
		std.stream.MemoryStream m = new std.stream.MemoryStream(mem);
		LineParser lp = new LineParser;
		while (!m.eof) {
			char[] outline;
			char[] linebuf = m.readLine();
			lp.setLine(linebuf);
			if (lp.isMatch("#Plane")) {
				outline ~= "#Plane " ~ lp.getStr() ~ ",";	//#Plane x,
				char[] filename = lp.getStr();
				// ファイルをフルパスにする
				if (!FileSys.isAbs(filename)) {
					outline ~= "\"" ~ tmpPath ~ filename ~ "\"";
				} else {
					outline ~= "\"" ~ filename ~ "\"";
				}
			} else {
				outline ~= linebuf;
			}
			outline ~= "\r\n";	// 改行処理
			std.file.append(file,cast(void[])outline);
		}
	}

protected:
//	static final const char[] PASS = "cPpBDHbC6p1p";
	static final const char[] PASS = "test";
	static final const char[] EXT_LST = "lst";
	static final const char[] EXT_SDF = "sdf";
	static FileArchiverZip arc;		// 

	char[] defFilename;
}
