package client.renderer.blockmodel;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import client.renderer.texture.IIconCreator;
import client.renderer.texture.TextureAtlasSprite;
import client.renderer.texture.TextureMap;
import common.collect.Lists;
import common.collect.Maps;
import common.collect.Sets;
import common.init.BlockRegistry;
import common.init.FluidRegistry;
import common.init.ItemRegistry;
import common.item.Item;
import common.item.ItemStack;
import common.model.ModelRotation;
import common.util.Facing;
import common.util.IRegistry;
import common.util.RegistrySimple;
import common.world.State;

public abstract class ModelBakery
{
    private static final Set<String> LOCATIONS_BUILTIN_TEXTURES = Sets.newHashSet(
//    		"blocks/water_flow", "blocks/water_still", 
//    		"blocks/lava_flow", "blocks/lava_still", 
    		"blocks/destroy_stage_0", "blocks/destroy_stage_1", 
    		"blocks/destroy_stage_2", "blocks/destroy_stage_3", 
    		"blocks/destroy_stage_4", "blocks/destroy_stage_5", 
    		"blocks/destroy_stage_6", "blocks/destroy_stage_7", 
    		"blocks/destroy_stage_8", "blocks/destroy_stage_9", 
    		"items/empty_armor_slot_helmet", "items/empty_armor_slot_chestplate", 
    		"items/empty_armor_slot_leggings", "items/empty_armor_slot_boots");
    protected static final String MISSING = "builtin/missing" + '#' + "missing";
    public static final ModelBlock MODEL_GENERATED = (ModelBlock)new ModelBlock(null).add().d("");
    public static final ModelBlock MODEL_ENTITY = (ModelBlock)new ModelBlock(null).add().d("");
    
    static {
    	for(int z = 0; z < FluidRegistry.getNumFluids(); z++) {
    		String name = BlockRegistry.getNameFromBlock(FluidRegistry.getStaticBlock(z)).toString();
    		LOCATIONS_BUILTIN_TEXTURES.add("blocks/" + name + "_flow");
    		LOCATIONS_BUILTIN_TEXTURES.add("blocks/" + name + "_still");
		}
    }

    public static IRegistry<String, IBakedModel> setupModelRegistry(TextureMap textureMap, 
    		Map<State, String> map)
    {
        final Map<String, TextureAtlasSprite> sprites = Maps.<String, TextureAtlasSprite>newHashMap();
        Map<String, ModelBlock> models = Maps.<String, ModelBlock>newLinkedHashMap();
        List<String> variants = Lists.<String>newArrayList();
        FaceBakery faceBakery = new FaceBakery();
        RegistrySimple<String, IBakedModel> bakedRegistry = new RegistrySimple();
        List<String> itemLocations = Lists.<String>newArrayList();
//        Map<Item, List<Integer>> variantNames = Maps.<Item, List<Integer>>newIdentityHashMap();
//    	variants.clear();
//    	Map<IBlockState, String> map = blockModelShapes.getMap();
		models.put(MISSING, (ModelBlock)new ModelBlock(null).add().all());
        variants.add(MISSING);
    	for(Entry<State, String> entry : map.entrySet()) {
    		ModelBlock model = (ModelBlock)entry.getKey().getBlock().getModel(ModelBlock.PROVIDER, BlockRegistry.getNameFromBlock(entry.getKey().getBlock())
							.toString(), entry.getKey());
//    		ResourceLocation blk = new ResourceLocation(entry.getValue().getName());
    		models.put(entry.getValue(), model);
    		variants.add(entry.getValue());
    	}
//        ResourceLocation loc = new ResourceLocation("item_frame");
//    	String res = "item_frame" + '#' + "normal";
//        ModelBlock model = new ModelBlock("birch_planks")
//			.add(3, 3, 15.5f, 13, 13, 16)
//				.n("itemframe_background").uv(3, 3, 13, 13).noCull()
//				.s("itemframe_background").uv(3, 3, 13, 13).noCull()
//			.add(2, 2, 15, 14, 3, 16)
//				.d().uv(2, 0, 14, 1).noCull()
//				.u().uv(2, 15, 14, 16).noCull()
//				.n().uv(2, 13, 14, 14).noCull()
//				.s().uv(2, 13, 14, 14).noCull()
//				.w().uv(15, 13, 16, 14).noCull()
//				.e().uv(0, 13, 1, 14).noCull()
//			.add(2, 13, 15, 14, 14, 16)
//				.d().uv(2, 0, 14, 1).noCull()
//				.u().uv(2, 15, 14, 16).noCull()
//				.n().uv(2, 2, 14, 3).noCull()
//				.s().uv(2, 2, 14, 3).noCull()
//				.w().uv(15, 2, 16, 3).noCull()
//				.e().uv(0, 2, 1, 3).noCull()
//			.add(2, 3, 15, 3, 13, 16)
//				.n().uv(13, 3, 14, 13).noCull()
//				.s().uv(2, 3, 3, 13).noCull()
//				.w().uv(15, 3, 16, 13).noCull()
//				.e().uv(0, 3, 1, 13).noCull()
//			.add(13, 3, 15, 14, 13, 16)
//				.n().uv(2, 3, 3, 13).noCull()
//				.s().uv(13, 3, 14, 13).noCull()
//				.w().uv(15, 3, 16, 13).noCull()
//				.e().uv(0, 3, 1, 13).noCull();
//		models.put(res, model);
//        variants.add(res);
//        res = "item_frame" + '#' + "map";
//        model = new ModelBlock("birch_planks")
//			.add(1, 1, 15.001f, 15, 15, 16)
//				.n("itemframe_background").uv(1, 1, 15, 15).noCull()
//				.s("itemframe_background").uv(1, 1, 15, 15).noCull()
//			.add(0, 0, 15.001f, 16, 1, 16)
//				.d().uv(0, 0, 16, 1).noCull()
//				.u().uv(0, 15, 16, 16).noCull()
//				.n().uv(0, 15, 16, 16).noCull()
//				.s().uv(0, 15, 16, 16).noCull()
//				.w().uv(15, 15, 16, 16).noCull()
//				.e().uv(0, 15, 1, 16).noCull()
//			.add(0, 15, 15.001f, 16, 16, 16)
//				.d().uv(0, 0, 16, 1).noCull()
//				.u().uv(0, 15, 16, 16).noCull()
//				.n().uv(0, 0, 16, 1).noCull()
//				.s().uv(0, 0, 16, 1).noCull()
//				.w().uv(15, 0, 16, 1).noCull()
//				.e().uv(0, 0, 1, 1).noCull()
//			.add(0, 1, 15.001f, 1, 15, 16)
//				.n().uv(15, 1, 16, 15).noCull()
//				.s().uv(0, 1, 1, 15).noCull()
//				.w().uv(15, 1, 16, 15).noCull()
//				.e().uv(0, 1, 1, 15).noCull()
//			.add(15, 1, 15.001f, 16, 15, 16)
//				.n().uv(0, 1, 1, 15).noCull()
//				.s().uv(15, 1, 16, 15).noCull()
//				.w().uv(15, 1, 16, 15).noCull()
//				.e().uv(0, 1, 1, 15).noCull();
//		models.put(res, model);
//        variants.add(res);
        
//        RenderRegistry.registerVariants(variantNames);
        List<ItemStack> stacks = Lists.newArrayList();
        for (Item item : ItemRegistry.REGISTRY)
        {
//        	List<Integer> list = variantNames.get(item);
//			if(list == null)
//			    list = Collections.<Integer>singletonList(0);
//            for(Integer s : list)
//            {
        	item.getRenderItems(item, stacks);
        	for(ItemStack stack : stacks) {
        		String resourcelocation = "item/" + ItemRegistry.getNameFromItem(item).toString() + "#" + stack.getMetadata() + '#' + "inventory";
                models.put(resourcelocation, (ModelBlock)item.getModel(ModelBlock.PROVIDER, ItemRegistry.getNameFromItem(item).toString(), stack.getMetadata()));
                itemLocations.add(resourcelocation);
            }
        	stacks.clear();
        }
        
    	final Set<String> set = Sets.<String>newHashSet();
        List<String> list = Lists.newArrayList(variants);
        Collections.sort(list, new Comparator<String>()
        {
            public int compare(String p_compare_1_, String p_compare_2_)
            {
                return p_compare_1_.compareTo(p_compare_2_);
            }
        });

        for (String modelresourcelocation : list)
        {
            ModelBlock modelblock = models.get(modelresourcelocation);

            if (modelblock == null)
            {
            	throw new RuntimeException("Fehlendes Modell für: " + modelresourcelocation);
            }
            else
            {

                for (BlockPart blockpart : modelblock.getElements())
                {
                    for (BlockPartFace blockpartface : blockpart.mapFaces.values())
                    {
                        set.add(blockpartface.texture);
                    }
                }

                set.add(modelblock.getPrimary());
//                return set;
//                set.addAll(getTextureLocations(modelblock));
            }
        }

        set.addAll(LOCATIONS_BUILTIN_TEXTURES);
//    	final Set<ResourceLocation> set = getVariantsTextureLocations();
    	for (String resourcelocation : itemLocations) // .values())
        {
            ModelBlock modelblock = (ModelBlock)models.get(resourcelocation);

            if (modelblock != null)
            {
                set.add(modelblock.getPrimary());

                if (modelblock.getParent() == MODEL_GENERATED)
                {
                    for (int n = 0; n < modelblock.getNumTextures(); n++)
                    {
                        set.add(modelblock.getTexture(n));
                    }
                }
                else if (modelblock.getParent() != MODEL_ENTITY)
                {
                    for (BlockPart blockpart : modelblock.getElements())
                    {
                        for (BlockPartFace blockpartface : blockpart.mapFaces.values())
                        {
                            set.add(blockpartface.texture);
                        }
                    }
                }
            }
        }
//        set.addAll(getItemsTextureLocations());
        set.remove(TextureMap.LOCATION_MISSING_TEXTURE);
        IIconCreator iiconcreator = new IIconCreator()
        {
            public void registerSprites(TextureMap iconRegistry)
            {
                for (String resourcelocation : set)
                {
                    TextureAtlasSprite textureatlassprite = iconRegistry.registerSprite(resourcelocation);
                    sprites.put(resourcelocation, textureatlassprite);
                }
            }
        };
        textureMap.loadSprites(iiconcreator);
        sprites.put(TextureMap.LOCATION_MISSING_TEXTURE, textureMap.getMissingSprite());
        
        for (String resourcelocation : itemLocations) // .values())
        {
            ModelBlock modelblock = models.get(resourcelocation);

            if (modelblock != null && modelblock.getParent() == MODEL_GENERATED)
            {
                ModelBlock modelblock1 = ModelGenerator.makeItemModel(textureMap, modelblock);
                models.put(resourcelocation, modelblock1);
            }
            else if (modelblock != null && modelblock.getParent() == MODEL_ENTITY)
            {
                models.put(resourcelocation, modelblock);
            }
        }

        for (TextureAtlasSprite textureatlassprite : sprites.values())
        {
            if (!textureatlassprite.isAnimated())
            {
                textureatlassprite.clearFramesTextureData();
            }
        }
        
        for (String modelresourcelocation : variants)
        {
            ModelBlock modelblock = models.get(modelresourcelocation);

            if (modelblock != null)
            {
                bakedRegistry.putObject(modelresourcelocation, bakeModel(sprites, faceBakery, textureMap.getMissingSprite(), 
                		modelblock, modelblock.getRotation(), modelblock.isUvLocked()));
            }
            else
            {
            	throw new RuntimeException("Fehlendes Modell für: " + modelresourcelocation);
            }
        }

//        for (Entry<String, ResourceLocation> entry : itemLocations.entrySet())
        for (String entry : itemLocations)
        {
//            ResourceLocation resourcelocation = (ResourceLocation)entry.getValue();
//            ResourceLocation inventory = new ResourceLocation((String)entry.getKey(), "inventory");
            ModelBlock modelblock1 = (ModelBlock)models.get(entry) ; // resourcelocation);

            if (modelblock1 != null)
            {
                if (modelblock1.getParent() == MODEL_ENTITY)
                {
                    bakedRegistry.putObject(entry /* inventory */, new BuiltInModel(modelblock1.getTransform()));
                }
                else
                {
                	bakedRegistry.putObject(entry /* inventory */, bakeModel(sprites, faceBakery, textureMap.getMissingSprite(), 
                			modelblock1, ModelRotation.X0_Y0, false));
                }
            }
            else
            {
            	throw new RuntimeException("Fehlendes Modell für: " + entry); // resourcelocation);
            }
        }
        
        return bakedRegistry;
    }

    private static IBakedModel bakeModel(Map<String, TextureAtlasSprite> sprites, FaceBakery faceBakery,
    		TextureAtlasSprite fallback, ModelBlock modelBlockIn, ModelRotation modelRotationIn, boolean uvLocked)
    {
        TextureAtlasSprite particle = sprites.get(modelBlockIn.getPrimary());
        BakedModel.Builder builder = new BakedModel.Builder(modelBlockIn).setTexture(particle == null ? fallback : particle);
        for (BlockPart blockpart : modelBlockIn.getElements())
        {
            for (Facing enumfacing : blockpart.mapFaces.keySet())
            {
                BlockPartFace face = blockpart.mapFaces.get(enumfacing);
                TextureAtlasSprite sprite = sprites.get(face.texture);
                sprite = sprite == null ? fallback : sprite;

                if (face.cull == null)
                    builder.addGeneralQuad(faceBakery.makeBakedQuad(blockpart.positionFrom, blockpart.positionTo, face, sprite, enumfacing, modelRotationIn, blockpart.partRotation, uvLocked, blockpart.shade));
                else
                    builder.addFaceQuad(modelRotationIn.rotateFace(face.cull), faceBakery.makeBakedQuad(blockpart.positionFrom, blockpart.positionTo, face, sprite, enumfacing, modelRotationIn, blockpart.partRotation, uvLocked, blockpart.shade));
            }
        }
        return builder.makeBakedModel();
    }
}
