/*
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.ios; import gplx.*;
public interface IoUrlInfo {
	String Key();
	byte DirSpr_byte();
	String DirSpr();
	boolean CaseSensitive();
	String EngineKey();

	boolean Match(String raw);
	boolean IsDir(String raw);
	String Xto_api(String raw);
	String OwnerDir(String raw);
	String OwnerRoot(String raw);
	String NameAndExt(String raw);
	String NameOnly(String raw);
	String Ext(String raw);
	String XtoRootName(String raw, int rawLen);
}
class IoUrlInfo_nil implements IoUrlInfo {
	public String Key() {return KeyConst;} public static final String KeyConst = String_.Null_mark;
	public String EngineKey() {return "<<INVALID>>";}
	public String DirSpr() {return "<<INVALID>>";}
	public byte DirSpr_byte() {return Byte_ascii.Slash;}
	public String VolSpr() {return "<<INVALID>>";}
	public boolean CaseSensitive() {return false;}
	public boolean Match(String raw) {return false;}
	public boolean IsDir(String raw) {return false;}
	public String Xto_api(String raw) {return "";}
	public String OwnerDir(String raw) {return IoUrlInfo_base.NullString;}
	public String OwnerRoot(String raw) {return IoUrlInfo_base.NullString;}
	public String NameAndExt(String raw) {return "";}
	public String NameOnly(String raw) {return "";}
	public String Ext(String raw) {return "";}
	public String XtoRootName(String raw, int rawLen) {return "";}
	public static final IoUrlInfo_nil _ = new IoUrlInfo_nil(); IoUrlInfo_nil() {}
}
abstract class IoUrlInfo_base implements IoUrlInfo {
	@gplx.Internal protected static final int DirSprLen = 1;
	@gplx.Internal protected static final String NullString = "", ExtSeparator = ".";
	public abstract String Key();
	public abstract byte DirSpr_byte();
	public abstract String DirSpr();
	public abstract boolean CaseSensitive();
	public abstract boolean Match(String raw);
	public abstract String EngineKey();
	public boolean IsDir(String raw) {return String_.HasAtEnd(raw, DirSpr());}
	public abstract String XtoRootName(String raw, int rawLen);
	@gplx.Virtual public String Xto_api(String raw) {
		return IsDir(raw)
			? String_.DelEnd(raw, IoUrlInfo_base.DirSprLen)	// if Dir, remove trailing DirSpr, since most api will not expect it (ex: .Delete will malfunction)
			: raw;
	}
	public String OwnerDir(String raw) {
		int rawLen = String_.Len(raw);
		int ownerDirSprPos = OwnerDirPos(raw, rawLen);
		if (ownerDirSprPos <= OwnerDirPos_hasNoOwner) return IoUrlInfo_base.NullString;	// no ownerDir found; return Null; only (a) NullUrls (b) RootUrls ("C:\") (c) relative ("fil.txt")
		return String_.MidByLen(raw, 0, ownerDirSprPos + 1);	// +1 to include backslash
	}
	@gplx.Virtual public String OwnerRoot(String raw) {
		String temp = raw, rv = raw;
		while (true) {
			temp = OwnerDir(temp);
			if (String_.Eq(temp, IoUrlInfo_base.NullString)) break;
			rv = temp;
		}
		return rv;
	}
	public String NameAndExt(String raw) {	// if Dir, will return \ as last char
		int rawLen = String_.Len(raw);
		int ownerDirSprPos = OwnerDirPos(raw, rawLen);
		if (ownerDirSprPos == OwnerDirPos_isNull) return IoUrlInfo_base.NullString;	// NullUrl and RootUrl return Null;
		return ownerDirSprPos == OwnerDirPos_hasNoOwner || ownerDirSprPos == OwnerDirPos_isRoot
			? raw											// no PathSeparator b/c (a) RootDir ("C:\"); (b) relative ("fil.txt")
			: String_.DelBgn(raw, ownerDirSprPos + 1);		// +1 to skip backslash
	}
	public String NameOnly(String raw) {
		String nameAndExt = NameAndExt(raw);
		if (IsDir(raw)) {
			String rootName = XtoRootName(raw, String_.Len(raw));	// C:\ -> C; / -> root
			return rootName == null
				? String_.DelEnd(nameAndExt, IoUrlInfo_base.DirSprLen)
				: rootName;
		}
		int pos = String_.FindBwd(nameAndExt, IoUrlInfo_base.ExtSeparator);
		return pos == String_.Find_none
			? nameAndExt									// Ext not found; return entire NameAndExt
			: String_.MidByLen(nameAndExt, 0, pos);
	}
	public String Ext(String raw) {		// if Dir, return DirSpr; if Fil, return . as first char; ex: .txt; .png
		if (IsDir(raw)) return this.DirSpr();
		String nameAndExt = NameAndExt(raw);
		int pos = String_.FindBwd(nameAndExt, IoUrlInfo_base.ExtSeparator);
		return pos == String_.Find_none ? "" : String_.DelBgn(nameAndExt, pos);
	}
	int OwnerDirPos(String raw, int rawLen) {
		if		(rawLen == 0)						return OwnerDirPos_isNull;
		else if (XtoRootName(raw, rawLen) != null)	return OwnerDirPos_isRoot;
		else	{// NullUrls and RootUrls have no owners
			int posAdj = IsDir(raw) ? IoUrlInfo_base.DirSprLen : 0;	// Dir ends with DirSpr, adjust lastIndex by DirSprLen
			return String_.FindBwd(raw, this.DirSpr(), rawLen - 1 - posAdj); // -1 to adjust for LastIdx
		}
	}
	static final int
		  OwnerDirPos_hasNoOwner		= -1	// ListAdp_.NotFound
		, OwnerDirPos_isNull			= -2
		, OwnerDirPos_isRoot			= -3;
}
class IoUrlInfo_wnt extends IoUrlInfo_base {
	@Override public String Key()			{return "wnt";}
	@Override public String EngineKey()		{return IoEngine_.SysKey;}
	@Override public String DirSpr()			{return Op_sys.Wnt.Fsys_dir_spr_str();}
	@Override public byte DirSpr_byte()		{return Byte_ascii.Backslash;}
	@Override public boolean CaseSensitive()	{return Op_sys.Wnt.Fsys_case_match();}
	@Override public boolean Match(String raw)	{return String_.Len(raw) > 1 && String_.CharAt(raw, 1) == ':';} // 2nd char is :; assumes 1 letter drives
	@Override public String XtoRootName(String raw, int rawLen) {
		return rawLen == 3 && String_.CharAt(raw, 1) == ':' // only allow single letter drives; ex: C:\; note, CharAt(raw, 1) to match Match
			? Char_.XtoStr(String_.CharAt(raw, 0))
			: null;
	}
	public static final IoUrlInfo_wnt _ = new IoUrlInfo_wnt(); IoUrlInfo_wnt() {}
}
class IoUrlInfo_lnx extends IoUrlInfo_base {
	@Override public String Key()			{return "lnx";}
	@Override public String EngineKey()		{return IoEngine_.SysKey;}
	@Override public String DirSpr()			{return DirSprStr;} static final String DirSprStr = Op_sys.Lnx.Fsys_dir_spr_str();
	@Override public byte DirSpr_byte()		{return Byte_ascii.Slash;}
	@Override public boolean CaseSensitive()	{return Op_sys.Lnx.Fsys_case_match();}
	@Override public boolean Match(String raw)	{return String_.HasAtBgn(raw, DirSprStr);}	// anything that starts with /
	@Override public String XtoRootName(String raw, int rawLen) {
		return rawLen == 1 && String_.Eq(raw, DirSprStr)
			? "root"
			: null;
	}
	@Override public String OwnerRoot(String raw) {return DirSprStr;}	// drive is always /
	@Override public String Xto_api(String raw) {
		return String_.Eq(raw, DirSprStr)			// is root
			? DirSprStr
			: super.Xto_api(raw);					// NOTE: super.Xto_api will strip off last /
	}
	public static final IoUrlInfo_lnx _ = new IoUrlInfo_lnx(); IoUrlInfo_lnx() {}
}
class IoUrlInfo_rel extends IoUrlInfo_base {
	@Override public String Key()			{return "rel";}
	@Override public String EngineKey()		{return IoEngine_.SysKey;}
	@Override public String DirSpr()			{return info.DirSpr();}
	@Override public byte DirSpr_byte()		{return info.DirSpr_byte();}
	@Override public boolean CaseSensitive()	{return info.CaseSensitive();}
	@Override public String XtoRootName(String raw, int rawLen) {return info.XtoRootName(raw, rawLen);}
	@Override public boolean Match(String raw)	{return true;} // relPath is always lastResort; return true
	IoUrlInfo info;
        public static IoUrlInfo_rel new_(IoUrlInfo info) {
		IoUrlInfo_rel rv = new IoUrlInfo_rel();
		rv.info = info;
		return rv;
	}	IoUrlInfo_rel() {}
}
class IoUrlInfo_mem extends IoUrlInfo_base {
	@Override public String Key()			{return key;} private String key;
	@Override public String EngineKey()		{return engineKey;} private String engineKey;
	@Override public String DirSpr()			{return "/";}
	@Override public byte DirSpr_byte()		{return Byte_ascii.Slash;}
	@Override public boolean CaseSensitive()	{return false;}
	@Override public String XtoRootName(String raw, int rawLen) {
		return String_.Eq(raw, key) ? String_.DelEnd(key, 1) : null;
	}
	@Override public boolean Match(String raw) {return String_.HasAtBgn(raw, key);}
	public static IoUrlInfo_mem new_(String key, String engineKey) {
		IoUrlInfo_mem rv = new IoUrlInfo_mem();
		rv.key = key; rv.engineKey = engineKey;
		return rv;
	}	IoUrlInfo_mem() {}
}
class IoUrlInfo_alias extends IoUrlInfo_base {
	@Override public String Key() {return srcDir;}
	@Override public String EngineKey() {return engineKey;} private String engineKey;
	@Override public String DirSpr() {return srcDirSpr;}
	@Override public byte DirSpr_byte()	{return srcDirSpr_byte;} private byte srcDirSpr_byte;
	@Override public boolean CaseSensitive() {return false;}
	@Override public String XtoRootName(String raw, int rawLen) {
		return String_.Eq(raw, srcRootDir) ? srcRootName : null;
	}
	@Override public boolean Match(String raw) {return String_.HasAtBgn(raw, srcDir);}
	@Override public String Xto_api(String raw) {			
		String rv = String_.Replace(raw, srcDir, trgDir);											// replace src with trg
		if (!String_.Eq(srcDirSpr, trgDirSpr)) rv = String_.Replace(rv, srcDirSpr, trgDirSpr);		// replace dirSprs
		return IsDir(raw)
			? String_.DelEnd(rv, IoUrlInfo_base.DirSprLen)	// remove trailingSeparator, else Directory.Delete will not work properly
			: rv;
	}
	void SrcDir_set(String v) {
		srcDir = v;
		boolean lnx = DirSpr_lnx(v);
		if (srcDirSpr == null) {
			if (lnx) {
				srcDirSpr = Op_sys.Lnx.Fsys_dir_spr_str();
				srcDirSpr_byte = Op_sys.Lnx.Fsys_dir_spr_byte();
			}
			else {
				srcDirSpr = Op_sys.Wnt.Fsys_dir_spr_str();
				srcDirSpr_byte = Op_sys.Wnt.Fsys_dir_spr_byte();
			}
		}
		if (srcRootName == null) srcRootName = lnx ? "root" : String_.Mid(srcDir, 0, String_.FindFwd(srcDir, ":"));
		if (srcRootDir == null) srcRootDir = lnx ? "/" : srcDir;
	}
	void TrgDir_set(String v) {
		trgDir = v;
		boolean lnx = DirSpr_lnx(v);
		if (trgDirSpr == null) trgDirSpr = lnx ? Op_sys.Lnx.Fsys_dir_spr_str() : Op_sys.Wnt.Fsys_dir_spr_str();
	}
	boolean DirSpr_lnx(String s) {return String_.Has(s, Op_sys.Lnx.Fsys_dir_spr_str());}
	void EngineKey_set(String v) {engineKey = v;}
	String srcDir, trgDir, srcDirSpr, trgDirSpr, srcRootDir, srcRootName;
        public static IoUrlInfo_alias new_(String srcDir, String trgDir, String engineKey) {
		IoUrlInfo_alias rv = new IoUrlInfo_alias();
		rv.SrcDir_set(srcDir);
		rv.TrgDir_set(trgDir);
		rv.EngineKey_set(engineKey);
		return rv;
	}
	public static final IoUrlInfo_alias KEYS = new IoUrlInfo_alias();
	public final String
		  Data_EngineKey			= "engineKey"
		, Data_SrcDir				= "srcDir"
		, Data_TrgDir				= "trgDir"
		;
}
