package net.oni2.aeinstaller.backend.oni.management;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
import java.util.TreeSet;
import java.util.Vector;

import net.oni2.aeinstaller.backend.Paths;
import net.oni2.aeinstaller.backend.oni.OniSplit;
import net.oni2.platformtools.applicationinvoker.ApplicationInvocationResult;

import org.apache.commons.io.FileUtils;

import com.paour.NaturalOrderComparator;

/**
 * @author Christian Illy
 */
public class Initializer {
	private static void createEmptyPath(File path) throws IOException {
		if (path.exists())
			FileUtils.deleteDirectory(path);
		path.mkdirs();
	}

	/**
	 * Initializes the Edition core
	 * 
	 * @param listener
	 *            Listener for status updates
	 */
	public static void initializeEdition(InstallProgressListener listener) {
		File init = new File(Paths.getTempPath(), "init");

		int totalSteps = 0;
		int stepsDone = 0;

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

		TreeSet<File> levelFiles = new TreeSet<File>(
				new NaturalOrderComparator());

		for (File f : Paths.getVanillaGDF().listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				return name.endsWith(".dat");
			}
		})) {
			totalSteps++;
			levelFiles.add(f);
		}
		totalSteps = totalSteps * 2 + 2;

		try {
			File logFile = new File(Paths.getInstallerPath(),
					"Initialization.log");
			Logger log = new Logger(logFile);

			Date start = new Date();
			log.println("Initialization of Edition core started at "
					+ sdf.format(start));
			log.println("Cleaning directories");

			listener.installProgressUpdate(stepsDone, totalSteps,
					"Cleaning up directories");
			createEmptyPath(Paths.getVanillaOnisPath());
			createEmptyPath(init);
			File level0Folder = new File(init, "level0_Final");
			createEmptyPath(level0Folder);

			stepsDone++;

			log.println("Exporting levels and moving files to level0");

			for (File f : levelFiles) {
				String levelName = f.getName().substring(0,
						f.getName().indexOf('.'));
				Scanner fi = new Scanner(levelName);
				int levelNumber = Integer.parseInt(fi.findInLine("[0-9]+"));

				log.println("\t" + levelName + ":");
				log.println("\t\tExporting");
				listener.installProgressUpdate(stepsDone, totalSteps,
						"Exporting vanilla level " + levelNumber);

				// Edition/GameDataFolder/level*_Final/
				File tempLevelFolder = new File(init, levelName);

				// Export Vanilla-Level-Dat -> Temp/Level
				ApplicationInvocationResult res = OniSplit.export(
						tempLevelFolder, f);
				log.logAppOutput(res, true);

				log.println("\t\tMoving files");
				handleFileGlobalisation(tempLevelFolder, level0Folder,
						levelNumber);
				stepsDone++;
				log.println();
			}

			log.println("Reimporting levels");
			TreeSet<File> initFolders = new TreeSet<File>(
					new NaturalOrderComparator());
			for (File f : init.listFiles()) {
				initFolders.add(f);
			}

			for (File f : initFolders) {
				String levelName = f.getName();

				log.println("\t" + levelName);
				listener.installProgressUpdate(stepsDone, totalSteps,
						"Creating globalized " + levelName);

				Vector<File> folders = new Vector<File>();
				folders.add(f);

				ApplicationInvocationResult res = OniSplit
						.importLevel(folders,
								new File(Paths.getVanillaOnisPath(), levelName
										+ ".dat"));
				log.logAppOutput(res, true);

				log.println();
				stepsDone++;
			}

			listener.installProgressUpdate(stepsDone, totalSteps,
					"Copying basic files");
			// Copy Oni-configs
			File persistVanilla = new File(Paths.getOniBasePath(),
					"persist.dat");
			File persistEdition = new File(Paths.getEditionBasePath(),
					"persist.dat");
			File keyConfVanilla = new File(Paths.getOniBasePath(),
					"key_config.txt");
			File keyConfEdition = new File(Paths.getEditionBasePath(),
					"key_config.txt");
			if (persistVanilla.exists() && !persistEdition.exists())
				FileUtils.copyFile(persistVanilla, persistEdition);
			if (keyConfVanilla.exists() && !keyConfEdition.exists())
				FileUtils.copyFile(keyConfVanilla, keyConfEdition);

			FileUtils.deleteDirectory(init);

			Date end = new Date();
			log.println("Initialization ended at " + sdf.format(end));
			log.println("Process took "
					+ ((end.getTime() - start.getTime()) / 1000) + " seconds");
			log.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static void moveFileToTargetOrDelete(File source, File target) {
		if (source.equals(target))
			return;
		if (!target.exists()) {
			if (!source.renameTo(target)) {
				System.err.println("File " + source.getPath() + " not moved!");
			}
		} else if (!source.delete()) {
			System.err.println("File " + source.getPath() + " not deleted!");
		}
	}

	private static void handleFileGlobalisation(File tempFolder,
			File level0Folder, int levelNumber) {
		// Move AKEV and related files to subfolder so they're not globalized:
		if (levelNumber != 0) {
			File akevFolder = new File(tempFolder, "AKEV");
			akevFolder.mkdir();
			OniSplit.move(akevFolder, tempFolder.getPath() + "/AKEV*.oni",
					"overwrite");
		}

		for (File f : tempFolder.listFiles(new FileFilter() {
			@Override
			public boolean accept(File pathname) {
				return pathname.isFile();
			}
		})) {
			// Move matching files to subfolder NoGlobal:
			if (f.getName().startsWith("TXMPfail")
					|| f.getName().startsWith("TXMPlevel")
					|| (f.getName().startsWith("TXMP") && f.getName().contains(
							"intro"))
					|| f.getName().startsWith("TXMB")
					|| f.getName().equals("M3GMpowerup_lsi.oni")
					|| f.getName().equals("TXMPlsi_icon.oni")
					|| (f.getName().startsWith("TXMB") && f.getName().contains(
							"splash_screen.oni"))) {
				File noGlobal = new File(tempFolder, "NoGlobal");
				noGlobal.mkdir();
				File noGlobalFile = new File(noGlobal, f.getName());
				moveFileToTargetOrDelete(f, noGlobalFile);
			}
			// Move matching files to level0_Animations/level0_TRAC
			else if (f.getName().startsWith("TRAC")) {
				File level0File = new File(level0Folder, f.getName());
				moveFileToTargetOrDelete(f, level0File);
			}
			// Move matching files to level0_Animations/level0_TRAM
			else if (f.getName().startsWith("TRAM")) {
				File level0File = new File(level0Folder, f.getName());
				moveFileToTargetOrDelete(f, level0File);
			}
			// Move matching files to level0_Textures
			else if (f.getName().startsWith("ONSK")
					|| f.getName().startsWith("TXMP")) {
				File level0File = new File(level0Folder, f.getName());
				moveFileToTargetOrDelete(f, level0File);
			}
			// Move matching files to *VANILLA*/level0_Characters
			else if (f.getName().startsWith("ONCC")
					|| f.getName().startsWith("TRBS")
					|| f.getName().startsWith("ONCV")
					|| f.getName().startsWith("ONVL")
					|| f.getName().startsWith("TRMA")
					|| f.getName().startsWith("TRSC")
					|| f.getName().startsWith("TRAS")) {
				File level0File = new File(level0Folder, f.getName());
				moveFileToTargetOrDelete(f, level0File);
			}
			// Move matching files to level0_Sounds
			else if (f.getName().startsWith("OSBD")
					|| f.getName().startsWith("SNDD")) {
				File level0File = new File(level0Folder, f.getName());
				moveFileToTargetOrDelete(f, level0File);
			}
			// Move matching files to level0_Particles
			else if (f.getName().startsWith("BINA3")
					|| f.getName().startsWith("M3GMdebris")
					|| f.getName().equals("M3GMtoxic_bubble.oni")
					|| f.getName().startsWith("M3GMelec")
					|| f.getName().startsWith("M3GMrat")
					|| f.getName().startsWith("M3GMjet")
					|| f.getName().startsWith("M3GMbomb_")
					|| f.getName().equals("M3GMbarab_swave.oni")
					|| f.getName().equals("M3GMbloodyfoot.oni")) {
				File level0File = new File(level0Folder, f.getName());
				moveFileToTargetOrDelete(f, level0File);
			}
			// Move matching files to Archive (aka delete them)
			else if (f.getName().startsWith("AGDB")
					|| f.getName().startsWith("TRCM")) {
				f.delete();
			}
			// Move matching files to /level0_Final/
			else if (f.getName().startsWith("ONWC")) {
				File level0File = new File(level0Folder, f.getName());
				moveFileToTargetOrDelete(f, level0File);
			}
		}
	}

}
