package de.is24.deadcode4j;

import com.google.common.base.Optional;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.util.Collection;
import java.util.List;

import static com.google.common.collect.Iterables.addAll;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static de.is24.deadcode4j.Utils.addIfNonNull;

/**
 * A <code>Module</code> represents a java module from the deadcode4j perspective.
 *
 * @since 1.6
 */
public class Module {

    @Nonnull
    private final String moduleId;
    @Nullable
    private final String encoding;
    @Nonnull
    private final Collection<Resource> dependencies;
    @Nullable
    private final Repository outputRepository;
    @Nonnull
    private final List<Repository> allRepositories;

    /**
     * Creates a new <code>Module</code>.
     *
     * @param moduleId         the module's identifier
     * @param encoding         the module's source encoding
     * @param dependencies     the resources this module depends on
     * @param outputRepository the "output" repository - i.e. the directory where compiled classes can be found
     * @param repositories     additional repositories to analyze
     * @since 1.6
     */
    public Module(@Nonnull String moduleId,
                  @Nullable String encoding,
                  @Nonnull Collection<Resource> dependencies,
                  @Nullable Repository outputRepository,
                  @Nonnull Iterable<Repository> repositories) {
        this.moduleId = moduleId;
        this.encoding = encoding;
        this.dependencies = dependencies;
        this.outputRepository = outputRepository;
        this.allRepositories = newArrayList();
        addIfNonNull(allRepositories, outputRepository);
        addAll(allRepositories, repositories);
    }

    @Override
    public String toString() {
        StringBuilder buffy = new StringBuilder("Module [").append(this.moduleId).append("] with");
        if (this.outputRepository == null) {
            buffy.append("out");
        }
        buffy.append(" output repository");
        if (this.allRepositories.size() > 1) {
            buffy.append(" and ").append(this.allRepositories.size() - 1).append(" additional repositories");
        }
        return buffy.toString();
    }

    /**
     * Returns the module's ID.
     *
     * @since 1.6
     */
    @Nonnull
    public String getModuleId() {
        return moduleId;
    }


    /**
     * Returns the module's source file encoding.
     *
     * @since 1.6
     */
    @Nullable
    public String getEncoding() {
        return encoding;
    }

    /**
     * Returns all class path entries of this module.
     *
     * @since 1.6
     */
    @Nonnull
    public Iterable<File> getClassPath() {
        List<File> classPath = newArrayListWithCapacity(dependencies.size());
        for (Resource dependency : dependencies) {
            Optional<File> classPathEntry = dependency.getClassPathEntry();
            if (classPathEntry.isPresent()) {
                classPath.add(classPathEntry.get());
            }
        }
        return classPath;
    }

    /**
     * Returns the "output" repository - i.e. the directory where compiled classes can be found.
     *
     * @since 1.6
     */
    @Nullable
    public Repository getOutputRepository() {
        return outputRepository;
    }


    /**
     * Returns all repositories to analyze (including the {@link #getOutputRepository() output repository}).
     *
     * @since 1.6
     */
    @Nonnull
    public Iterable<Repository> getAllRepositories() {
        return this.allRepositories;
    }

}

@@ -1899,24 +1899,292 @@
 es);%0A    %7D%0A%0A
+    @Override%0A    public boolean equals(Object obj) %7B%0A        return this == obj %7C%7C Module.class.isInstance(obj) && this.moduleId.equals(Module.class.cast(obj).moduleId);%0A    %7D%0A%0A    @Override%0A    public int hashCode() %7B%0A        return this.moduleId.hashCode();%0A    %7D%0A%0A
     @Overrid
@@ -3992,10 +3992,513 @@
 %0A    %7D%0A%0A
+    private boolean requires(@Nonnull Module module) %7B%0A        for (Resource dependency : dependencies) %7B%0A            Optional%3CModule%3E optionalModule = dependency.getReferencedModule();%0A            if (!optionalModule.isPresent()) %7B%0A                continue;%0A            %7D%0A            Module referencedModule = optionalModule.get();%0A            if (module.equals(referencedModule) %7C%7C referencedModule.requires(module)) %7B%0A                return true;%0A            %7D%0A        %7D%0A        return false;%0A    %7D%0A%0A
 %7D%0A

implement equals(), hashcode() & means to determine if a module depends on another