package speiger.src.coreengine.ui.gui.font;

import speiger.src.coreengine.math.MathUtils;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsDevice;
import speiger.src.coreengine.platform.graphics.api.texture.Texture;
import speiger.src.coreengine.platform.graphics.api.texture.TextureSettings;
import speiger.src.coreengine.platform.graphics.api.texture.states.SwizzleMask;
import speiger.src.coreengine.platform.graphics.api.texture.states.TextureFormat;
import speiger.src.coreengine.ui.gui.font.glyth.Glyth;
import speiger.src.coreengine.ui.gui.font.glyth.IGlythSheetInfo;

public class FontTexture {
	private static final TextureSettings COLOR = TextureSettings.builder().build();
	private static final TextureSettings NO_COLOR = TextureSettings.builder()
			.format(TextureFormat.LUMINANCE)
			.swizzle(SwizzleMask.RED, SwizzleMask.RED, SwizzleMask.RED, SwizzleMask.RED)
			.build();
	
	Texture texture;
	boolean color;
	int bounds;
	Slot slot;
	
	public FontTexture(int bounds, boolean color) {
		this.texture = GraphicsDevice.get().createTexture(color ? COLOR : NO_COLOR, bounds, bounds);
		this.bounds = bounds;
		this.color = color;
		this.slot = new Slot(0, 0, bounds, bounds);
	}
	
	public static TextureFormat formatByColor(boolean colored) {
		return colored ? TextureFormat.RGBA : TextureFormat.R;
	}
	
	public Glyth build(IGlythSheetInfo info) {
		if(color != info.isColored()) return null;
		Slot result = slot.insert(info);
		if(result != null) {
			info.upload(texture, result.x, result.y);
			float minU = (float)(result.x) / (float)bounds;
			float maxU = (float)((result.x) + info.width()) / (float)bounds;
			float minV = (float)(result.y) / (float)bounds;
			float maxV = (float)((result.y) + info.height()) / (float)bounds;
			return new Glyth(texture, minU, minV, maxU, maxV, info.left(), info.right(), info.top(), info.bottom());
		}
		return null;
	}
	
	public void delete() {
		texture.remove();
	}
	
	public boolean colored() { return color; }
	
	public static class Slot {
		final int x;
		final int y;
		final int width;
		final int height;
		Slot[] children = null;
		boolean occupied;
		
		public Slot(int x, int y, int width, int height) {
			this.x = x;
			this.y = y;
			this.width = width;
			this.height = height;
		}
		
		public Slot insert(IGlythSheetInfo info) {
			if(children == null) {
				if(occupied) return null;
				int iw = info.width();
				int ih = info.height();
				if(iw > width || ih > height) return null;
				if(iw == width && ih == height) {
					occupied = true;
					return this;
				}
				int dw = width - iw;
				int dh = height - ih;
				children = new Slot[2];
				if(dw > dh) {
					int offset = MathUtils.ceil(iw / 50F);
					children[0] = new Slot(x, y, iw, height);
					children[1] = new Slot(x + iw + offset, y, dw - offset, height);
				}
				else {
					int offset = MathUtils.ceil(ih / 50F);
					children[0] = new Slot(x, y, width, ih);
					children[1] = new Slot(x, y + ih + offset, width, dh - offset);
				}
				return children[0].insert(info);
			}
			for(int i = 0,m=children.length;i<m;i++) {
				Slot slot = children[i].insert(info);
				if(slot != null) return slot;
			}
			return null;
		}
				
		@Override
		public String toString() {
			return "Slot[x="+x+", y="+y+", w="+width+", h="+height+"]";
		}
	}
}
