package net.minecraftforge.client.model;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.IReloadableResourceManager;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.ModelLoader.VanillaLoader;
import net.minecraftforge.client.model.b3d.B3DLoader;
import net.minecraftforge.client.model.obj.OBJLoader;
import net.minecraftforge.fml.common.FMLLog;

import org.apache.logging.log4j.Level;

/*
 * Central hub for custom model loaders.
 */
public class ModelLoaderRegistry
{
    private static final Set<ICustomModelLoader> loaders = new HashSet<ICustomModelLoader>();
    private static final Map<ResourceLocation, IModel> cache = new HashMap<ResourceLocation, IModel>();

    // Forge built-in loaders
    static
    {
        registerLoader(B3DLoader.instance);
        registerLoader(OBJLoader.instance);
        registerLoader(ModelFluid.FluidLoader.instance);
        registerLoader(ItemLayerModel.Loader.instance);
        registerLoader(MultiLayerModel.Loader.instance);
        registerLoader(ModelDynBucket.LoaderDynBucket.instance);
    }

    /*
     * Makes system aware of your loader.
     */
    public static void registerLoader(ICustomModelLoader loader)
    {
        loaders.add(loader);
        ((IReloadableResourceManager)Minecraft.getMinecraft().getResourceManager()).registerReloadListener(new IResourceManagerReloadListener()
        {
            public void onResourceManagerReload(IResourceManager manager)
            {
                for(ICustomModelLoader loader : loaders) loader.onResourceManagerReload(manager);
            }
        });
    }

    public static boolean loaded(ResourceLocation location)
    {
        return cache.containsKey(location);
    }


    public static ResourceLocation getActualLocation(ResourceLocation location)
    {
        if(location.getResourcePath().startsWith("builtin/")) return location;
        return new ResourceLocation(location.getResourceDomain(), "models/" + location.getResourcePath());
    }

    /**
     * Primary method to get IModel instances.
     * ResourceLocation argument will be passed directly to the custom model loaders,
     * ModelResourceLocation argument will be loaded through the blockstate system.
     */
    public static IModel getModel(ResourceLocation location) throws IOException
    {
        IModel model;
        if(location instanceof ModelResourceLocation)
        {
            ModelLoader loader = ModelLoader.VanillaLoader.instance.getLoader();
            if(loader != null)
            {
                model = loader.getVariantModel((ModelResourceLocation)location);
            }
            else
            {
                FMLLog.log(Level.ERROR, "Loading model too early, skipping: %s", location);
                model = getMissingModel();
            }
        }
        else
        {
            if(cache.containsKey(location)) return cache.get(location);
            ResourceLocation actual = getActualLocation(location);
            ICustomModelLoader accepted = null;
            for(ICustomModelLoader loader : loaders)
            {
                try
                {
                    if(loader.accepts(actual))
                    {
                        if(accepted != null)
                        {
                            FMLLog.severe("2 loaders (%s and %s) want to load the same model %s", accepted, loader, location);
                            throw new IllegalStateException("2 loaders want to load the same model");
                        }
                        accepted = loader;
                    }
                }
                catch(Exception e)
                {
                    FMLLog.log(Level.ERROR, e, "Exception checking if model %s can be loaded with loader %s, skipping", location, loader);
                }
            }

            // no custom loaders found, try vanilla one
            if(accepted == null)
            {
                if(VanillaLoader.instance.accepts(actual)) accepted = VanillaLoader.instance;
            }

            if(accepted == null)
            {
                FMLLog.severe("no suitable loader found for the model %s, skipping", location);
                model = getMissingModel();
            }
            else
            {
                try
                {
                    model = accepted.loadModel(actual);
                }
                catch (IOException e)
                {
                    throw e;
                }
                catch(Exception e)
                {
                    FMLLog.log(Level.ERROR, e, "Exception loading model %s with loader %s, skipping", location, accepted);
                    model = getMissingModel();
                }
            }
        }
        cache.put(location, model);
        return model;
    }

    public static IModel getMissingModel()
    {
        return ModelLoader.VanillaLoader.instance.getLoader().getMissingModel();
    }

    public static void clearModelCache()
    {
        cache.clear();
        // putting the builtin models in
        cache.put(new ResourceLocation("minecraft:builtin/generated"), ModelLoader.VanillaLoader.instance.getLoader().getItemModel());
        cache.put(new ResourceLocation("minecraft:block/builtin/generated"), ModelLoader.VanillaLoader.instance.getLoader().getItemModel());
        cache.put(new ResourceLocation("minecraft:item/builtin/generated"), ModelLoader.VanillaLoader.instance.getLoader().getItemModel());
    }
}

@@ -1042,16 +1042,63 @@
 odel%3E();
+%0A    private static boolean registered = false;
 %0A%0A    //
@@ -1617,32 +1617,70 @@
 rs.add(loader);%0A
+        if(!registered)%0A        %7B%0A    
         ((IReloa
@@ -1700,16 +1700,17 @@
 Manager)
+ 
 Minecraf
@@ -1808,34 +1808,42 @@
 tener()%0A        
+ 
-%7B%0A
+   %7B%0A    
             publ
@@ -1904,34 +1904,40 @@
 er)%0A            
-%7B%0A
+    %7B%0A  
                 
@@ -1928,35 +1928,38 @@
                 
+  
 for
+ 
 (ICustomModelLoa
@@ -2033,26 +2033,33 @@
         
-%7D%0A
+    %7D%0A   
         
 %7D);%0A    
@@ -2054,12 +2054,54 @@
     
+ 
 %7D);%0A
+            registered = true;%0A        %7D%0A
     

Fix multiple registrations of reloading hooks in custom model loaders.