package speiger.src.coreengine.rendering.guiOld.renderer.provider;

import java.awt.image.BufferedImage;
import java.util.List;

import com.google.gson.JsonObject;

import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.collections.ints.utils.maps.Int2ObjectMaps;
import speiger.src.collections.objects.misc.pairs.ObjectObjectPair;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.AssetManager;
import speiger.src.coreengine.assets.base.IAsset;
import speiger.src.coreengine.math.vector.ints.Vec2i;
import speiger.src.coreengine.rendering.guiOld.helper.FontBuilder;
import speiger.src.coreengine.rendering.guiOld.helper.FontBuilder.WrittenChar;
import speiger.src.coreengine.rendering.guiOld.renderer.IFontRenderer.CharInstance;
import speiger.src.coreengine.rendering.texturesOld.base.ITexture;
import speiger.src.coreengine.rendering.texturesOld.base.TextureManager;
import speiger.src.coreengine.utils.helpers.JsonUtil;

public class BitmapFontProvider implements IFontProvider
{
	FontInfo info;
	ITexture texture;
	Int2ObjectMap<CharInstance>[] instances;
	float space;
	
	public BitmapFontProvider(FontInfo info, ITexture texture, Int2ObjectMap<CharInstance>[] instances)
	{
		this.info = info;
		this.texture = texture;
		this.instances = instances;
		if(instances[0].containsKey(' ')) space = instances[0].get(' ').getXAdvance();
		else if(instances[1].containsKey(' ')) space = instances[1].get(' ').getXAdvance();
	}
	
	@Override
	public void destroy()
	{
		if(texture != null)
		{
			texture.destroy();
			texture = null;
		}
	}
	
	@Override
	public ITexture getTexture()
	{
		return texture;
	}
	
	@Override
	public boolean isCharacterValid(int codepoint)
	{
		return instances[0].containsKey(codepoint) || instances[1].containsKey(codepoint);
	}
	
	@Override
	public CharInstance getCharacter(int codepoint, boolean bold)
	{
		Int2ObjectMap<CharInstance> map = instances[bold ? 1 : 0];
		return (map.isEmpty() ? instances[bold ? 0 : 1] : map).get(codepoint);
	}
	
	@Override
	public float height()
	{
		return info.fontHeight;
	}
	
	@Override
	public float baseLine()
	{
		return info.fontBase;
	}
	
	@Override
	public float getSpaceWidth()
	{
		return space;
	}
	
	@Override
	public float getTabWidth()
	{
		return space * info.tabs;
	}
	
	@SuppressWarnings("unchecked")
	public static BitmapFontProvider load(JsonObject object, float desiredSize, AssetManager manager)
	{
		FontInfo info = new FontInfo(object.getAsJsonObject("info"));
		float multiplier = info.setDesiredHeight(desiredSize);
		Int2ObjectMap<CharInstance>[] maps = new Int2ObjectMap[]{new Int2ObjectOpenHashMap<>(), new Int2ObjectOpenHashMap<>()};
		JsonUtil.iterate(object.get("chars"), T -> {
			CharInstance instance = info.create(T);
			instance.scale(multiplier);
			maps[instance.isBold() ? 1 : 0].put(instance.getCharacter(), instance);
		});
		if(maps[0].isEmpty()) maps[0] = Int2ObjectMaps.empty();
		if(maps[1].isEmpty()) maps[1] = Int2ObjectMaps.empty();
		return new BitmapFontProvider(info, ITexture.simple(AssetLocation.of(object.get("file").getAsString())), maps);
	}
	
	@SuppressWarnings("unchecked")
	public static IFontProvider create(JsonObject object, float desiredSize, AssetManager manager)
	{
		try(IAsset asset = TextureManager.INSTANCE.getManager().getAsset(AssetLocation.of(object.get("file").getAsString())))
		{
			JsonObject info = object.getAsJsonObject("info");
			int tabs = JsonUtil.getOrDefault(info, "tabs", 4);
			boolean literal = JsonUtil.getOrDefault(info, "literal", false);
			boolean plain = JsonUtil.getOrDefault(info, "plain", true);
			boolean bold = JsonUtil.getOrDefault(info, "bold", true);
			if(!plain && !bold) throw new IllegalStateException("You need a plain or bold font at the very least");
			
			int flags = (literal ? FontBuilder.LITERAL : 0) | (plain ? FontBuilder.PLAIN : 0) | (bold ? FontBuilder.BOLD : 0);
			ObjectObjectPair<ObjectObjectPair<Vec2i, BufferedImage>, List<WrittenChar>> written = FontBuilder.createBitmapFont(asset.stream(), info.get("charset").getAsString(), flags, info.get("size").getAsFloat());
			BufferedImage image = written.getKey().getValue();
			Vec2i size = written.getKey().getKey();
			FontInfo fontInfo = new FontInfo(image.getWidth(), image.getHeight(), size.y(), size.x(), tabs);
			float mulitplier = fontInfo.setDesiredHeight(desiredSize);
			Int2ObjectMap<CharInstance>[] maps = new Int2ObjectMap[]{new Int2ObjectOpenHashMap<CharInstance>(), new Int2ObjectOpenHashMap<CharInstance>()};
			for(WrittenChar entry : written.getValue())
			{
				CharInstance instance = entry.create(fontInfo.textureWidth, fontInfo.textureHeight);
				instance.scale(mulitplier);
				maps[instance.isBold() ? 1 : 0].put(instance.getCharacter(), instance);
			}
			if(maps[0].isEmpty()) maps[0] = Int2ObjectMaps.empty();
			if(maps[1].isEmpty()) maps[1] = Int2ObjectMaps.empty();
			return new BitmapFontProvider(fontInfo, ITexture.direct(image), maps);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return null;
	}
}