// BitmapFont for Unity by Lukasz Bruun (2011) - http://lukasz.dk
// Released into the Public Domain

using UnityEngine;
using System.Collections.Generic;
using System.Xml;
using System;

public class BitmapFont 
{	
	public class Sprite
	{
		public float x = 0.0f;	
		public float y = 0.0f;	
		public Rect texCoords;
		public char c;
		
		public Sprite(float x, float y, Rect texCoords, char c)
		{
			this.x = x;
			this.y = y;
			this.texCoords = texCoords;
			this.c = c;
		}
	}

	private class Glyph
	{
		public Rect texCoords;
		public float advance = 0.0f;
		public Dictionary<uint, int> kerning;
		
		public Glyph(float x, float y, float w, float h, float advance = 0.0f)
		{
			texCoords = new Rect(x, y, w, h);
			kerning = new Dictionary<uint, int>();
			this.advance = advance;
		}
		
		public int GetKerning(uint leftChar)
		{
			if(kerning.ContainsKey(leftChar))
			{
				return kerning[leftChar];
			}
			
			return 0;	
		}
	}
	
	private Dictionary<uint, Glyph> glyphs = new Dictionary<uint, Glyph>();	
	private float lineHeight = 0.0f;
	
	public BitmapFont(string input, float offsetX, float offsetY)
	{
		LoadXML(input, offsetX, offsetY);				
	}
	
	public List<Sprite> GetSprites(string text, float x, float y)
	{
		List<Sprite> sprites = new List<Sprite>();	
		char prevChar = '\0';
		
		float startx = x;
		
		foreach(char c in text)
		{	
			if(c == '\n')
			{
				x = startx;
				y += lineHeight;		
			}
			else
			{
				Glyph g = glyphs[(uint)c];
				
				Rect r = GetTexCoords(c);
				sprites.Add(new Sprite(x, y, r, c));
				
				int kerning = g.GetKerning(prevChar);
																
				x += g.advance + kerning;
				prevChar = c;
			}
		}	
		
		return sprites;
	}
	
	private Rect GetTexCoords(char c)
	{
		Glyph g = glyphs[(uint)c];
		
		if(g == null)
		{
			return new Rect();	
		}
		
		return g.texCoords;
	}
	
	private bool LoadXML(string input, float offsetX, float offsetY)
	{				
		XmlDocument xd = new XmlDocument();
		
		// Skip UTF byte order mark (BOM)
		input = input.Substring(input.IndexOf("<"));
			
		xd.LoadXml(input);
		
		foreach (XmlNode node in xd.ChildNodes)
		{			
			if(node.Name == "font")
			{
				foreach(XmlNode fontNode in node.ChildNodes)
				{
					if(fontNode.Name == "glyphs")
					{
						foreach(XmlNode gNode in fontNode.ChildNodes)
						{														
							uint glyphNum = UInt32.Parse(GetXMLAttribute(gNode, "code"), System.Globalization.NumberStyles.AllowHexSpecifier);
									
							string[] position = GetXMLAttribute(gNode, "origin").Split(','); 
							string[] size = GetXMLAttribute(gNode, "size").Split('x');
							
							float x = Convert.ToSingle(position[0]);
							float y = Convert.ToSingle(position[1]);
							float w = Convert.ToSingle(size[0]);
							float h = Convert.ToSingle(size[1]);
							float a = Convert.ToSingle(GetXMLAttribute(gNode, "aw"));
							
							lineHeight = Mathf.Max(lineHeight, h);
		
							glyphs[glyphNum] = new Glyph(x + offsetX, y + offsetY, w, h, a);
						}		
					}
					else if(fontNode.Name == "kernpairs")
					{
						foreach(XmlNode kNode in fontNode.ChildNodes)
						{							
							char left = GetXMLAttribute(kNode, "left")[0];
							char right = GetXMLAttribute(kNode, "right")[0];
							int adjust = Convert.ToInt32(GetXMLAttribute(kNode, "adjust"));
														
							glyphs[(uint)right].kerning[left] = adjust;
						}
					}						
				}
			}
		}
		
		return true;	
	}
	
	private static string GetXMLAttribute(XmlNode n, string strAttr)
	{
		XmlAttribute attr = n.Attributes.GetNamedItem(strAttr) as XmlAttribute;
	
		if (attr != null)
		{
			return attr.Value;
		}
		else
		{
			return "";	
		}
	}
}
