/*
 * Forge Mod Loader
 * Copyright (c) 2012-2013 cpw.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 * Contributors:
 *     cpw - implementation
 */

package net.minecraftforge.fml.common.discovery;

import java.io.File;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.LoaderException;
import net.minecraftforge.fml.common.ModClassLoader;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.relauncher.CoreModManager;
import net.minecraftforge.fml.relauncher.FileListHelper;
import net.minecraftforge.fml.relauncher.ModListHelper;

import org.apache.logging.log4j.Level;

import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.ObjectArrays;

public class ModDiscoverer
{
    private static Pattern zipJar = Pattern.compile("(.+).(zip|jar)$");

    private List<ModCandidate> candidates = Lists.newArrayList();

    private ASMDataTable dataTable = new ASMDataTable();

    private List<File> nonModLibs = Lists.newArrayList();

    public void findClasspathMods(ModClassLoader modClassLoader)
    {
        List<String> knownLibraries = ImmutableList.<String>builder()
                // skip default libs
                .addAll(modClassLoader.getDefaultLibraries())
                // skip loaded coremods
                .addAll(CoreModManager.getLoadedCoremods())
                // skip reparse coremods here
                .addAll(CoreModManager.getReparseableCoremods())
                .build();
        File[] minecraftSources = modClassLoader.getParentSources();
        if (minecraftSources.length == 1 && minecraftSources[0].isFile())
        {
            FMLLog.fine("Minecraft is a file at %s, loading", minecraftSources[0].getAbsolutePath());
            candidates.add(new ModCandidate(minecraftSources[0], minecraftSources[0], ContainerType.JAR, true, true));
        }
        else
        {
            for (int i = 0; i < minecraftSources.length; i++)
            {
                if (minecraftSources[i].isFile())
                {
                    if (knownLibraries.contains(minecraftSources[i].getName()))
                    {
                        FMLLog.finer("Skipping known library file %s", minecraftSources[i].getAbsolutePath());
                    }
                    else
                    {
                        FMLLog.fine("Found a minecraft related file at %s, examining for mod candidates", minecraftSources[i].getAbsolutePath());
                        candidates.add(new ModCandidate(minecraftSources[i], minecraftSources[i], ContainerType.JAR, i==0, true));
                    }
                }
                else if (minecraftSources[i].isDirectory())
                {
                    FMLLog.fine("Found a minecraft related directory at %s, examining for mod candidates", minecraftSources[i].getAbsolutePath());
                    candidates.add(new ModCandidate(minecraftSources[i], minecraftSources[i], ContainerType.DIR, i==0, true));
                }
            }
        }

    }

    public void findModDirMods(File modsDir)
    {
        File[] modList = FileListHelper.sortFileList(modsDir, null);
        modList = FileListHelper.sortFileList(ObjectArrays.concat(modList, ModListHelper.additionalMods.values().toArray(new File[0]), File.class));

        for (File modFile : modList)
        {
            // skip loaded coremods
            if (CoreModManager.getLoadedCoremods().contains(modFile.getName()))
            {
                FMLLog.finer("Skipping already parsed coremod or tweaker %s", modFile.getName());
            }
            else if (modFile.isDirectory())
            {
                FMLLog.fine("Found a candidate mod directory %s", modFile.getName());
                candidates.add(new ModCandidate(modFile, modFile, ContainerType.DIR));
            }
            else
            {
                Matcher matcher = zipJar.matcher(modFile.getName());

                if (matcher.matches())
                {
                    FMLLog.fine("Found a candidate zip or jar file %s", matcher.group(0));
                    candidates.add(new ModCandidate(modFile, modFile, ContainerType.JAR));
                }
                else
                {
                    FMLLog.fine("Ignoring unknown file %s in mods directory", modFile.getName());
                }
            }
        }
    }

    public List<ModContainer> identifyMods()
    {
        List<ModContainer> modList = Lists.newArrayList();

        for (ModCandidate candidate : candidates)
        {
            try
            {
                List<ModContainer> mods = candidate.explore(dataTable);
                if (mods.isEmpty() && !candidate.isClasspath())
                {
                    nonModLibs.add(candidate.getModContainer());
                }
                else
                {
                    modList.addAll(mods);
                }
            }
            catch (LoaderException le)
            {
                FMLLog.log(Level.WARN, le, "Identified a problem with the mod candidate %s, ignoring this source", candidate.getModContainer());
            }
            catch (Throwable t)
            {
                Throwables.propagate(t);
            }
        }

        return modList;
    }

    public ASMDataTable getASMTable()
    {
        return dataTable;
    }

    public List<File> getNonModLibs()
    {
        return nonModLibs;
    }
}

@@ -2139,33 +2139,36 @@
             
-c
+addC
 andidate
 s.add(new Mo
@@ -2147,37 +2147,32 @@
     addCandidate
-s.add
 (new ModCandidat
@@ -2873,33 +2873,36 @@
             
-c
+addC
 andidate
 s.add(new Mo
@@ -2881,37 +2881,32 @@
     addCandidate
-s.add
 (new ModCandidat
@@ -3263,33 +3263,36 @@
             
-c
+addC
 andidate
 s.add(new Mo
@@ -3271,37 +3271,32 @@
     addCandidate
-s.add
 (new ModCandidat
@@ -4137,33 +4137,36 @@
             
-c
+addC
 andidate
 s.add(new Mo
@@ -4145,37 +4145,32 @@
     addCandidate
-s.add
 (new ModCandidat
@@ -4493,25 +4493,28 @@
         
-c
+addC
 andidate
 s.add(ne
@@ -4505,21 +4505,16 @@
 andidate
-s.add
 (new Mod
@@ -5785,32 +5785,32 @@
 ModLibs()%0A    %7B%0A
-
         return n
@@ -5825,11 +5825,392 @@
 s;%0A    %7D
+%0A%0A    private void addCandidate(ModCandidate candidate)%0A    %7B%0A        for (ModCandidate c : candidates)%0A        %7B%0A            if (c.getModContainer().equals(candidate.getModContainer()))%0A            %7B%0A                FMLLog.finer(%22  Skipping already in list %25s%22, candidate.getModContainer());%0A                return;%0A            %7D%0A        %7D%0A        candidates.add(candidate);%0A    %7D
 %0A%7D%0A

Add duplicate protection to ModDiscoverer, do not process files in the mods directory if we already found them in the classpath. {Such as if LiteLoader is installed and added it} Closes #557