package main;

import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.FileUtils;

import data.BuildInfo;
import data.BuildType;
import data.RepoInfo;
import io.Reader;
import io.Writer;
import tasks.bench.Compile;
import tasks.bench.Download;
import tasks.bench.Executable;
import tasks.bench.FindBuildType;
import tasks.bench.LibrariesExtractor;
import tasks.bench.LibrariesFilter;
import tasks.bench.Move;
import tasks.bench.Setup;
import tasks.bench.Zip;

/**
 * Downloads, extracts and filters benchmarks
 * 
 * @author Lisa Nguyen Quang Do
 */

public class MakeCollection {

	private void run() {
		Writer writer = new Writer(Config.resultFile, Config.target);
		Reader reader = new Reader(Config.resultFile, Config.target);

		Reader whitelistReader = new Reader(Config.resultFile, Config.whitelist);
		Map<String, Integer> whitelist = whitelistReader.getWhitelist();

		Map<Integer, RepoInfo> suitableRepositories = reader.getSuitableReposInfo();
		for (Integer rowId : suitableRepositories.keySet()) {

			RepoInfo repository = suitableRepositories.get(rowId);
			String baseName = repository.id + "_" + repository.name;

			System.out.println("\n\n******** REPOSITORY " + repository.id + " " + repository.name);

			// Setup workspace
			System.out.println("Setting up " + repository.id);
			Setup setup = new Setup(repository.id);
			File projectDir = setup.createSetup();
			if (projectDir != null) {
				System.out.println("Directory created " + projectDir);

				// Downloading source code
				Download download = new Download(projectDir, repository);
				download.downloadSource();
				System.out.println("Download complete for " + baseName);

				// Setting correct project size
				String size = getSize(projectDir);
				writer.writeInfo(rowId, Config.XML_size, size);
				System.out.println("Project size is: " + size);

				// Zip sources
				Zip zipTask = new Zip(Config.tmpDir, projectDir, baseName + ".zip");
				File zipFile = zipTask.zipIt();
				if (zipFile == null) {
					System.out.println("Error in creating zip file: " + baseName + ".zip");
					continue;
				}
				System.out.println("Zip file created for " + baseName + " : " + zipFile);

				// Setting correct projectDir
				for (File f : projectDir.listFiles()) {
					if (f.isDirectory() && !f.isHidden()) {
						projectDir = f;
						break;
					}
				}

				// Find build type
				FindBuildType findBuildType = new FindBuildType(projectDir);
				BuildInfo buildInfo = findBuildType.findBuildType();
				writer.writeInfo(rowId, Config.XML_potentialBuildTypes,
						buildInfo.potentialBuildTypes.keySet().toString());
				writer.writeInfo(rowId, Config.XML_potentialBuildFiles, getRelativePaths(buildInfo, projectDir));

				// Build type filter
				if (buildInfo.potentialBuildTypes.keySet().contains(BuildType.ANDROID)) {
					System.out.println("Android project, abort.");
					continue;
				}
				buildInfo.potentialBuildTypes.keySet()
						.retainAll(new HashSet<BuildType>(Arrays.asList(Config.consideredBuildTypes)));
				if (buildInfo.potentialBuildTypes.keySet().isEmpty()) {
					System.out
							.println("Build types not considered, abort: " + buildInfo.potentialBuildTypes.toString());
					continue;
				}
				System.out.println("Build types detected: " + buildInfo.potentialBuildTypes.keySet().toString());
				System.out.println("Build files detected: " + getRelativePaths(buildInfo, projectDir));

				// Compile filter
				Compile compile = new Compile();
				compile.compile(buildInfo);
				if (!buildInfo.compiles) {
					System.out.println("Project does not compile. Aborting " + baseName);
					continue;
				}
				System.out.println("Project compiles with " + buildInfo.usedBuildType);
				writer.writeInfo(rowId, Config.XML_buildType, buildInfo.usedBuildType.toString());
				writer.writeInfo(rowId, Config.XML_buildFile, getRelativePaths(buildInfo.usedBuildFiles, projectDir));
				writer.writeInfo(rowId, Config.XML_compiles, buildInfo.compiles + "");

				// Extract libraries
				LibrariesExtractor librariesExtractor = new LibrariesExtractor(buildInfo, projectDir);
				Set<String> libs = librariesExtractor.getLibrariesFromBuildFiles();
				Set<String> jars = librariesExtractor.getJarFilesInProject();
				writer.writeInfo(rowId, Config.XML_libs, libs.toString());
				writer.writeInfo(rowId, Config.XML_jars, jars.toString());

				// Libraries filter
				LibrariesFilter librariesFilter = new LibrariesFilter();
				Set<String> libraries = librariesFilter.getCommonLibraries(libs, whitelist.keySet());
				libraries.addAll(librariesFilter.getCommonLibraries(jars, whitelist.keySet()));
				writer.writeInfo(rowId, Config.XML_hasLibraries, libraries.toString());
				if (libraries.isEmpty()) {
					System.out.println("No web libraries. Aborting.");
					continue;
				}
				System.out.println("Web libraries found: " + libraries.toString());

				// Copy sources
				Move move = new Move();
				File fileMoved = move.moveFile(zipFile, Config.sourceDirectory + File.separator + baseName + ".zip");
				if (fileMoved == null) {
					System.out.println("Error in executable copy: " + zipFile);
					continue;
				}
				System.out.println("Sources copied: " + fileMoved);
				writer.writeInfo(rowId, Config.XML_source, fileMoved.getName());

				// Extract executables
				Executable executableTask = new Executable();
				Set<File> executables = executableTask.getExecutables(buildInfo, projectDir);
				System.out.println("Executables extracted: " + getRelativePaths(executables, projectDir));

				// If everything is fine, save the project zip and executables
				Set<String> binFiles = new HashSet<String>();
				for (File exec : executables) {
					String ext = exec.getName().substring(exec.getName().lastIndexOf("."), exec.getName().length());
					fileMoved = move.moveFile(exec, Config.binDirectory + File.separator + baseName + ext);
					if (fileMoved == null) {
						System.out.println("Error in executable copy: " + exec);
						continue;
					}
					System.out.println("Executable copied: " + exec);
					binFiles.add(fileMoved.getName());
				}
				writer.writeInfo(rowId, Config.XML_executables, binFiles.toString());

			}
		}
		System.out.println("Collection finished");
	}

	private String getSize(File file) {
		double bytes = FileUtils.sizeOfDirectory(file);
		double kilobytes = (bytes / 1024);
		return kilobytes + "";
	}

	private String getRelativePaths(BuildInfo buildInfo, File projectDir) {
		StringBuilder sb = new StringBuilder();
		for (Set<File> setFile : buildInfo.potentialBuildTypes.values()) {
			sb.append(getRelativePaths(setFile, projectDir));
		}
		String ret = sb.toString();
		if (ret.endsWith(", "))
			ret = ret.substring(0, ret.length() - 2);
		return ret;
	}

	private String getRelativePaths(Set<File> set, File projectDir) {
		StringBuilder sb = new StringBuilder();
		for (File f : set) {
			sb.append(getRelativePath(f, projectDir));
			sb.append(", ");
		}
		return sb.toString();
	}

	private String getRelativePath(File f, File projectDir) {
		String pathFile = f.getAbsolutePath();
		String pathFolder = projectDir.getAbsolutePath();
		return pathFile.substring(pathFolder.length() + 1);
	}

	public static void main(String[] args) throws Exception {
		MakeCollection main = new MakeCollection();
		main.run();
	}
}
