/*!
 * Tinycon - A small library for manipulating the Favicon
 * Tom Moor, http://tommoor.com
 * Copyright (c) 2012 Tom Moor
 * @license MIT Licensed
 * @version 0.6.3
 *
 * https://github.com/tommoor/tinycon
 *
 * Copyright (c) 2013 Tom Moor
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

(function(){

	var Tinycon = {};
	var currentFavicon = null;
	var originalFavicon = null;
	var faviconImage = null;
	var canvas = null;
	var options = {};
	var r = window.devicePixelRatio || 1;
	var size = 16 * r;
	var defaults = {
		width: 7,
		height: 9,
		// XXX: Horde
		offset: 0,
		font: 10 * r + 'px arial',
		colour: '#ffffff',
		background: '#F03D25',
		fallback: true,
		crossOrigin: true,
		abbreviate: true
	};

	var ua = (function () {
		var agent = navigator.userAgent.toLowerCase();
		// New function has access to 'agent' via closure
		return function (browser) {
			return agent.indexOf(browser) !== -1;
		};
	}());

	var browser = {
		ie: ua('msie') || ua('trident') || ua('edge'),
		chrome: ua('chrome'),
		webkit: ua('chrome') || ua('safari'),
		safari: ua('safari') && !ua('chrome'),
		mozilla: ua('mozilla') && !ua('chrome') && !ua('safari')
	};

	// private methods
	var getFaviconTag = function(){

		var links = document.getElementsByTagName('link');

		for(var i=0, len=links.length; i < len; i++) {
			if ((links[i].getAttribute('rel') || '').match(/\bicon\b/)) {
				return links[i];
			}
		}

		return false;
	};

	var removeFaviconTag = function(){

		var links = document.getElementsByTagName('link');
		var head = document.getElementsByTagName('head')[0];

		for(var i=0, len=links.length; i < len; i++) {
			var exists = (typeof(links[i]) !== 'undefined');
			if (exists && (links[i].getAttribute('rel') || '').match(/\bicon\b/)) {
				head.removeChild(links[i]);
			}
		}
	};

	var getCurrentFavicon = function(){

		if (!originalFavicon || !currentFavicon) {
			var tag = getFaviconTag();
			originalFavicon = currentFavicon = tag ? tag.getAttribute('href') : '/favicon.ico';
		}

		return currentFavicon;
	};

	var getCanvas = function (){

		if (!canvas) {
			canvas = document.createElement("canvas");
			canvas.width = size;
			canvas.height = size;
		}

		return canvas;
	};

	var setFaviconTag = function(url){
		removeFaviconTag();

		var link = document.createElement('link');
		link.type = 'image/x-icon';
		link.rel = 'icon';
		link.href = url;
		document.getElementsByTagName('head')[0].appendChild(link);
	};

	var log = function(message){
		if (window.console) window.console.log(message);
	};

	var drawFavicon = function(label, colour) {

		// fallback to updating the browser title if unsupported
		if (!getCanvas().getContext || browser.ie || browser.safari || options.fallback === 'force') {
			return updateTitle(label);
		}

		var context = getCanvas().getContext("2d");
		var colour = colour || '#000000';
		var src = getCurrentFavicon();

		faviconImage = document.createElement('img');
		faviconImage.onload = function() {

			// clear canvas
			context.clearRect(0, 0, size, size);

			// draw the favicon
			context.drawImage(faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, size, size);

			// draw bubble over the top
			if ((label + '').length > 0) drawBubble(context, label, colour);

			// refresh tag in page
			refreshFavicon();
		};

		// allow cross origin resource requests if the image is not a data:uri
		// as detailed here: https://github.com/mrdoob/three.js/issues/1305
		if (!src.match(/^data/) && options.crossOrigin) {
			faviconImage.crossOrigin = 'anonymous';
		}

		faviconImage.src = src;
	};

	var updateTitle = function(label) {

		if (options.fallback) {
			// Grab the current title that we can prefix with the label
			var originalTitle = document.title;

			// Strip out the old label if there is one
			if (originalTitle[0] === '(') {
				originalTitle = originalTitle.slice(originalTitle.indexOf(' '));
			}

			if ((label + '').length > 0) {
				document.title = '(' + label + ') ' + originalTitle;
			} else {
				document.title = originalTitle;
			}
		}
	};

	var drawBubble = function(context, label, colour) {

		// automatic abbreviation for long (>2 digits) numbers
		if (typeof label == 'number' && label > 99 && options.abbreviate) {
			label = abbreviateNumber(label);
		}

		// bubble needs to be larger for double digits
		var len = (label + '').length-1;

		var width = options.width * r + (6 * r * len),
			height = options.height * r;

		var top = size - height,
            left = size - width - r,
            bottom = 16 * r,
            right = 16 * r,
            radius = 2 * r;

		// webkit seems to render fonts lighter than firefox
		context.font = (browser.webkit ? 'bold ' : '') + options.font;
		context.fillStyle = options.background;
		context.strokeStyle = options.background;
		context.lineWidth = r;

		// bubble
		context.beginPath();
        context.moveTo(left + radius, top);
		context.quadraticCurveTo(left, top, left, top + radius);
		context.lineTo(left, bottom - radius);
        context.quadraticCurveTo(left, bottom, left + radius, bottom);
        context.lineTo(right - radius, bottom);
        context.quadraticCurveTo(right, bottom, right, bottom - radius);
        context.lineTo(right, top + radius);
        context.quadraticCurveTo(right, top, right - radius, top);
        context.closePath();
        context.fill();

		// bottom shadow
		context.beginPath();
		context.strokeStyle = "rgba(0,0,0,0.3)";
		context.moveTo(left + radius / 2.0, bottom);
		context.lineTo(right - radius / 2.0, bottom);
		context.stroke();

		// label
		context.fillStyle = options.colour;
		context.textAlign = "right";
		context.textBaseline = "top";

		// unfortunately webkit/mozilla are a pixel different in text positioning
		// XXX: Horde
		// context.fillText(label, r === 2 ? 29 : 15, browser.mozilla ? 7*r : 6*r);
		context.fillText(label, r === 2 ? 29 : 15, (browser.mozilla ? 7*r : 6*r) + options.offset);
	};

	var refreshFavicon = function(){
		// check support
		if (!getCanvas().getContext) return;

		setFaviconTag(getCanvas().toDataURL());
	};

	var abbreviateNumber = function(label) {
		var metricPrefixes = [
			['G', 1000000000],
			['M',    1000000],
			['k',       1000]
		];

		for(var i = 0; i < metricPrefixes.length; ++i) {
			if (label >= metricPrefixes[i][1]) {
				label = round(label / metricPrefixes[i][1]) + metricPrefixes[i][0];
				break;
			}
		}

		return label;
	};

	var round = function (value, precision) {
		var number = new Number(value);
		return number.toFixed(precision);
	};

	// public methods
	Tinycon.setOptions = function(custom){
		options = {};

		for(var key in defaults){
			options[key] = custom.hasOwnProperty(key) ? custom[key] : defaults[key];
		}
		return this;
	};

	Tinycon.setImage = function(url){
		currentFavicon = url;
		refreshFavicon();
		return this;
	};

	Tinycon.setBubble = function(label, colour) {
		label = label || '';
		drawFavicon(label, colour);
		return this;
	};

	Tinycon.reset = function(){
		setFaviconTag(originalFavicon);
	};

	Tinycon.setOptions(defaults);
	window.Tinycon = Tinycon;

	if(typeof define === 'function' && define.amd) {
		define(Tinycon);
	}

})();
