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

import java.util.Map;
import java.util.function.Consumer;

import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.rendering.gui.font.glyth.Glyth;
import speiger.src.coreengine.rendering.gui.font.glyth.GlythData;
import speiger.src.coreengine.rendering.gui.font.glyth.UnbakedGlyth.GlythBaker;
import speiger.src.coreengine.rendering.tesselation.buffer.IVertexBuilder;
import speiger.src.coreengine.utils.helpers.TextUtil;
import speiger.src.coreengine.utils.misc.CodepointSequence;

public class Font {
	private static final int FORMAT_CODE_POINT = '§';
	FontSplitter splitter;
	Map<AssetLocation, FontGroup> fonts;
	GlythBaker baker;
	float oversample;
	GlythCache[] styledCache = new GlythCache[] {new GlythCache(this, 0), new GlythCache(this, 1), new GlythCache(this, 2), new GlythCache(this, 3)};
	
	protected Font(Map<AssetLocation, FontGroup> fonts, GlythBaker baker, float oversample, Consumer<Runnable> clearing) {
		this.fonts = fonts;
		this.baker = baker;
		this.oversample = oversample;
		clearing.accept(this::reset);
		splitter = new FontSplitter(this);
	}
	
	private void reset() {
		for(int i = 0;i<4;i++) {
			styledCache[i].reset();
		}
	}
	
	public GlythData data(FontStyle font, int codepoint) {
		return styledCache[font.style() & 0x3].data(font, codepoint);
	}
	
	public Glyth glyth(FontStyle font, int codepoint) {
		return styledCache[font.style() & 0x3].glyth(font, codepoint);
	}
	
	public float width(FontStyle font, int codepoint) {
		return styledCache[font.style() & 0x3].data(font, codepoint).advance();		
	}
	
	protected FontGroup font(AssetLocation font) {
		return fonts.get(font);
	}
	
	public float drawText(String text, float x, float y, int color, TexturedBuffer buffer, boolean end) {
		return drawText(TextStyle.DEFAULT, text, x, y, color, buffer, 1F, end);
	}
	
	public float drawText(TextStyle style, String text, float x, float y, int color, TexturedBuffer buffer, float scale, boolean end) {
		TextStyle currentStyle = style;
		int currentColor = currentStyle.hasColor() ? currentStyle.color() : color;
		float xStart = x;
		int previousCodepoint = -1;
		for(int i = 0,m=text.length();i<m;) {
			int codepoint = text.codePointAt(i);
			CodepointSequence result;
			if(codepoint == FORMAT_CODE_POINT && (result = findValue(CodepointSequence.of(text, i))) != null) {
				currentStyle = currentStyle.parseArguments(style, result.toString().split(","));
				currentColor = currentStyle.hasColor() ? currentStyle.color() : color;
				i += result.length()+3;
			}
			GlythData data = data(currentStyle.font(), codepoint);
			if(previousCodepoint != -1) {
				x -= data.kerning(previousCodepoint);
			}
			Glyth glyth = glyth(currentStyle.font(), codepoint);
			if(glyth.isValid()) {
				float minX = (glyth.left() + x) * scale;
				float minY = (glyth.top() + y) * scale;
				float maxX = (glyth.right() + x) * scale;
				float maxY = (glyth.bottom() + y) * scale;
				
				IVertexBuilder builder = buffer.builderForTexture(glyth.texture());
				builder.pos(minX, minY, 0F).tex(glyth.minU(), glyth.minV()).rgba(currentColor).endVertex();
				builder.pos(maxX, minY, 0F).tex(glyth.maxU(), glyth.minV()).rgba(currentColor).endVertex();
				builder.pos(maxX, maxY, 0F).tex(glyth.maxU(), glyth.maxV()).rgba(currentColor).endVertex();
				builder.pos(maxX, maxY, 0F).tex(glyth.maxU(), glyth.maxV()).rgba(currentColor).endVertex();
				builder.pos(minX, maxY, 0F).tex(glyth.minU(), glyth.maxV()).rgba(currentColor).endVertex();
				builder.pos(minX, minY, 0F).tex(glyth.minU(), glyth.minV()).rgba(currentColor).endVertex();
			}
			x += data.advance();
			i += Character.charCount(codepoint);
			previousCodepoint = codepoint;
		}
		if(end) buffer.end();
		return x - xStart;
	}
	
	static CodepointSequence findValue(CodepointSequence input) {
		if(input.length() < 3 || input.codePointAt(0) != FORMAT_CODE_POINT || input.codePointAt(1) != '<') return null;
		int end = TextUtil.findEnd(input, "§<", ">");
		if(end == -1) return null;
		return input.subSequence(2, end-1);
	}
	
	
	public static interface TexturedBuffer {
		public IVertexBuilder builderForTexture(int textureId);
		public void end();
	}
}
