/*
	Copyright (C) 2013 2014 Johan Mattsson

	This library is free software; you can redistribute it and/or modify 
	it under the terms of the GNU Lesser General Public License as 
	published by the Free Software Foundation; either version 3 of the 
	License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful, but 
	WITHOUT ANY WARRANTY; without even the implied warranty of 
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
	Lesser General Public License for more details.
*/

using B;

namespace BirdFont {

class SvgFont : GLib.Object {
	Font font;
	double units = 1;
	double font_advance = 0;
	
	public SvgFont (Font f) {
		this.font = f;
	}
	
	/** Load svg font from file. */
	public void load (string path) {
		string data;
		XmlParser xml_parser;
		try {
			FileUtils.get_contents (path, out data);
			xml_parser = new XmlParser (data);
			parse_svg_font (xml_parser.get_root_tag ());
		} catch (GLib.Error e) {
			warning (e.message);
		}
	}
	
	void parse_svg_font (Tag tag) {
		foreach (Tag t in tag) {
			if (t.get_name () == "defs") {
				parse_svg_font (t);
			}
			
			if (t.get_name () == "font") {
				parse_font_tag (t);
				parse_svg_font (t);
			}

			if (t.get_name () == "font-face") {
				parse_font_limits (t);
			}

			if (t.get_name () == "hkern") {
				parse_hkern (t);
			}
									
			if (t.get_name () == "glyph") {
				parse_glyph (t);
			}
		}		
	}

	void parse_hkern (Tag tag) {
		string left = "";
		string right = "";
		string left_name = "";
		string right_name = "";
		double kerning = 0;
		unichar l, r;
		StringBuilder sl, sr;
		GlyphRange grr, grl;
		KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();
		
		foreach (Attribute attr in tag.get_attributes ()) {
			// left
			if (attr.get_name () == "u1") {
				left = attr.get_content ();
			}	
					
			// right	
			if (attr.get_name () == "u2") {
				right = attr.get_content ();
			}

			if (attr.get_name () == "g1") {
				left_name = attr.get_content ();
			}
			
			if (attr.get_name () == "g2") {
				right_name = attr.get_content ();
			}
				
			// kerning
			if (attr.get_name () == "k") {
				kerning = double.parse (attr.get_content ()) * units;
			}
		}
				
		// FIXME: ranges and sequences for u1 & u2 + g1 & g2
		foreach (string lk in left.split (",")) {
			foreach (string rk in right.split (",")) {
				l = get_unichar (lk);
				r = get_unichar (rk);
				
				sl = new StringBuilder ();
				sl.append_unichar (l);
				
				sr = new StringBuilder ();
				sr.append_unichar (r);
				
				try {
					grl = new GlyphRange ();
					grl.parse_ranges (sl.str);
					
					grr = new GlyphRange ();
					grr.parse_ranges (sr.str);
				
					classes.set_kerning (grl, grr, -kerning);
				} catch (MarkupError e) {
					warning (e.message);
				}		
			}
		}
	}

	void parse_font_limits (Tag tag) {
		double top_limit = 0;
		double bottom_limit = 0;
		
		foreach (Attribute attr in tag.get_attributes ()) {
			if (attr.get_name () == "units-per-em") {
				units = 100.0 / double.parse (attr.get_content ());
			}	
		}

		foreach (Attribute attr in tag.get_attributes ()) {	
			if (attr.get_name () == "ascent") {
				top_limit = double.parse (attr.get_content ());
			}
			
			if (attr.get_name () == "descent") {
				bottom_limit = double.parse (attr.get_content ());
			}		
		}
		
		top_limit *= units;
		bottom_limit *= units;
		
		font.bottom_limit = bottom_limit;
		font.top_limit = top_limit;
	}
	
	void parse_font_tag (Tag tag) {
		foreach (Attribute attr in tag.get_attributes ()) {
			if (attr.get_name () == "horiz-adv-x") {
				font_advance = double.parse (attr.get_content ());
			}
						
			if (attr.get_name () == "id") {
				font.set_name (attr.get_content ());
			}
		}
	}
	
	/** Obtain unichar value from either a character, a hex representation of
	 *  a character or series of characters (f, &#x6d; or ffi).
	 */
	static unichar get_unichar (string val) {
		string v = val;
		unichar unicode_value;
		
		if (val == "&") {
			return '&';
		}
		
		// TODO: parse ligatures
		if (v.has_prefix ("&")) {
			// parse hex value
			v = v.substring (0, v.index_of (";"));
			v = v.replace ("&#x", "U+");
			v = v.replace (";", "");
			unicode_value = Font.to_unichar (v);
		} else {
			// obtain unicode value
			
			if (v.char_count () > 1) {
				warning ("font contains ligatures");
				return '\0';
			}
			
			unicode_value = v.get_char (0);
		}
		
		return unicode_value;	
	}
	
	bool is_ligature (string v) {
		if (v.has_prefix ("&")) {
			return false;
		}
		
		return v.char_count () > 1;
	}
	
	void parse_glyph (Tag tag) {
		unichar unicode_value = 0;
		string glyph_name = "";
		string svg = "";
		Glyph glyph;
		GlyphCollection glyph_collection;
		double advance = font_advance;
		string ligature = "";
		SvgParser parser = new SvgParser ();
		StringBuilder unicode_name; 

		parser.set_format (SvgFormat.INKSCAPE);

		foreach (Attribute attr in tag.get_attributes ()) {
			if (attr.get_name () == "unicode") {
				unicode_value = get_unichar (attr.get_content ());
				
				if (glyph_name == "") {
					glyph_name = attr.get_content ();
				}
				
				if (is_ligature (attr.get_content ())) {
					ligature = attr.get_content ();
				}
			}
			
			// svg data
			if (attr.get_name () == "d") {
				svg = attr.get_content ();
			}
			
			if (attr.get_name () == "glyph-name") {
				glyph_name = attr.get_content ();
			}

			if (attr.get_name () == "horiz-adv-x") {
				advance = double.parse (attr.get_content ());
			}
		}

		unicode_name = new StringBuilder ();
		unicode_name.append_unichar (unicode_value);

		glyph = new Glyph (unicode_name.str, unicode_value);
		parser.add_path_to_glyph (svg, glyph, true, units);			
		glyph.right_limit = glyph.left_limit + advance * units;
		
		// FIXME: add svg font ligatures
		/*
		if (ligature != "") {
			glyph.set_ligature_substitution (ligature);
		}
		*/
		
		glyph_collection = new GlyphCollection (unicode_value, glyph_name);
		glyph_collection.insert_glyph (glyph, true);
		
		font.add_glyph_collection (glyph_collection);
	}
}

}
