package de.dhbwstuttgart.environment;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

import com.google.common.collect.Lists;
import de.dhbwstuttgart.core.JavaTXCompiler;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;

import de.dhbwstuttgart.exceptions.DebugException;
import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.parser.JavaTXParser;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SourceFileContext;
import de.dhbwstuttgart.parser.antlr.Java17Parser.SrcfileContext;
import de.dhbwstuttgart.parser.scope.GatherNames;
import de.dhbwstuttgart.parser.scope.JavaClassRegistry;

/**
 * Stellt die Java-Environment dar und speichert alle Binarys, Librarys und Sourcefiles im zu kompilierenden Projekt Sie erstellt anhand dieser Informationen die JavaClassNameRegistry
 * 
 * TODO: Zur Initialisierung der CompilationEnvironment sollten alle SourceFiles mit ANTLR geparst werden und alle Klassen Generics und Typen herausgefunden werden
 */
public class CompilationEnvironment {
	private final List<URL> librarys;
	private final List<File> sourceFiles;
	public final PackageCrawler packageCrawler;

	/**
	 * Imitiert die Environment beim Aufruf des JavaCompilers auf einer Menge von java-Dateien Die Environment enthält automatisch die Java Standard Library
	 * 
	 * @param sourceFiles die zu kompilierenden Dateien
	 */
	public CompilationEnvironment(List<File> sourceFiles, DirectoryClassLoader classLoader) {
		/**
		 * Java 9 bringt einige Änderungen am Classloader So funktioniert der BootClassLoader nicht mehr. hier gibts ein paar Quellen zum nachlesen: http://java9.wtf/class-loading/ https://stackoverflow.com/questions/46494112/classloaders-hierarchy-in-java-9
		 *
		 */
		// String bootClassPath = System.getProperty("sun.boot.class.path");
		// DirectoryClassLoader cl = DirectoryClassLoader.getPlatformClassLoader();
		String bootClassPath = System.getProperty("java.class.path");
		librarys = new ArrayList<>();
		for (String path : bootClassPath.split(File.pathSeparator)) {
			try {
				librarys.add(new URL("file:" + path));
			} catch (MalformedURLException e) {
				new DebugException("Fehler im Classpath auf diesem System");
			}
		}
		// URLClassLoader loader = new URLClassLoader(new URL[0], cl);
		// librarys = Arrays.asList(loader.getURLs());

		this.sourceFiles = sourceFiles;
		this.packageCrawler = new PackageCrawler(classLoader);
	}

	public void addClassesToRegistry(JavaClassRegistry registry, SourceFileContext tree, File sourceFile, JavaTXCompiler compiler) throws ClassNotFoundException, IOException {
		Map<String, Integer> allNames;
		if (tree instanceof SrcfileContext srcfile) {
			allNames = GatherNames.getNames((SrcfileContext) tree, packageCrawler, compiler);
			for (Class c : loadDefaultPackageClasses(getPackageName(srcfile), sourceFile, compiler)) {
				allNames.put(c.getName(), c.getTypeParameters().length);
			}
			registry.addNames(allNames);
		} else {
			throw new NotImplementedException();
		}

	}

	public static List<Class> loadDefaultPackageClasses(String packageName, File sourceFile, JavaTXCompiler compiler) throws IOException, ClassNotFoundException {
		ClassLoader classLoader = compiler.getClassLoader();
		List<Class> ret = new ArrayList<>();
		// Set classLoader to include default package for this specific source file
		File dir = sourceFile.getAbsoluteFile().getParentFile();
		String dirPath = dir.toString() + "/";
		if (!packageName.isEmpty())
			dirPath = dirPath.substring(0, dirPath.length() - packageName.length() - 1);
		String path = dirPath;
		ArrayList<File> defaultPath = Lists.newArrayList(new File(path));
		classLoader = new DirectoryClassLoader(defaultPath, classLoader);
		// Gather all names in the default package for this source file (classes that are imported by default)
		File[] files = dir.listFiles((dir1, name) -> name.endsWith(".class"));
		if (files != null)
			for (File classFile : files) {
				String className = classFile.getName().substring(0, classFile.getName().length() - 6);
				// Don't load class if its defined in the input
				if (compiler.input.contains(new JavaClassName(className))) continue;
				if (className.matches("Fun\\d+\\$\\$.*"))
					continue;
				var name = packageName;
				if (!packageName.isEmpty()) name += ".";
				name += className;
				ret.add(classLoader.loadClass(name));
			}
		return ret;
	}

	private static String getPackageName(SrcfileContext forTree) {
		String packageName = "";
		if (forTree.packageDeclaration() != null && !forTree.packageDeclaration().qualifiedName().identifier().isEmpty())
			packageName = forTree.packageDeclaration().qualifiedName().getText();
		return packageName;
	}

}
