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.Sprite;
import client.renderer.texture.TextureMap;
import common.block.Block;
import common.block.liquid.BlockDynamicLiquid;
import common.block.liquid.BlockLiquid;
import common.block.liquid.BlockStaticLiquid;
import common.collect.Lists;
import common.collect.Maps;
import common.collect.Sets;
import common.init.BlockRegistry;
import common.init.ItemRegistry;
import common.item.Item;
import common.model.Model.ModelProvider;
import common.model.ModelRotation;
import common.model.GuiPosition;
import common.model.Model;
import common.util.Facing;
import common.util.Pair;
import common.world.State;

public abstract class ModelBakery
{
    private static final Set<String> BUILTINS = Sets.newHashSet(
    		"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"
    );
    protected static final String MISSING = "builtin/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(Pair<BlockStaticLiquid, BlockDynamicLiquid> liquid : BlockLiquid.LIQUIDS) {
    		String name = BlockRegistry.getName(liquid.first());
    		BUILTINS.add("blocks/" + name + "_flow");
    		BUILTINS.add("blocks/" + name + "_still");
		}
    }

    public static Map<String, IBakedModel> setupModelRegistry(TextureMap textureMap, 
    		Map<State, String> map)
    {
    	final ModelProvider provider = new ModelProvider() {
    		public Model getModel(String primary) {
    			return new ModelBlock(primary);
    		}
    	};
        final Map<String, Sprite> sprites = Maps.<String, Sprite>newHashMap();
        Map<String, ModelBlock> models = Maps.<String, ModelBlock>newLinkedHashMap();
        List<String> variants = Lists.<String>newArrayList();
        FaceBakery faceBakery = new FaceBakery();
        Map<String, IBakedModel> bakedRegistry = Maps.newHashMap();
        List<String> itemLocations = Lists.<String>newArrayList();
		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(provider, BlockRegistry.getName(entry.getKey().getBlock())
							.toString(), entry.getKey());
    		models.put(entry.getValue(), model);
    		variants.add(entry.getValue());
    	}
        for (Item item : ItemRegistry.items())
        {
    		String loc = "item/" + ItemRegistry.getName(item);
            models.put(loc, getModel(item, provider, ItemRegistry.getName(item)));
            itemLocations.add(loc);
        	String[] extra = item.getSprites();
        	if(extra != null) {
	        	for(String sprite : extra) {
	        		loc = "item/" + sprite;
	                models.put(loc, new ModelBlock(GuiPosition.NORMAL, sprite));
	                itemLocations.add(loc);
	            }
        	}
        }
        
    	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());
            }
        }

        set.addAll(BUILTINS);
    	for (String resourcelocation : itemLocations)
        {
            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.remove(TextureMap.MISSING);
        textureMap.loadSprites(set, sprites);
        sprites.put(TextureMap.MISSING, 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 (Sprite textureatlassprite : sprites.values())
        {
            if (!textureatlassprite.isAnimated())
            {
                textureatlassprite.clearFramesTextureData();
            }
        }
        
        for (String modelresourcelocation : variants)
        {
            ModelBlock modelblock = models.get(modelresourcelocation);

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

        for (String entry : itemLocations)
        {
            ModelBlock modelblock1 = (ModelBlock)models.get(entry);

            if (modelblock1 != null)
            {
                if (modelblock1.getParent() == MODEL_ENTITY)
                {
                    bakedRegistry.put(entry, new BuiltInModel(modelblock1.getTransform()));
                }
                else
                {
                	bakedRegistry.put(entry, bakeModel(sprites, faceBakery, textureMap.getMissingSprite(), 
                			modelblock1, modelblock1.getRotation(), modelblock1.isUvLocked()));
                }
            }
            else
            {
            	throw new RuntimeException("Fehlendes Modell für: " + entry);
            }
        }
        
        return bakedRegistry;
    }

	private static final ModelBlock getModel(Item item, ModelProvider provider, String name) {
		if(item.hasBuiltinModel())
	    	return new ModelBlock(MODEL_ENTITY, GuiPosition.NORMAL);
		Block block = item.getBlock();
		State state = block == null ? null : block.getItemState();
		if(state != null)
			return new ModelBlock((ModelBlock)state.getBlock().getModel(provider, BlockRegistry.getName(state.getBlock()), state), state.getBlock().getItemPosition());
		return new ModelBlock(GuiPosition.NORMAL, item.getTextures(name));
	}

    private static IBakedModel bakeModel(Map<String, Sprite> sprites, FaceBakery faceBakery,
    		Sprite fallback, ModelBlock modelBlockIn, ModelRotation modelRotationIn, boolean uvLocked)
    {
        Sprite 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);
                Sprite 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();
    }
}
