package io.andrewohara.tinkertime.controllers.workflows;
import java.io.IOException;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.google.inject.Inject;
import com.j256.ormlite.dao.Dao;
import io.andrewohara.common.workflows.tasks.WorkflowBuilder;
import io.andrewohara.tinkertime.controllers.ModLoader;
import io.andrewohara.tinkertime.controllers.ModManager.ModNotDownloadedException;
import io.andrewohara.tinkertime.controllers.workflows.tasks.AnalyzeModZipTask;
import io.andrewohara.tinkertime.controllers.workflows.tasks.CheckForUpdateTask;
import io.andrewohara.tinkertime.controllers.workflows.tasks.DownloadModImageTask;
import io.andrewohara.tinkertime.controllers.workflows.tasks.DownloadModInBrowserTask;
import io.andrewohara.tinkertime.controllers.workflows.tasks.DownloadModZipTask;
import io.andrewohara.tinkertime.controllers.workflows.tasks.RemoveModTask;
import io.andrewohara.tinkertime.controllers.workflows.tasks.RunCrawlerTask;
import io.andrewohara.tinkertime.controllers.workflows.tasks.SaveModTask;
import io.andrewohara.tinkertime.io.crawlers.Crawler;
import io.andrewohara.tinkertime.io.crawlers.CrawlerFactory;
import io.andrewohara.tinkertime.io.crawlers.CrawlerFactory.UnsupportedHostException;
import io.andrewohara.tinkertime.models.ModFile;
import io.andrewohara.tinkertime.models.mod.Mod;
public class ModWorkflowBuilder extends WorkflowBuilder {
private final CrawlerFactory crawlerService;
private final ModLoader modLoader;
private final Dao<ModFile, Integer> modFilesDao;
private Crawler<?> cachedCrawler;
@Inject
public ModWorkflowBuilder(CrawlerFactory crawlerService, ModLoader modLoader, Dao<ModFile, Integer> modFilesDao, Mod mod) {
super(mod);
this.crawlerService = crawlerService;
this.modLoader = modLoader;
this.modFilesDao = modFilesDao;
}
private Mod getMod(){
return (Mod) context;
}
private Crawler<?> getCrawler() throws UnsupportedHostException{
return (cachedCrawler != null) ? cachedCrawler : (cachedCrawler = crawlerService.getCrawler(getMod()));
}
/**
* Notifies the listeners if an update is available for the given file
* @throws UnsupportedHostException
*/
public void checkForUpdates(boolean markIfAvailable) throws UnsupportedHostException {
addTask(new CheckForUpdateTask(getCrawler(), getMod(),markIfAvailable));
}
/**
* Downloads the latest version of the mod referenced by the URL.
* @throws UnsupportedHostException
*/
public void updateMod(boolean forceUpdate) throws UnsupportedHostException {
// Cleanup operations prior to update
if (getMod().isDownloaded()){
if (!forceUpdate){
checkForUpdates(true);
}
// Disable Mod if it is enabled
try {
if (getMod().isEnabled()){
disableMod();
}
} catch (ModNotDownloadedException e) {
// Do Nothing
}
addTask(new RunCrawlerTask(getCrawler())); // Get user to select asset before deleting
deleteModZip();
}
downloadMod();
}
private void downloadMod() throws UnsupportedHostException{
Crawler<?> crawler = getCrawler();
addTask(new RunCrawlerTask(crawler)); // prefetch metadata
addTask(new DownloadModZipTask(crawler, getMod()));
addTask(new DownloadModImageTask(crawler, getMod()));
addTask(new AnalyzeModZipTask(getMod(), modFilesDao));
addTask(new SaveModTask(crawler, getMod()));
}
public void downloadModInBrowser(Mod mod) throws UnsupportedHostException{
addTask(new DownloadModInBrowserTask(getCrawler(), getMod()));
}
public void addLocalMod(Path zipPath){
// Add Mod
copy(zipPath, getMod().getZipPath());
addTask(new AnalyzeModZipTask(getMod(), modFilesDao));
}
/**
* Delete the mod's zip file, but do not mark the mod as deleted
* @param mod
* @param config
*/
public void deleteModZip(){
delete(getMod().getZipPath());
}
/**
* Fully delete the mod and mark it as deleted.
* @param mod
* @param config
* @param modLoader
*/
public void deleteMod() {
// Try to disable the mod first
try {
if (getMod().isEnabled()){
disableMod();
}
} catch (ModNotDownloadedException e) {
// Do nothing
}
addTask(new RemoveModTask(getMod()));
deleteModZip();
}
public void disableMod() throws ModNotDownloadedException{
Set<Path> modDestPaths = getModDestPaths(getMod());
// Check if any files for this mod are dependencies of other mods.
// All files which are a dependency will not be deleted
for (Mod otherMod : modLoader.getMods()){
if (otherMod != null && !otherMod.equals(getMod()) && otherMod.isEnabled()){
modDestPaths.removeAll(getModDestPaths(otherMod));
}
}
delete(modDestPaths);
cleanupDir(getMod().getInstallation().getGameDataPath());
}
private Set<Path> getModDestPaths(Mod mod){
Set<Path> fileDestPaths = new LinkedHashSet<>();
for (ModFile modFile : mod.getModFiles()){
fileDestPaths.add(modFile.getDestPath());
}
return fileDestPaths;
}
public void enableMod() throws ModNotDownloadedException {
if (!getMod().isDownloaded()){
throw new ModNotDownloadedException(getMod(), "mod has no zip path");
}
Path zipPath = getMod().getZipPath();
try (ZipFile zipFile = new ZipFile(zipPath.toFile())){
Map<Path, ZipEntry> zipData = new LinkedHashMap<>();
for (ModFile modFile : getMod().getModFiles()){
zipData.put(modFile.getDestPath(), modFile.getEntry(zipFile));
}
unzip(zipPath, zipData);
} catch (IOException e) {
throw new ModNotDownloadedException(getMod(), "Error opening zip");
}
}
}