function PieMenu(options) {
	this.initialize(options);
}

PieMenu.prototype = {
	initialize: function(options) {
		this.options = options;
		this._createSvg();
		// add filter
		// this._addFilter();	
		this._buildMenu();
		this._addMenuOutline();	
		this._addEventListeners();
	},
	
	_createSvg: function() {
		var tempNode = document.createElement("div");
		tempNode.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="livemargins-pie-menu"><g class="livemargins-piemenu-group"/></svg>'
		this.svg = tempNode.childNodes[0];
		
		if (this.options.placeIn) {
			this.options.placeIn.appendChild(this.svg);
		} else {
			document.body.appendChild(this.svg);
		}
	},
	
	_buildMenu: function() {
		// 设置中心点
		if (typeof this.options.top != "undefined" && typeof this.options.left != "undefined") {
			this.svg.setAttributeNS(null, "style", "top:" + (this.options.top - this.options.radius) + "px;left:" 
					+ (this.options.left - this.options.radius) + "px;");
		}
		
		this.svg.setAttributeNS(null, "height", this.options.radius * 2);
		this.svg.setAttributeNS(null, "width", this.options.radius * 2);
		
		this.pieChart = new PieChart({
			size:  this.options.size,
			radius: this.options.radius,
			rotate: !this.options.rotate ? 0 : this.options.rotate,
			radian: !this.options.radian ? 360 : this.options.radian > 360 ? 360 : this.options.radian,
			innerring: !this.options.innerring ? 2 : this.options.innerring,
			level: 1,
			items: this.options.items,
			placeIn: this.svg.querySelectorAll("g.livemargins-piemenu-group")[0]
		});
	},
	
	_addEventListeners: function() {
		var eventsNames = ["click", "mouseover", "mouseout", "mousedown", "mouseup"];
		var self = this;
		eventsNames.forEach(function(eventName) {
			if (self.options["on" + eventName]) {
				self.svg.addEventListener(eventName, self.options["on" + eventName], false);
			}
		});
        
        // Disable context menu on the piemenu.
        this.svg.addEventListener("contextmenu", function(event) {
            event.stopPropagation();
            event.preventDefault();
        }, true);
	},
	
	_removeEventListeners: function() {
        var eventsNames = ["click", "mouseover", "mouseout", "mousedown", "mouseup"];
        var self = this;
        eventsNames.forEach(function(eventName) {
            if (self.options["on" + eventName]) {
                self.svg.removeEventListener(eventName, self.options["on" + eventName], false);
            }
        });
	},
	
	_addFilter: function() {
		// random name
		this.options.filter = "livemargins-pie-filter";
		var filter = [];
		filter.push('<svg xmlns="http://www.w3.org/2000/svg">');
		filter.push('<filter id="' + this.options.filter + '" filterUnits="userSpaceOnUse" x="0" y="0" width="' + (this.options.radius * 2) + '" height="' + (this.options.radius * 2) + '">');
		filter.push(	'<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>');
		filter.push(	'<feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>');
		filter.push(	'<feSpecularLighting in="blur" surfaceScale="5" specularConstant=".75" specularExponent="20" lighting-color="#bbbbbb" result="specOut">');
		filter.push(		'<fePointLight x="-5000" y="-10000" z="20000"/>');
		filter.push(	'</feSpecularLighting>');
		filter.push(	'<feComposite in="specOut" in2="SourceAlpha" operator="in" result="specOut"/>');
		filter.push(	'<feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="litPaint"/>');
		filter.push(	'<feMerge>');
		filter.push(		'<feMergeNode in="offsetBlur"/>');
		filter.push(		'<feMergeNode in="litPaint"/>');
		filter.push(	'</feMerge>');
		filter.push('</filter>');
		filter.push('</svg>');
		
		var tempNode = document.createElement("div");
		tempNode.innerHTML = filter.join("");
		this.svg.appendChild(tempNode.childNodes[0].childNodes[0]);
	},
	
	_addMenuOutline: function() {
		var tempNode = document.createElement("div");
		var html = [];
		html.push('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">');
		html.push(	'<circle class="livemargins-pie-menu-outline" r="' + this.options.radius +'" cx="' + this.options.radius +'" cy="' + this.options.radius +'" stroke-width="1" stroke="#979797" fill="none"/>');
		html.push('</svg>');
		tempNode.innerHTML = html.join("");
		this.svg.appendChild(tempNode.childNodes[0].childNodes[0]);
	},
	
	hide: function() {
		window.clearTimeout(this._showTimeout);
		this._removeClass(this.svg, "livemargins-pie-menu-show");
	},
	
	show: function() {
		window.clearTimeout(this._showTimeout);
		var self = this;
		this._showTimeout = window.setTimeout(function() {
			if (self._hasClass(self.svg, "livemargins-pie-menu-show")) {
				return;
			}
			self._addClass(self.svg, "livemargins-pie-menu-show");
		}, 100);
	},
	
	moveTo: function(l, t) {
		this.svg.style.left = l - this.options.radius + "px";
		this.svg.style.top = t - this.options.radius + "px";
	},
	
	_removeClass: function(elem, className) {
        elem.classList.remove(className);
    },
    
    _addClass: function(elem, className) {
        elem.classList.add(className);
    },
    
    _hasClass: function(elem, className) {
        return elem.classList.contains(className);
    },
	
	destroy: function() {
	    if (this.pieChart) {
	       this.pieChart.destroy();
	    }
	    this._removeEventListeners();
	    if (this.svg.parentNode) {
	        this.svg.parentNode.removeChild(this.svg);
	    }
	}
};

function PieChart(options) {
	this.initialize(options);
}

PieChart.prototype = {
	initialize: function(options) {
		if (!options.placeIn) {
			throw "placeIn is essential.";
		}
		
		this.options = options;
		// size of pie chart, default is 6
		this.options.size = !this.options.size ? 6 : this.options.size
		this.options.rotate = !this.options.rotate ? 0 : this.options.rotate;
		// Pie弧度
		this.options.radian = !this.options.radian ? 360 : this.options.radian;
		this.options.radian = this.options.radian > 360 ? 360 : this.options.radian;
		// 内环弧度
		this.options.innerring = !this.options.innerring ? 2 : this.options.innerring;
		// the level of piemenu
		this.options.level = !this.options.level ? 1 : this.options.level;
		this._createGroup();
		this.buildPie();
		this._toggle();
	},
	
	buildPie: function() {
		var radius = this.options.radius;
		// create paths and images 
		this._createElements();		
		this._drawPie();
		this._drawImage();
		this._addPieSketch();
		this._addEventListeners();
	},
	
	dRotate: function(r) {
		this.rotate(this.options.rotate + (!r ? 10 : -10));
	},
	
	rotate: function(deg) {
		this.options.rotate = deg;
		this._drawPie();
		this._drawImage();
	},
	
	setRadius: function(radius) {
		this.options.radius = radius;
		this._drawPie();
		this._drawImage();
	},
	
	setRadian: function(r) {
		this.options.radian = r;
		this._drawPie();
		this._drawImage();
	},
	
	setInnerRing: function(r) {
		this.options.innerring = r;
		this._drawPie();
		this._drawImage();
	},
	
	_addEventListeners: function() {
		var eventsNames = ["click", "mouseover", "mouseout", "mousedown", "mouseup"];
		
		var gs = this._getGroups();
		
		var max = this.options.items.length;
		var self = this;
		
		for (var i = 0; i < max; i++) {
			var g = gs[i];
			eventsNames.forEach(function(eventName) {
				g.addEventListener("mouseover", function(event) {
					var idx = this;
					// self.svg.querySelectorAll("g.livemargins-pie-sector")[idx].appendChild(self._getPieSketch());
					self._getPieSketch().setAttributeNS(null, "d", self._calculateD(idx));
				}.bind(i), false);
				g.addEventListener("mouseout", function(event) {
					var idx = this;
					self._getPieSketch().setAttributeNS(null, "d", "");
				}.bind(i), false);
				if (self.options.items[i]["on" + eventName]) {
					g.addEventListener(eventName, function(event) {
						var idx = this;
						self.options.items[idx]["on" + eventName](event);
					}.bind(i), false);
				}
			});
			
			// build next level menu.
			this._buildNextLevelChart(i);
		}
		
		eventsNames.forEach(function(eventName) {
			if (self.options["on" + eventName]) {
				self.svg.addEventListener(eventName, self.options["on" + eventName], false);
			}
		});
	},
	
	_removeEventListeners: function() {
        var eventsNames = ["click", "mouseover", "mouseout", "mousedown", "mouseup"];
        var self = this;
	    eventsNames.forEach(function(eventName) {
	        if (self.options["on" + eventName]) {
	            self.svg.removeEventListener(eventName, self.options["on" + eventName], false);
	        }
	    });
	},
	
	_buildNextLevelChart: function(idx) {
		if (this._getItem(idx).items && this._getItem(idx).items.length > 0) {
			var self = this;
			this._getGroups()[idx].addEventListener("click", function(event) {
				var index = this;
				var g = self._getGroups()[index];
				if (null == g._pieChart) {
					g._pieChart = new PieChart({
						size: self.options.size - 1,
						radius: self.options.radius,
						radian: self.options.radian - self._getDDeg(),
						innerring: self.options.innerring,
						level: self.options.level + 1,
						rotate: self.options.rotate + self._getDDeg(),						
						placeIn: self.options.placeIn,
						items: self._getItem(index).items
					});
				} else {
					g._pieChart._toggle();
				}
			}.bind(idx), false);
		}
	},
	
	_toggle: function() {
		window.clearTimeout(this._toggleTimeout);
		var self = this;
		this._toggleTimeout = window.setTimeout(function() {
			if (self._hasClass(self.g, "livemargins-piechart-show")) {
				self._removeClass(self.g, "livemargins-piechart-show");
			} else {
				self._addClass(self.g, "livemargins-piechart-show");
			}
		}, 0);
	},
	
	_createElements: function() {
		for (var i = 0; i < this.options.items.length; i++) {
			var tempNode = document.createElement("div");
			var html = [];
			html.push('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">');
			html.push(	'<g class="livemargins-pie-sector" index="' + i + '">');
			html.push(		'<path class="livemargins-pie-sector-bg" index="' + i + '" />');
			html.push(		'<image height="16" width="16" title="' + this.options.items[i].tooltip + '" xlink:href="' + this.options.items[i].imgsrc + '" index="' + i + '" />');
			html.push(	'</g>');
			html.push('</svg>');
			tempNode.innerHTML = html.join(""); 
			this.g.appendChild(tempNode.childNodes[0].childNodes[0]);
		}
		
		// fill with empty pie.
		if (this.options.items.length < this.options.size) {
			var max = this.options.size - this.options.items.length;
			for (var i = 0; i < max; i++) {
				var html = [];
				html.push('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">');
				html.push(	'<g class="livemargins-pie-sector livemargins-pie-sector-empty">');
				html.push(		'<path class="livemargins-pie-sector-bg" />');
				html.push(	'</g>');
				html.push('</svg>');
				tempNode.innerHTML = html.join(""); 
				this.g.appendChild(tempNode.childNodes[0].childNodes[0]);
			}
		}
	},
	
	_createGroup: function() {
		var tempNode = document.createElement("div");
		tempNode.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g class="livemargins-piemenu-level-' + this.options.level + '" /></svg>';
		this.g = tempNode.childNodes[0].childNodes[0];
		this.options.placeIn.appendChild(this.g);
	},
	
	_addPieSketch: function() {
		var tempNode = document.createElement("div");
		var html = [];
		html.push('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">');
		html.push(	'<path class="livemargins-pie-sector-sketch" />');
		html.push('</svg>');
		tempNode.innerHTML = html.join("");
		this.g.appendChild(tempNode.childNodes[0].childNodes[0]);
	},
	
	_getPieSketch: function() {
		return this.g.querySelectorAll("path.livemargins-pie-sector-sketch")[0];
	},
	
	// 计算图标的位置
	_drawImage: function() {
		var images = this.g.getElementsByTagName("image");
		var dDeg = this._getDDeg();
		for (var i = 0; i < images.length; i++) {
			var image = images[i];
			var deg = this.options.rotate + dDeg * (i + 1) - dDeg / 2;
			var p = this._getPoint(this.options.radius, this.options.radius, this.options.radius - 15, deg);
			image.setAttributeNS(null, "x", p[0] - 16 / 2);		// minus the half of height
			image.setAttributeNS(null, "y", p[1] - 16 / 2);
		}
	},
	
	_drawPie: function() {		
		var pies = this._getPies();
		for (var i = 0; i < pies.length; i++) {
			var pie = pies[i];
			pie.setAttributeNS(null, "d", this._calculateD(i));
		}			
	},
	
	_getItem: function(idx) {
		return this.options.items[idx];
	},
	
	_getPies: function() {
		return this.g.querySelectorAll("path.livemargins-pie-sector-bg");
	},
	
	_getGroups: function() {
		return this.g.querySelectorAll("g.livemargins-pie-sector");
	},
	
	_getDDeg: function() {
		return this.options.radian / Math.max(this.options.items.length, this.options.size);
	},
	
	_getPiePoint: function(deg) {
		var r = (this.options.radius - this.options.innerring) / 2 + this.options.innerring;
		return this._getPoint(this.options.radius, this.options.radius, r, deg);
	},
	
	_getPoint: function(x, y, r, deg) {
		var radian = deg * Math.PI / 180;
		return [x + r * Math.cos(radian), y - r * Math.sin(radian)];
	},
		
	// 计算路径, 由内外两个环构成
	_calculateD: function(idx, end) {
		var d = [];
		var dDeg = this._getDDeg();
		var r = this.options.radius;
		var p1 = this._getPoint(r, r, r, this.options.rotate + dDeg * idx);
		d.push("M" + p1[0] + "," + p1[1]);
		var p2 = this._getPoint(r, r, r, this.options.rotate + dDeg * (idx + 1));
		d.push("A" + [r, r, 0, 0, 0, p2[0], p2[1]].join(","));
		var p3 = this._getPoint(r, r, this.options.innerring, this.options.rotate + dDeg * (idx + 1));
		d.push("L" + p3[0] + "," + p3[1]);
		var p4 = this._getPoint(r, r, this.options.innerring, this.options.rotate + dDeg * idx);
		d.push("A" + [this.options.innerring, this.options.innerring, 0, 0, 1, p4[0], p4[1]].join(","));
		if (false != end) {
			d.push("Z");
		}
		return d.join(" ");
	},
	
	_removeClass: function(elem, className) {
	    elem.classList.remove(className);
	},
	
	_addClass: function(elem, className) {
	    elem.classList.add(className);
	},
	
	_hasClass: function(elem, className) {
	    return elem.classList.contains(className);
	},
	
	destroy: function() {
	    if (this.g.parentNode) {
	        this.g.parentNode.removeChild(this.g);
	    }
	    this._removeEventListeners();
	}
};