package de.is24.deadcode4j;

import com.google.common.collect.Sets;
import javassist.ClassPool;
import javassist.NotFoundException;

import javax.annotation.Nonnull;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;

public class DeadCodeFinder {

    private static URL[] toUrls(@Nonnull File[] codeRepositories) {
        URL[] urls = new URL[codeRepositories.length];
        for (int i = urls.length; i-- > 0; )
            try {
                urls[i] = codeRepositories[i].toURI().toURL();
            } catch (MalformedURLException e) {
                throw new RuntimeException("Failed to set up code repositories!", e);
            }
        return urls;
    }

    private final Set<Analyzer> analyzers;

    public DeadCodeFinder(@Nonnull Set<Analyzer> analyzers) {
        this.analyzers = analyzers;
    }

    public DeadCodeFinder() {
        this(Sets.newHashSet(new ClassFileAnalyzer(), new SpringXmlAnalyzer()));
    }

    public DeadCode findDeadCode(File... codeRepositories) {
        ClassPool classPool = setupJavassist(codeRepositories);
        ClassLoader classLoader = new URLClassLoader(toUrls(codeRepositories));
        CodeContext codeContext = new CodeContext(codeRepositories, classLoader, classPool);


        AnalyzedCode analyzedCode = new AnalyzedCode(Collections.<String>emptyList(), Collections.<String, Iterable<String>>emptyMap());
        for (Analyzer analyzer : analyzers) {
            analyzedCode = analyzedCode.merge(analyzer.analyze(codeContext));
        }

        Collection<String> deadClasses = determineDeadClasses(analyzedCode);

        return new DeadCode(analyzedCode.getAnalyzedClasses(), deadClasses);
    }

    private ClassPool setupJavassist(@Nonnull File[] codeRepositories) {
        ClassPool classPool = new ClassPool(false);

        for (File codeRepository : codeRepositories) {
            try {
                classPool.appendClassPath(codeRepository.getAbsolutePath());
            } catch (NotFoundException e) {
                throw new IllegalArgumentException("The given code directory [" + codeRepository + "] cannot be used!", e);
            }
        }
        return classPool;
    }

    @Nonnull
    private Collection<String> determineDeadClasses(@Nonnull AnalyzedCode analyzedCode) {
        Set<String> classesInUse = newHashSet();
        for (Iterable<String> usedClasses : analyzedCode.getDependenciesForClass().values()) {
            for (String clazz : usedClasses) {
                classesInUse.add(clazz);
            }
        }

        List<String> deadClasses = newArrayList(analyzedCode.getAnalyzedClasses());
        deadClasses.removeAll(classesInUse);
        return deadClasses;
    }

}

@@ -1271,32 +1271,287 @@
 Repositories) %7B%0A
+        CodeContext codeContext = createCodeContext(codeRepositories);%0A        AnalyzedCode analyzedCode = analyzeCode(codeContext);%0A        return computeDeadCode(analyzedCode);%0A    %7D%0A%0A    private CodeContext createCodeContext(File%5B%5D codeRepositories) %7B%0A
         ClassPoo
@@ -1690,33 +1690,14 @@
     
-CodeContext codeContext =
+return
  new
@@ -1752,17 +1752,86 @@
 sPool);%0A
-%0A
+    %7D%0A%0A    private AnalyzedCode analyzeCode(CodeContext codeContext) %7B
 %0A       
@@ -2086,32 +2086,133 @@
 xt));%0A        %7D%0A
+        return analyzedCode;%0A    %7D%0A%0A    private DeadCode computeDeadCode(AnalyzedCode analyzedCode) %7B
 %0A        Collect
@@ -2269,25 +2269,24 @@
 lyzedCode);%0A
-%0A
         retu

clean code