/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.core.ios; import gplx.*; import gplx.core.*;
import gplx.core.consoles.*; import gplx.core.criterias.*;
import gplx.core.ios.streams.*;
public interface IoEngine {
	String		Key();
	boolean		ExistsFil_api(Io_url url);
	void		SaveFilText_api(IoEngine_xrg_saveFilStr args);
	String		LoadFilStr(IoEngine_xrg_loadFilStr args);
	void		DeleteFil_api(IoEngine_xrg_deleteFil args);
	void		CopyFil(IoEngine_xrg_xferFil args);
	void		MoveFil(IoEngine_xrg_xferFil args);
	IoItmFil	QueryFil(Io_url url);
	void		UpdateFilAttrib(Io_url url, IoItmAttrib atr); // will fail if file does not exists
	void		UpdateFilModifiedTime(Io_url url, DateAdp modified);
	IoStream	OpenStreamRead(Io_url url);
	IoStream	OpenStreamWrite(IoEngine_xrg_openWrite args);
	void		XferFil(IoEngine_xrg_xferFil args);
	void		RecycleFil(IoEngine_xrg_recycleFil xrg);
	boolean		Truncate_fil(Io_url url, long size);

	boolean		ExistsDir(Io_url url);
	void		CreateDir(Io_url url); // creates all folder levels (EX: C:\a\b\c\ will create C:\a\ and C:\a\b\). will not fail if called on already existing folders.
	void		DeleteDir(Io_url url);
	void		MoveDir(Io_url src, Io_url trg);		// will fail if trg exists
	void		CopyDir(Io_url src, Io_url trg);
	IoItmDir	QueryDir(Io_url url);

	void		DeleteDirDeep(IoEngine_xrg_deleteDir args);
	void		MoveDirDeep(IoEngine_xrg_xferDir args);		// will fail if trg exists
	IoItmDir	QueryDirDeep(IoEngine_xrg_queryDir args);
	void		XferDir(IoEngine_xrg_xferDir args);
	boolean		DownloadFil(IoEngine_xrg_downloadFil xrg);
	Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg);
}
class IoEngineUtl {
	public int BufferLength() {return bufferLength;} public void BufferLength_set(int v) {bufferLength = v;} int bufferLength = 4096; // 0x1000	
	public void DeleteRecycleGplx(IoEngine engine, IoEngine_xrg_recycleFil xrg) {
		Io_url recycleUrl = xrg.RecycleUrl();
		if (recycleUrl.Type_fil())
			engine.MoveFil(IoEngine_xrg_xferFil.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true));
		else
			engine.MoveDirDeep(IoEngine_xrg_xferDir.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true));
	}
	public void DeleteDirDeep(IoEngine engine, Io_url dirUrl, IoEngine_xrg_deleteDir args) {
		Console_adp usrDlg = args.UsrDlg();
		IoItmDir dir = engine.QueryDir(dirUrl); if (!dir.Exists()) return;
		for (Object subDirObj : dir.SubDirs()) {
			IoItmDir subDir = (IoItmDir)subDirObj;
			if (!args.SubDirScanCrt().Matches(subDir)) continue;
			if (args.Recur()) DeleteDirDeep(engine, subDir.Url(), args);
		}
		for (Object subFilObj : dir.SubFils()) {
			IoItmFil subFil = (IoItmFil)subFilObj;
			if (!args.MatchCrt().Matches(subFil)) continue;
			Io_url subFilUrl = subFil.Url();
			try {engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(subFilUrl).ReadOnlyFails_(args.ReadOnlyFails()));}
			catch (Exception exc) {usrDlg.Write_fmt_w_nl(Err_.Message_lang(exc));}
		}
		// all subs deleted; now delete dir
		if (!args.MatchCrt().Matches(dir)) return;
		try {engine.DeleteDir(dir.Url());}
		catch (Exception exc) {usrDlg.Write_fmt_w_nl(Err_.Message_lang(exc));}
	}
	public void XferDir(IoEngine srcEngine, Io_url src, IoEngine trgEngine, Io_url trg, IoEngine_xrg_xferDir args) {
		trgEngine.CreateDir(trg);
		IoItmDir srcDir = QueryDirDeep(srcEngine, IoEngine_xrg_queryDir.new_(src).Recur_(false));
		for (Object subSrcObj : srcDir.SubDirs()) {
			IoItmDir subSrc = (IoItmDir)subSrcObj;
			if (!args.SubDirScanCrt().Matches(subSrc)) continue;
			if (!args.MatchCrt().Matches(subSrc)) continue;
			Io_url subTrg = trg.GenSubDir_nest(subSrc.Url().NameOnly());	//EX: C:\abc\def\ -> C:\123\ + def\
			if (args.Recur()) XferDir(srcEngine, subSrc.Url(), trgEngine, subTrg, args);
		}
		IoItmList srcFils = IoItmList.list_(src.Info().CaseSensitive());
		for (Object srcFilObj : srcDir.SubFils()) {
			IoItmFil srcFil = (IoItmFil)srcFilObj;
			if (args.MatchCrt().Matches(srcFil)) srcFils.Add(srcFil);
		}
		for (Object srcFilObj : srcFils) {
			IoItmFil srcFil = (IoItmFil)srcFilObj;
			Io_url srcFilPath = srcFil.Url();
			Io_url trgFilPath = trg.GenSubFil(srcFilPath.NameAndExt());	//EX: C:\abc\fil.txt -> C:\123\ + fil.txt
			IoEngine_xrg_xferFil xferArgs = args.Type_move() ? IoEngine_xrg_xferFil.move_(srcFilPath, trgFilPath).Overwrite_(args.Overwrite()) : IoEngine_xrg_xferFil.copy_(srcFilPath, trgFilPath).Overwrite_(args.Overwrite());
			XferFil(srcEngine, xferArgs);
		}
		if (args.Type_move()) srcEngine.DeleteDirDeep(IoEngine_xrg_deleteDir.new_(src).Recur_(args.Recur()).ReadOnlyFails_(args.ReadOnlyFails()));// this.DeleteDirDeep(srcEngine, src, IoEngine_xrg_deleteItm.new_(src).Recur_(args.Recur()).ReadOnlyIgnored_(args.ReadOnlyIgnored()));
	}
	public void XferFil(IoEngine srcEngine, IoEngine_xrg_xferFil args) {
		Io_url src = args.Src(), trg = args.Trg();
		if (String_.Eq(srcEngine.Key(), trg.Info().EngineKey())) {
			if (args.Type_move())
				srcEngine.MoveFil(args);
			else
				srcEngine.CopyFil(args);
		}
		else {
			TransferStream(src, trg);
			if (args.Type_move()) srcEngine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(src));
		}
	}
	public IoItmDir QueryDirDeep(IoEngine engine, IoEngine_xrg_queryDir args) {
		IoItmDir rv = IoItmDir_.top_(args.Url());
		rv.Exists_set(QueryDirDeepCore(rv, args.Url(), engine, args.Recur(), args.SubDirScanCrt(), args.DirCrt(), args.FilCrt(), args.UsrDlg(), args.DirInclude()));
		return rv;
	}
	static boolean QueryDirDeepCore(IoItmDir ownerDir, Io_url url, IoEngine engine, boolean recur, Criteria subDirScanCrt, Criteria dirCrt, Criteria filCrt, Console_adp usrDlg, boolean dirInclude) {
		if (usrDlg.Canceled_chk()) return false;
		if (usrDlg.Enabled()) usrDlg.Write_tmp(String_.Concat("scan: ", url.Raw()));
		IoItmDir scanDir = engine.QueryDir(url);
		for (Object subDirObj : scanDir.SubDirs()) {
			IoItmDir subDir = (IoItmDir)subDirObj;
			if (!subDirScanCrt.Matches(subDir)) continue;
			if (dirCrt.Matches(subDir)) {
				ownerDir.SubDirs().Add(subDir);		// NOTE: always add subDir; do not use dirCrt here, else its subFils will be added to non-existent subDir
			}
			if (recur)
				QueryDirDeepCore(subDir, subDir.Url(), engine, recur, subDirScanCrt, dirCrt, filCrt, usrDlg, dirInclude);
		}
		for (Object subFilObj : scanDir.SubFils()) {
			IoItmFil subFil = (IoItmFil)subFilObj;
			if (filCrt.Matches(subFil)) ownerDir.SubFils().Add(subFil);
		}
		return scanDir.Exists();
	}
	void TransferStream(Io_url src, Io_url trg) {
		IoStream srcStream = null;
		IoStream trgStream = null;
		try {
			srcStream = IoEnginePool.Instance.Get_by(src.Info().EngineKey()).OpenStreamRead(src);
			trgStream = IoEngine_xrg_openWrite.new_(trg).Exec();
			srcStream.Transfer(trgStream, bufferLength);
		}
		finally {
			if (srcStream != null) srcStream.Rls();
			if (trgStream != null) trgStream.Rls();
		}
	}
	public static IoEngineUtl new_() {return new IoEngineUtl();} IoEngineUtl() {}
}
