package client.renderer.texture;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import client.Client;
import client.renderer.entity.RenderNpc;
import client.renderer.layers.LayerExtra;
import client.util.FileUtils;
import common.collect.Lists;
import common.collect.Maps;
import common.collect.Sets;
import common.entity.npc.EntityNPC;
import common.init.SpeciesRegistry;
import common.init.SpeciesRegistry.ModelType;
import common.log.Log;

public abstract class EntityTexManager
{
	public static String altTexture = null;
	public static int altLayer = -1;
	public static String altNpcLayer = null;

    private static final Set<Integer> USER_TEXTURES = Sets.newHashSet();
    private static final Map<Integer, LayerExtra> USER_LAYERS = Maps.newHashMap();
    private static final Map<String, LayerExtra> NPC_LAYERS = Maps.newHashMap();
    private static final Map<ModelType, String> DEF_TEXTURES = Maps.newEnumMap(ModelType.class);
    private static final Map<ModelType, LayerExtra> DEF_LAYERS = Maps.newEnumMap(ModelType.class);
    
    public static void loadNpcTextures() {
    	TextureManager manager = Client.CLIENT.getTextureManager();
    	for(Entry<String, ModelType> entry : SpeciesRegistry.SKINS.entrySet()) {
    		String skin = entry.getKey();
//    		skin = skin.startsWith("~") ? skin.substring(1) : skin;
    		String loc = EntityNPC.getSkinTexture(skin);
    		BufferedImage image;
    		try {
				image = TextureUtil.readImage(FileUtils.getResource(loc));
			}
			catch(IOException e) {
				if(e instanceof FileNotFoundException)
					Log.IO.warn("Textur ist nicht vorhanden: " + loc);
				else
					Log.IO.error(e, "Konnte Textur nicht laden: " + loc);
				try {
					image = TextureUtil.readImage(FileUtils.getResource(getDefault(entry.getValue())));
				}
				catch(IOException e2) {
					if(e2 instanceof FileNotFoundException)
						Log.IO.warn("Textur ist nicht vorhanden: " + loc);
					else
						Log.IO.error(e2, "Konnte Textur nicht laden: " + loc);
					image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
					Graphics2D g = image.createGraphics();
					g.setColor(Color.BLACK);
					g.fillRect(0, 0, 64, 64);
					g.dispose();
				}
			}
        	loc = getNpcSkinLocation(skin);
        	DynamicTexture dyntex = (DynamicTexture)manager.getTexture(loc);
    		if(dyntex == null || (dyntex.getWidth() != image.getWidth() || dyntex.getHeight() != image.getHeight())) {
    			if(dyntex != null)
    				manager.deleteTexture(loc);
    			dyntex = new DynamicTexture(image);
    			manager.loadTexture(loc, dyntex);
    		}
    		else {
    			image.getRGB(0, 0, image.getWidth(), image.getHeight(), dyntex.getData(), 0, image.getWidth());
    		}
    		dyntex.updateTexture();
    		LayerExtra extra = LayerExtra.getLayer(dyntex.getData(), dyntex.getWidth(), dyntex.getHeight(), entry.getValue());
    		if(EntityNPC.getSkinTexture(skin).equals(getDefault(entry.getValue()))) {
    			DEF_LAYERS.put(entry.getValue(), extra);
    		}
    		if(extra != null) {
    			extra = NPC_LAYERS.put(skin, extra);
    		}
    		else {
    			extra = NPC_LAYERS.remove(skin);
    		}
    		if(extra != null)
    			extra.discard();
//    		setTexture(skin, image, entry.getValue());
    	}
    }
    
    public static String getDefault(ModelType model) {
    	String def = DEF_TEXTURES.get(model);
    	if(def == null)
    		DEF_TEXTURES.put(model, def = Client.CLIENT.getRenderManager().getRenderObject(model).getDefaultTexture());
    	return def;
    }
    
    private static String getSkinLocation(int id) {
    	return "skins/player" + id + "/dyn";
    }
    
    private static String getNpcSkinLocation(String user) {
    	return "npcs/" + user.toLowerCase() + "/dyn";
    }

    public static LayerExtra getLayer(int id, String skin, ModelType model)
    {
    	if(id == -1)
    		return Client.CLIENT.getTextureManager().getTexture(getNpcSkinLocation(skin)) != null ? NPC_LAYERS.get(skin.toLowerCase()) : DEF_LAYERS.get(model);
    	return altNpcLayer != null ? Client.CLIENT.getTextureManager().getTexture(getNpcSkinLocation(altNpcLayer)) != null ? 
    			NPC_LAYERS.get(altNpcLayer.toLowerCase()) : DEF_LAYERS.get(model) : (altLayer != -1 ? USER_LAYERS.get(altLayer) : (USER_TEXTURES.contains(id) ? USER_LAYERS.get(id) : (skin != null && Client.CLIENT.getTextureManager().getTexture(getNpcSkinLocation(skin)) != null ? NPC_LAYERS.get(skin.toLowerCase()) : DEF_LAYERS.get(model))));
    }
    
    public static String getSkin(int id, String skin, ModelType model)
    {
    	String loc = id != -1 ? (altTexture != null ? altTexture : getSkinLocation(id)) : getNpcSkinLocation(skin);
    	if(id != -1 && skin != null && Client.CLIENT.getTextureManager().getTexture(loc) == null)
    		loc = getNpcSkinLocation(skin);
        return Client.CLIENT.getTextureManager().getTexture(loc) != null ? loc : getDefault(model);
    }
    
    public static boolean hasCustomSkin(int id) {
    	return Client.CLIENT.getTextureManager().getTexture(getSkinLocation(id)) != null;
    }
    
    public static void clearTextures() {
    	List<Integer> list = Lists.newArrayList(USER_TEXTURES);
    	for(Integer user : list) {
    		setTexture(user, null, null);
    	}
    }
    
    public static void setTexture(int id, byte[] comp, ModelType model) {
    	if(comp == null && !USER_TEXTURES.contains(id))
    		return;
//    	user = user.toLowerCase();
    	TextureManager manager = Client.CLIENT.getTextureManager();
    	String loc = getSkinLocation(id);
    	DynamicTexture dyntex = (DynamicTexture)manager.getTexture(loc);
    	if(comp == null) {
    		if(dyntex != null)
    			manager.deleteTexture(loc);
    		USER_TEXTURES.remove(id);
    		LayerExtra layer = USER_LAYERS.remove(id);
    		if(layer != null)
    			layer.discard();
    	}
    	else {
    		if(dyntex == null || (dyntex.getWidth() != model.texWidth || dyntex.getHeight() != model.texHeight)) {
    			if(dyntex != null)
    				manager.deleteTexture(loc);
    			dyntex = new DynamicTexture(model.texWidth, model.texHeight);
    			manager.loadTexture(loc, dyntex);
    		}
    		EntityTexManager.compToImage(comp, dyntex.getData(), model);
    		dyntex.updateTexture();
    		USER_TEXTURES.add(id);
    		LayerExtra extra = LayerExtra.getLayer(dyntex.getData(), dyntex.getWidth(), dyntex.getHeight(), model);
    		if(extra != null) {
    			extra = USER_LAYERS.put(id, extra);
    		}
    		else {
    			extra = USER_LAYERS.remove(id);
    		}
    		if(extra != null)
    			extra.discard();
    	}
    }

	public static void compToImage(byte[] comp, int[] img, ModelType model) {
		RenderNpc render = Client.CLIENT.getRenderManager().getRenderObject(model);
		if(comp.length < render.getCompressedSize())
			comp = new byte[render.getCompressedSize()];
		render.compToImage(comp, img, model.texWidth);
	}

	public static byte[] imageToComp(int[] img, ModelType model) {
		RenderNpc render = Client.CLIENT.getRenderManager().getRenderObject(model);
		byte[] comp = new byte[render.getCompressedSize()];
		render.imageToComp(comp, img, model.texWidth);
		return comp;
	}
	
	public static String getSkin(EntityNPC entity) {
		return getSkin(entity.isPlayer() ? entity.getId() : -1, entity.getChar(), entity.getSpecies().renderer);
	}
	
	public static LayerExtra getLayer(EntityNPC entity) {
		return getLayer(entity.isPlayer() ? entity.getId() : -1, entity.getChar(), entity.getSpecies().renderer);
	}
}
