var Class = require('../../core/class');
var Path = require('../../core/path');
var SVGParser = require('./core');
var Font = require('../../shapes/font');

var parse = SVGParser.prototype.parse;

var matchFontURL = /url\(['"\s]*([^\)]*?)['"\s]*\)\s+format\(['"]?svg['"]?\)/i;

var trimFontFamily = /^['"\s]+|['"\s]+$|,.*$/g;

var fillFaceAttributes = function(element, face){
	var attributes = element.attributes;
	for (var i = 0, l = attributes.length; i < l; i++){
		var attribute = attributes[i];
		if (!(attribute.nodeName in face)){
			face[attribute.nodeName] = attribute.nodeValue;
		}
	}
};

SVGParser.implement({

	parse: function(element, styles){
		if (element.documentElement && element != this.fontDocument){
			this.fontDocument = element;
			this.findFonts(element);
		}
		return parse.apply(this, arguments);
	},
	
	findFonts: function(document){
		var fonts = this.fonts || (this.fonts = {});
		var root = document.documentElement, node = root;
		treewalker: while (node){
			if (node.nodeType == 1 && node.nodeName == 'font-face'){
				this.fontFace(node);
			}
			if (node.firstChild){
				node = node.firstChild;
			} else {
				while (!node.nextSibling){
					node = node.parentNode;
					if (!node || node == root) break treewalker;
				}
				node = node.nextSibling;
			}
		}

		if (this.findCSS){
			var rules = this.findCSS(document).cssRules;
			for (var i = 0, l = rules.length; i < l; i++){
				var rule = rules[i];
				if (rule.fontFace){
					var url = matchFontURL.exec(rule.style.src);
					if (url) this.fontURL(document, url[1], rule.style);
				}
			}
		}
	},
	
	fontFace: function(element){
		var face = {};
		fillFaceAttributes(element, face);
		if (element.parentNode.nodeName == 'font') this.font(element.parentNode, face);
		var node = element.firstChild;
		while (node){
			if (node.nodeName == 'font-face-src'){
				node = node.firstChild;
				continue;
			}
			if (node.nodeName == 'font-face-uri'){
				var url = node.getAttribute('xlink:href');
				this.fontURL(element.ownerDocument, url, face);
			}
			node = node.nextSibling;
		}
	},
	
	fontURL: function(document, url, face){
		if (!face['font-family']) return;
		var pendingFonts = this.pendingFonts || (this.pendingFonts = {});
		var family = face['font-family'].replace(trimFontFamily, '');
		var callbacks = pendingFonts[family] = [];
		this.findByURL(document, url, function(font){
			delete pendingFonts[family];
			if (font){
				var f = this.font(font, face);
				for (var i = 0, l = callbacks.length; i < l; i++)
					callbacks[i].call(this, f);
			}
		});
	},
	
	findFont: function(styles, callback){
		var family = styles['font-family'].replace(trimFontFamily, '');
		var callbacks = this.pendingFonts && this.pendingFonts[family];
		if (callbacks){
			callbacks.push(callback);
		} else if (this.fonts){
			callback.call(this, this.fonts[family]);
		} else {
			callback.call(this, null);
		}
	},
	
	font: function(element, face){
		var glyphs = {}, font = { face: face, glyphs: glyphs };

		var w = element.getAttribute('horiz-adv-x');
		if (w) font.w = parseInt(w, 10);
		
		var node = element.firstChild;
		while (node){
			switch(node.nodeName){
				case 'font-face':
					fillFaceAttributes(node, face);
					break;
				case 'missing-glyph':
				case 'glyph':
					var glyph = {},
						code = node.nodeName == 'missing-glyph' ? 'missing' : node.getAttribute('unicode'),
						w = node.getAttribute('horiz-adv-x'),
						d = node.getAttribute('d');
					if (!code) break;
					if (w) glyph.w = parseInt(w, 10);

					if (d){
						// Flip path
						var path = new FlippedVMLPathAtNormalPrecision(d);
						glyph.d = path.toVML().substr(1);
					}
					glyphs[code] = glyph;
					break;
				// TODO: Kerning
			}
			node = node.nextSibling;
		}
		
		var units = face['units-per-em'];
		
		if (isNaN(units)) face['units-per-em'] = units = 1000;

		if (face.ascent == null) face.ascent = face.descent == null ? 0.8 * units : units - face.descent;
		if (face.descent == null) face.descent = face.ascent - units;
		
		var family = face['font-family'];
		if (!family) return;
		face['font-family'] = family = family.replace(trimFontFamily, '');

		var fonts = this.fonts || (this.fonts = {});
		if (face.ascent) face.ascent = +face.ascent;
		if (face.descent) face.descent = +face.descent;
		fonts[family] = font;
		if (this.MODE.Font)
			this.MODE.Font.register(font);
		else
			Font.register(font);
		return font;
	}

});

var round = Math.round;

var FlippedVMLPathAtNormalPrecision = Class(Path, {

	initialize: function(path){
		this.reset();
		if (path){
			this.push(path);
		}
	},

	onReset: function(){
		this.path = [];
	},

	onMove: function(sx, sy, x, y){
		this.path.push('m', round(x), -round(y));
	},

	onLine: function(sx, sy, x, y){
		this.path.push('l', round(x), -round(y));
	},

	onBezierCurve: function(sx, sy, p1x, p1y, p2x, p2y, x, y){
		this.path.push('c',
			round(p1x), -round(p1y),
			round(p2x), -round(p2y),
			round(x), -round(y)
		);
	},

	onClose: function(){
		this.path.push('x');
	},

	toVML: function(){
		return this.path.join(' ');
	}

});
