/**
 * @package  Liquid Meter
 * @author   Okler Themes
 * @license  Licensed under CC BY-NC-ND
 *
 * Copyright (c) 2014
 * Inspired on dribble shot:
 * http://dribbble.com/shots/1069484-Charging-Animation
 */

(function($) {

	'use strict';

	var defaultOptions = {
		shape: 'circle',
		color: '#82BF41',
		background: '#4C4C52',
		stroke: '#23282F',
		textColor: '#FFFFFF',
		fontFamily: 'Open Sans',
		fontSize: '24px',
		fontWeight: '600',
		liquidOpacity: 0.9,
		liquidPalette: ['#82BF41'],
		speed: 3000,
		animate: true
	};

	var LiquidMeter = function(el, options) {

		if (typeof Snap == 'undefined') {
			throw 'Snap.js not found.';
		}

		this.meter = $(el);
		this.options = options;
		this.options.text = this.meter.text();
		this.init();

	};

	LiquidMeter.prototype = {

		init: function(el, options) {

			this.wrapper = this.meter.parent('.liquid-meter');
			if ( !this.wrapper.get(0) ) {
				var wrapper = $('<div>').attr({ 'class': 'liquid-meter' });
				this.wrapper = this.meter.wrap( wrapper ).parent();
			}

			var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
			svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
			svg.setAttribute('viewBox', '0 0 220 220');
			this.wrapper.prepend(svg);

			this.svg = $(svg).attr({
				width: '100%',
				height: '100%'
			});

			this.frames = [
				[6, -6, -10, 6, 13, -10],		// 1
				[6, -5, -13, 4, 31, -5],		// 2
				[7, -5, -9, 0, 31, -5],			// 3
				[7, -5, -9, -6, 30, -6],		// 4
				[6, -10, -10, -10, 33, -4],		// 5
				[8, -4, -8, -13, 38, 7],		// 6
				[8, -8, -5, -15, 55, 11],		// 7
				[7, -19, -2, -16, 33, 16],		// 8
				[3, -23, -2, -16, 42, 21],		// 9
				[3, -22, 7, -18, 27, 21],		// 10
				[2, -26, 15, -18, 25, 20] ,		// 11
				[-3, -23, 18, -18, 26, 19] ,	// 12
				[-6, -40, 18, -18, 22, 22] ,	// 13
				[-15, -16, 23, -13, 37, 22],	// 14
				[-15, -16, 23, -13, 37, 22],	// 15
				[-17, -16, 23, -10, 33, 22],	// 16
				[-18, -16, 23, -10, 35, 21],	// 17
				[-20, -12, 23, -5, 34, 20],		// 18
				[-22, -13, 20, -1, 35, 22],		// 19
				[-22, -13, 20, 5, 32, 21],		// 20
				[-21, -16, 19, 8, 34, 15],		// 21
				[-23, -16, 16, 10, 36, 9],		// 22
				[-22, -23, 13, 10, 48, 0],		// 23
				[-23, -20, 13, 8, 45, -6],		// 24
				[-20, -22, 7, 10, 44, -6],		// 25
				[-20, -22, 5, 10, 42, -7],		// 26
				[-18, -20, 0, 10, 41, -8],		// 27
				[-15, -22, -7, 10, 41, -8],		// 28
				[-11, -33, -8, 10, 41, -8],		// 29
				[-7, -36, -6, 10, 15, -11],		// 30
				[0, -39, -8, 10, 15, -11],		// 31
				[0, -36, -8, 7, 15, -11],		// 32
			];

			this.paper = Snap(svg);

			this.draw();
			this.observe();
			if ( this.options.animate ) {
				this.animate();
			} else {
				this.drawFrame(1);
			}

			this.wrapper.addClass( 'liquid-meter-loaded' );

			return this;
		},

		draw: function() {
			this.value = parseInt(this.meter.val() || this.meter.attr('value'), 10);

			var paper = this.paper;

			this.recipient = paper.circle(110, 110, 95);

			var background = this.makeGradient(this.options.background);

			this.recipient.attr({
				fill: background,
				stroke: this.options.stroke,
				strokeWidth: 15
			});

			var mask = paper.circle(110, 110, 87);
			mask.attr({
				fill: '#FFFFFF'
			});

			this.liquid = paper.path();

			this.liquid.attr({
				id: 'front',
				fill: this.options.color,
				mask: mask,
				stroke: lighten(this.options.color, 20),
				strokeWidth: 1
			});

			this.label = paper.text('50%', '50%', [this.value, '%']);
			this.label.attr({
				fill: this.options.textColor,
				dy: '.4em',
				'font-family': this.options.fontFamily,
				'font-size': this.options.fontSize,
				'font-weight': this.options.fontWeight,
				'text-anchor': 'middle',
				stroke: this.options.textColor
			});
			this.label.selectAll('tspan:nth-child(2)').attr({
				'font-size': '24',
				stroke: 'none'
			});

			return this;

		},

		observe: function() {
			var _self = this;

			this.meter.on('change', function() {
				_self.set( $(this).val() );
			});
		},

		set: function( val ) {
			var value = parseInt( val, 10 );
			this.meter.val( value );
			this.value = value;
			this.label.node.textContent = value + '%';
		},

		liquidPalette: function( val ) {
			this.options.liquidPalette = val;
		},

		background: function( val ) {
			this.options.background = val;
		},

		stroke: function( val ) {
			this.options.stroke = val;
		},

		color: function( val ) {
			this.options.color = val;

			this.liquid.attr({
				fill: this.options.color,
				stroke: lighten(this.options.color, 20)
			});
		},

		makeGradient: function(color) {
			return this.paper.gradient(Snap.format('L(0, 0, 100, 100){color.top}-{color.bottom}', {
				color: {
					top: lighten(color, 6),
					bottom: color
				}
			}));
		},

		makePath: function(value, t1, x1, y1, t2, x2, y2) {
			var top = ((100 - value) * 1.76) + 22;
			return Snap.format('M0,{left} C{curve.x1},{curve.y1} {curve.x2},{curve.y2} 220,{right} L220,220 L0,220 z', {
				left: top - (t1 || 0),
				right: top - (t2 || 0),
				curve: {
					x1: x1 + top,
					y1: y1 + top,
					x2: x2 + top,
					y2: y2 + top
				}
			});
		},

		drawFrame: function(number) {
			var frame = this.frames[number - 1],
				t1 = frame[0],
				x1 = frame[1],
				y1 = frame[2],
				t2 = frame[3],
				x2 = frame[4],
				y2 = frame[5];
			this.liquid.attr({ d: this.makePath( this.value, t1, x1, y1, t2, x2, y2 )});
		},

		animate: function() {
			var requestAnimationFrame = window.requestAnimationFrame       ||
										window.webkitRequestAnimationFrame ||
										window.mozRequestAnimationFrame    ||
										window.oRequestAnimationFrame      ||
										window.msRequestAnimationFrame     ||
										function (callback) {
											setTimeout(callback, 0);
										},
				frames = this.frames.length,
				interval = this.options.speed / frames,
				currentFrame = 1,
				drawFrame = this.drawFrame.bind(this);

				function loop() {
					requestAnimationFrame(function() {
						draw();
					});
					setTimeout(loop, interval);
				}

				function draw() {
					drawFrame(currentFrame);
					currentFrame++;
					if (currentFrame >= frames) {
						currentFrame = 1;
					}
				}

				loop();

			return this;
		}

	};

	/**
	* liquidMeter jQuery plugin - public method
	* @param  - {Object} options - Passed options extending defaultOptions
	* @return - {Selector}       - Array of elements
	*/
	$.fn.liquidMeter = function(options, value) {
		var isSetter = (typeof options === 'string' && !!value);

		if ( !isSetter ) {
			options = $.extend({}, defaultOptions, options);
		}
		return this.map(function () {

			// preventing against multiple instantiations
			var instance = $(this).data('liquid-meter');
			if (!instance) {
				instance = new LiquidMeter(this, options);
				$(this).data('liquid-meter', instance);
			}
			if ( isSetter ) {
				instance[ options ]( value );
			}
			return instance;

		});
	};

	/**
	 * Util function for lightening the colour with a %
	 * @param - string - colour with leading #
	 * @param - number - percentage to lighten by
	 */
	function lighten(color, percent) {
		var n = parseInt(color.slice(1), 16),
		a = Math.round(2.55 * percent || 0),
		// Bitshift 16 bits to the left
		r = (n >> 16) + a,
		// Bitshift 8 bits to the left based on blue
		b = (n >> 8 & 0x00FF) + a,
		//
		g = (n & 0x0000FF) + a;
		// Calculate
		return '#' + (
				0x1000000 + (r < 255 ? r < 1 ? 0 : r : 255) * 0x10000 +
				(b < 255 ? b < 1 ? 0 : b : 255) * 0x100 + (g < 255 ? g < 1 ? 0 : g : 255)
			).toString(16).slice(1).toUpperCase();
	};

}(jQuery));