package hh.dejsem.fm;

import android.support.v4.provider.DocumentFile;

import java.io.File;
import java.util.HashMap;

import hh.dejsem.K;
import hh.lib.D;

/**
 * ----------------------------------------------------------
 * metody modifikace objektů lokálního file systému
 * <p>
 * ● všechno se točí kolem ošetření externí SD-karty, využívá se DocumentFile
 * <p>
 * ● smysl by mělo použití DocumentFile pro vnitřní i externí kartu, ale performace je tragická,
 *      takže implementace je polovičatá a živelná
 * <p>
 * ● použité DocumentFiles se cachují, ale jaký to má vliv na performace jsem nezkoumal
 * ----------------------------------------------------------
 */
public class FSLocal {

	/**
	 * Informuje je-li argument na externí SD-kartě podle mount point SD-karty.
	 * Mount point se zjišťuje na začátku aplikace skrz Intent.ACTION_OPEN_DOCUMENT_TREE.
	 *
	 * @param fp je absolutní
	 * @return true je-li argument na externí SD-kartě
	 */
	static boolean isOnSdCard(String fp) { return fp.startsWith(K.SD_CARD_MOUNT_POINT); }

	D d;
	HashMap<String, DocumentFile> docFileCache = new HashMap<>();

	public FSLocal(D d) { this.d = d.klon(this); }

	/**
	 * Vrací DocumentFile z cache nebo ho vytvoří.
	 *
	 * @param fp je absolutní
	 * @return DocumentFile
	 * @throws
	 */
	DocumentFile getDocFile(String fp) {
		DocumentFile df = null;
		if(docFileCache.containsKey(fp)) df = docFileCache.get(fp);
		else {
			if(isOnSdCard(fp)) df = getSdCardDocFile(fp);
			else {
				File f = new File(fp);
				if(f.exists()) df = DocumentFile.fromFile(f);
			}
			if(df != null) docFileCache.put(fp, df);
		}
		return df;
	}

	/**
	 * Vrací DocumentFile objektu na externí SD-kartě.
	 *
	 * @param fp the fp
	 * @return the sd card doc file
	 */
	public DocumentFile getSdCardDocFile(String fp) {     // fp je absolutní cesta
		String relPath = fp.replace(K.SD_CARD_MOUNT_POINT, "");
		String[] subs = relPath.split("/");
		DocumentFile df = K.SD_CARD_ROOT_DOC_FILE;
		for(String sub: subs) {
			df = df.findFile(sub);
			if(df == null) break;
		}
		return df;
	}

	/**
	 * Vytvoří directory a vrátí jeho DocumentFile
	 *
	 * @param dirPath je absolutní
	 * @return DocumentFile
	 * @throws K.NotCreated
	 */
	public DocumentFile doCreateDir(String dirPath) throws Exception {
		return doCreateDir(dirPath, null);
	}

	/**
	 * Vytvoří directory child v dir parent a vrátí DocumentFile.
	 * Když parent neexistuje, změní se na K.SD_CARD_MOUNT_POINT nebo "/" a child relativně k němu.
	 * Je-li param child null nebo prázdný, vrací se DocumentFile parenta.
	 *
	 * @param parent je absolutní
	 * @param child  je relativní vůči parent, může být null nebo prázdný
	 * @return DocumentFile
	 * @throws K.NotCreated
	 */
	public DocumentFile doCreateDir(String parent, String child) throws K.NotCreated {
		DocumentFile parentDf = getDocFile(parent);
		if(parentDf == null) {
			child = (child == null ? new File(parent) : new File(parent, child)).getAbsolutePath();
			if(isOnSdCard(parent)) {
				child = child.replace(K.SD_CARD_MOUNT_POINT, "");
				if(child.startsWith("/")) child = child.substring(1, child.length() - 1);
				parent = K.SD_CARD_MOUNT_POINT;
				parentDf = K.SD_CARD_ROOT_DOC_FILE;
			}
			else {
				parent = "/";
				parentDf = getDocFile(parent);
			}
		}
		else if(child == null || child.length() == 0) return parentDf;
		return createDirChain(parentDf, parent, child);

	}

	/**
	 * Vytvoří directory child včetně jeho cesty v dir parent a vrátí DocumentFile.
	 * Parent musí existovat.
	 *
	 * @param parentDf parent DocumentFile
	 * @param parent   absolutní cesta
	 * @param child    cesta k child relativně k parent
	 * @return DocumentFile
	 * @throws K.NotCreated
	 */
	public DocumentFile createDirChain(DocumentFile parentDf, String parent, String child) throws K.NotCreated {
		DocumentFile df = parentDf;
		if(child != null && child.length() > 0) {
			String path = parent;
			for(String sub : child.split("/")) {
				path += "/" + sub;
				if(docFileCache.containsKey(path)) df = getDocFile(path);
				else {
					df = createDir(df, sub);
					docFileCache.put(path, df);
				}
			}
		}
		return df;
	}

	/**
	 * Vytvoří directory fn v dir parent a vrátí DocumentFile.
	 * Fn je jméno bez cesty.
	 * Parent musí existovat.
	 *
	 * @param parentDf parent DocumentFile
	 * @param fn       prosté jméno bez cesty
	 * @return DocumentFile
	 * @throws K.NotCreated
	 */
	public DocumentFile createDir(DocumentFile parentDf, String fn) throws K.NotCreated {  // fn bez cesty
		DocumentFile df = parentDf.findFile(fn);
		if(df == null) {
			df = parentDf.createDirectory(fn);
			if(df != null) {
				if(df.getName().equals(fn) && d.ll(4)) d.l(String.format("dir  %s created", fn));
				else {
					df.delete();
					throw new K.NotCreated(String.format("dir  %s can't be created, name already in use", fn));
				}
			}
			else throw new K.NotCreated(String.format("dir  %s not created for unknown reason", fn));
		}
		else {
			if(df.isDirectory()) { if(d.ll(4)) d.l(String.format("dir %s ready", fn)); }
			else new K.NotCreated(String.format("dir  %s can't be created, name already in use", fn));
		}
		return df;
	}

	/**
	 * Rename.
	 * Na externí SD-kartě se dá změnit jen jméno filu, nikoli cesta :-(
	 *
	 * @param from from je absoultní cesta
	 * @param to   to   je absoultní cesta
	 * @throws K.NotCompleted, K.NotCreated
	 */
	void doRename(String from, String to) throws K.NotCompleted, K.NotCreated {
		if(isOnSdCard(from)) {
			if(!getDocFile(from).renameTo(new File(to).getName())) throw new K.NotCompleted();
		}
		else if(!new File(from).renameTo(new File(to))) throw new K.NotCompleted();
	}

	/**
	 * Implementace "move" naráží na potíže s SD-kartou - DocumentFile neumožňuje přesunutí jména do jiné cesty,
	 *      takže by tu měl být test na externí SD-kartu a warning "Neimplementováno".
	 *
	 * @param from the from
	 * @param to   the to
	 * @throws Exception the exception
	 */
	void doMove(String from, String to) throws Exception {
		File fromF = new File(from);
		File toF = new File(to, fromF.getName());
		if(!fromF.renameTo(toF)) throw new K.NotCompleted();
	}

	/**
	 * Delete.
	 * Na vnitřní kartě je ponechána operace na File kvůli performace.
	 *
	 * @param fp je absolutní
	 * @throws K.NotCreated
	 */
	void doDelete(String fp) throws K.NotCompleted, K.NotCreated {
		if(isOnSdCard(fp)) {
			if(!getDocFile(fp).delete()) throw new K.NotCompleted();
		}
		else if(!new File(fp).delete()) throw new K.NotCompleted();
	}
}