package client.renderer.entity;

import java.util.ArrayList;

import client.renderer.model.ModelBase;
import client.renderer.texture.EntityTexManager;
import common.entity.npc.EntityNPC;
import common.network.IPlayer;


public abstract class RenderNpc extends RenderLiving<EntityNPC>
{
	private static class TexInfo {
		private final int x;
		private final int y;
		private final int w;
		private final int h;
		
		private TexInfo(int x, int y, int w, int h) {
			this.x = x;
			this.y = y;
			this.w = w;
			this.h = h;
		}
	}
	
	protected static class TexList extends ArrayList<TexInfo> {
		private int size;
		
		protected void add(int x, int y, int w, int h) {
			this.add(new TexInfo(x, y, w, h));
			this.size += w * h;
		}
		
		private int[] compile() {
			int[] data = new int[this.size() << 4];
			for(int z = 0; z < this.size(); z++) {
				TexInfo tex = this.get(z);
				data[z << 4] = tex.x;
				data[(z << 4) | 1] = tex.y;
				data[(z << 4) | 2] = tex.w;
				data[(z << 4) | 3] = tex.h;
			}
			this.clear();
			return data;
		}
		
		private int getSize() {
			return this.size;
		}
	}
	
	protected static class CoordList extends ArrayList<int[]> {
		protected void add(int rx, int ry, int x, int y, int w, int h) {
			this.add(new int[] {x, y, w, h, rx, ry});
		}
	}
	
	private int compressedSize;
	private int[] opaqueSegments;
	private int[] alphaSegments;
	
	protected void initSegments() {
        TexList opaque = new TexList();
        TexList alpha = new TexList();
        this.getSegments(opaque, alpha);
        this.compressedSize = alpha.getSize() * 4 + opaque.getSize() * 3;
        if(this.compressedSize > IPlayer.MAX_SKIN_SIZE)
        	throw new IllegalArgumentException("Renderer " + this.getClass() + ": Textur zu Groß (" + this.compressedSize + " Bytes)");
        this.opaqueSegments = opaque.compile();
        this.alphaSegments = alpha.compile();
//        Log.debug("Renderer " + this.getClass().getSimpleName() + ": " + this.compressedSize + " Bytes Texturgöße");
	}
	
    public RenderNpc(RenderManager renderManager, ModelBase model)
    {
        super(renderManager, model);
    }

//    public void renderName(EntityNPC entity, double x, double y, double z)
//    {
//        super.renderName(entity, x, y, z);
//        if(entity.isPlayer()Client && ((EntityNPCClient)entity).getHeadList() != null 
//        		&& this.canRenderStats(entity) && entity == this.renderManager.pointedEntity)
//        	this.renderLabel(entity, x, y + 0.6, z, ((EntityNPCClient)entity).getHeadList());
//    }
    
//    protected boolean canRenderName(EntityNPC entity)
//    {
//    	return entity == this.renderManager.pointedEntity && this.canRenderStats(entity);
//    }
    
    protected String getEntityTexture(EntityNPC entity)
    {
    	return EntityTexManager.getSkin(entity);
    }
    
    public void renderPlayerArm(EntityNPC entity)
    {
    }
    
    public abstract String getDefaultTexture();

	public final int getCompressedSize() {
		return this.compressedSize;
	}

	public final int compToImage(byte[] comp, int[] img, int stride) {
		int pos = 0;
		for(int z = 0; z < this.alphaSegments.length >> 2; z++)
			pos = copyData(comp, pos, stride, img, this.alphaSegments[z << 2], this.alphaSegments[(z << 2) | 1], 
					this.alphaSegments[(z << 2) | 2], this.alphaSegments[(z << 2) | 3], true);
		for(int z = 0; z < this.opaqueSegments.length >> 2; z++)
			pos = copyData(comp, pos, stride, img, this.opaqueSegments[z << 2], this.opaqueSegments[(z << 2) | 1], 
					this.opaqueSegments[(z << 2) | 2], this.opaqueSegments[(z << 2) | 3], false);
		return pos;
	}

	public final int imageToComp(byte[] comp, int[] img, int stride) {
		int pos = 0;
		for(int z = 0; z < this.alphaSegments.length >> 2; z++)
			pos = copyData(stride, img, this.alphaSegments[z << 2], this.alphaSegments[(z << 2) | 1], 
					this.alphaSegments[(z << 2) | 2], this.alphaSegments[(z << 2) | 3], comp, pos, true);
		for(int z = 0; z < this.opaqueSegments.length >> 2; z++)
			pos = copyData(stride, img, this.opaqueSegments[z << 2], this.opaqueSegments[(z << 2) | 1], 
					this.opaqueSegments[(z << 2) | 2], this.opaqueSegments[(z << 2) | 3], comp, pos, false);
		return pos;
	}

    public abstract void getSegments(TexList opaque, TexList alpha);
    public abstract void getHeadUVs(CoordList list);

	public static int copyData(byte[] comp, int coff, int stride, int[] img, int xo, int yo, int w, int h, boolean alpha) {
		for(int y = yo; y < h+yo; y++) {
			for(int x = xo; x < w+xo; x++) {
	    		img[y*stride+x] = ((((int)comp[coff+0]) << 16) & 0x00ff0000) | ((((int)comp[coff+1]) << 8) & 0x0000ff00) | (((int)comp[coff+2]) & 0x000000ff) 
	    				| (alpha ? ((int)comp[coff+3]) << 24 : 0xff000000);
	    		coff += alpha ? 4 : 3;
	    	}
		}
		return coff;
	}

	public static int copyData(int stride, int[] img, int xo, int yo, int w, int h, byte[] comp, int coff, boolean alpha) {
		for(int y = yo; y < h+yo; y++) {
			for(int x = xo; x < w+xo; x++) {
				comp[coff++] = (byte)((img[y*stride+x] >> 16) & 0xff);
				comp[coff++] = (byte)((img[y*stride+x] >> 8) & 0xff);
				comp[coff++] = (byte)(img[y*stride+x] & 0xff);
				if(alpha) {
	    			comp[coff++] = (byte)((img[y*stride+x] >> 24) & 0xff);
				}
	    	}
		}
		return coff;
	}
}
