package tasks.bench;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

import data.BuildInfo;
import tasks.util.Exec;

/**
 * Parses a project's build file to extract libraries, and searches for jar
 * files in the project
 * 
 * @author Lisa Nguyen Quang Do
 */

public class LibrariesExtractor {

	private final BuildInfo buildInfo;
	private final File projectDir;

	public LibrariesExtractor(BuildInfo buildInfo, File projectDir) {
		this.buildInfo = buildInfo;
		this.projectDir = projectDir;
	}

	public Set<String> getJarFilesInProject() {
		Set<String> libs = new HashSet<String>();
		List<String> res = Exec.exec("dir *.jar /s", projectDir);
		if (res != null) {
			for (String line : res) {
				for (String word : line.split("\\s+")) {
					if (word.endsWith(".jar"))
						libs.add(word);
				}
			}
		}
		return libs;
	}

	public Set<String> getLibrariesFromBuildFiles() {
		Set<String> libs = new HashSet<String>();
		for (File buildFile : buildInfo.usedBuildFiles) {
			libs.addAll(getLibrariesFromBuildFile(buildFile));
		}
		return libs;
	}

	private Set<String> getLibrariesFromBuildFile(File buildFile) {
		System.out.println("Extracting libraries from " + buildFile.getName());
		switch (buildInfo.usedBuildType) {
		case ANT:
			return getAntLibs(buildFile);
		case MAVEN:
			return getMavenLibs(buildFile);
		case GRADLE:
			return getGradleLibs(buildFile);
		default:
			throw new RuntimeException("Build type not known");
		}
	}

	private Set<String> getGradleLibs(File buildFile) {
		Set<String> libs = new HashSet<String>();

		try {
			BufferedReader br = new BufferedReader(new FileReader(buildFile));
			String line;
			boolean extract = false;
			while ((line = br.readLine()) != null) {
				if (line.contains("}"))
					extract = false;

				if (extract && !line.contains("fileTree")) {
					StringBuilder sb = new StringBuilder();
					Pattern p = Pattern.compile("\'([^\']*)\'");
					Matcher m = p.matcher(line);
					while (m.find()) {
						sb.append(m.group(1));
						sb.append(":");
					}
					String add = sb.toString();
					if (add.endsWith(":"))
						add = add.substring(0, add.length() - 1);
					libs.add(add);
				}

				if (line.contains("dependencies {"))
					extract = true;
			}
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return libs;
	}

	private Set<String> getMavenLibs(File buildFile) {
		Set<String> libs = new HashSet<String>();
		MavenXpp3Reader mavenreader = new MavenXpp3Reader();
		FileReader fReader;
		try {
			fReader = new FileReader(buildFile);
			Model model = mavenreader.read(fReader);
			List<Dependency> deps = model.getDependencies();
			for (Dependency d : deps)
				libs.add(getProperString(model, d.getArtifactId()) + ":" + getProperString(model, d.getVersion()) + ":"
						+ getProperString(model, d.getType()));
		} catch (IOException | XmlPullParserException e) {
			System.out.println("Error in extracting from " + buildFile);
			e.printStackTrace();
		}
		return libs;
	}

	private String getProperString(Model model, String string) {
		if (string == null)
			return "";

		Pattern p = Pattern.compile(Pattern.quote("${") + "(.*?)" + Pattern.quote("}"));
		Matcher m = p.matcher(string);
		while (m.find()) {
			String s = m.group(1);
			if (model.getProperties().get(s) != null)
				string = string.replace("${" + s + "}", model.getProperties().get(s).toString());
		}
		return string;
	}

	private Set<String> getAntLibs(File buildFile) {
		// We don't parse the build file
		return new HashSet<String>();
	}

}
