// console.log('Loading Progress...');

Spontaneous.Progress = (function($, S) {
	var Progress = function(parent, size, options) {
		var defaults = {
			spinner_fg_color: '#000000', // colour
			spinner_bg_color: null, // null  or false for transparent
			progress_fg_color: '#000000', // colour
			progress_bg_color: false, // null  or false for transparent
			spinner_alpha: 1,
			progress_alpha: 1,
			period: 550, // (ms) time taken for spinner to complete one revolution
			segments: 12,  // number of bars in spinner
			segment_length: 0.47, // length of spinner bar as fraction of total radius
			segment_width: 0.6, // spinner bar thickness. 1 means inner edges form unbroken circle
			trail_length: 1.1, // trail length of 1 means shaded bars go all the way around
			rounded: true,
			shaded: false
		};
		if (typeof options == 'undefined') options = {};
		var settings = {};
		for (var k in defaults) {
			settings[k] = defaults[k];
		}

		for (k in options) {
			settings[k] = options[k];
		}
		return {
			container: parent,
			size: size,
			_canvas: false,
			defaults: defaults,
			options: settings,

			_color: {},

			// forces the addition of the canvas. normally this is lazily added
			// only when the progress indicator is actually used
			init: function() {
				var c = this.canvas();
			},

			context: function() {
				return this.canvas().getContext('2d');
			},

			colour: function(t, l, alpha) {
				var c = t + '_' + l + '_color';
				if (typeof alpha == 'undefined') alpha = this.options[t+'_alpha'];
				if (!this._color[c]) {
					this._color[c] = this._parse_color(this.options[c]);
				}
				return 'rgba('+this._color[c][0]+', '+this._color[c][1]+', '+this._color[c][2]+', ' +alpha+ ')';
			},

			canvas: function() {
				var c;
				if (this.container && !this._canvas) {

					c = document.createElement('canvas');
					c.width = this.size;
					c.height = this.size;
					// _log(typeof this.container)
					var e = null;
					if (typeof this.container == 'string') {
						this.container = document.getElementById(this.container);
					}
					this.container.appendChild(c);
					this._canvas = c;

				}
				return this._canvas;
			},

			_percent: false,

			// thanks to Stoyan Stefanov
			// for original pie drawing code:
			// http://www.phpied.com/canvas-pie/
			update: function(percent) {
				this._indeterminate = false;

				percent = Math.max(0.1, parseFloat(percent)); // always show something...
				this._percent = percent;
				var ctx = this.context();

				var radius = this.size / 2;
				var center = this.size / 2;
				ctx.clearRect(0,0,this.size,this.size);
				var colour;

				if (this.options.progress_bg_color) {
					colour = this.colour('progress', 'bg');
					ctx.beginPath();
					ctx.moveTo(center, center);
					ctx.arc(  // draw next arc
						center, // x
						center, // y
						radius,    // radius
						Math.PI * -0.5, // -0.5 sets set the start to be top
						Math.PI * 2,
						false // clockwise?
					);
					ctx.closePath();
					ctx.fillStyle = colour;    // color
					ctx.fill();
				}
				colour = this.colour('progress', 'fg');


				var a = (parseFloat(percent)/100.0);
				ctx.beginPath();
				ctx.moveTo(center, center); // center of the pie
				ctx.arc(  // draw next arc
					center, // x
					center, // y
					radius,    // radius
					Math.PI * -0.5, // -0.5 sets set the start to be top
					Math.PI * (- 0.5 + 2 * a),
					false // clockwise?
				);

				ctx.lineTo(center, center); // line back to the center
				ctx.closePath();
				ctx.fillStyle = colour;    // color
				ctx.fill();
				return this;
			},

			_redraw: function() {
				if (this._percent) this.update(this._percent);
			},

			_indeterminate: false,
			_indeterminate_interval: false,

			indeterminate: function() {
				if (this._indeterminate) return;
				this._indeterminate = true;
				var __spinner = this;
				var __spinning = function() {
					__spinner._update_indeterminate();
				};
				this._draw_indeterminate();

				this._indeterminate_interval = setInterval(__spinning, parseFloat(this.options.period) / this.options.segments);
			},
			spin: function() { this.indeterminate(); },
			start: function() { this.indeterminate(); },

			_spin_angle: 0,
			_spin_increment: function() { return 2 / this.options.segments; },

			_update_indeterminate: function() {
				if (!this._indeterminate) {
					this.pause();
					return;
				}
				this._spin_angle += this._spin_increment();
				this._draw_indeterminate();
			},

			_draw_indeterminate: function() {
				var ctx = this.context();

				var radius = this._radius();
				var center = this._center();
				var inc = this._spin_increment();

				ctx.clearRect(0,0,this.size,this.size);
				var r1 = radius - (radius * this.options.segment_length);

				var p = ((2.0 * Math.PI * r1) / this.options.segments) * this.options.segment_width;
				var r2 = radius;

				if (this.options.rounded) r2 -= p/2;

				for (var i = 0; i < this.options.segments; i++) {
					var offset = (inc * i);
					var a = Math.PI * (1 -this._spin_angle + offset);

					ctx.beginPath();

					if (this.options.rounded) {
						this._draw_rounded(ctx, center, r1, r2, a, p);
					} else {
						this._draw_square(ctx, center, r1, r2, a, p);
					}
					ctx.closePath();
					ctx.fillStyle = this._fill(ctx, i);
					ctx.fill();
				}
			},

			_draw_rounded: function(ctx, c, r1, r2, a, p) {
				var sin = Math.sin(a);
				var cos = Math.cos(a);
				var a1 = Math.PI - a;
				var a2 = 2 * Math.PI - a;
				ctx.arc(
					c + r1 * sin, // x
					c + r1 * cos, // y
					p/2,// radius
					a1,
					a2,
					false
				);

				ctx.arc(
					c + r2 * sin, // x
					c + r2 * cos, // y
					p/2, // radius
					a2,
					a1,
					false
				);

			},

			_draw_square: function(ctx, c, r1, r2, a, p) {
				var sin = Math.sin(a);
				var cos = Math.cos(a);
				var dx = (p/2.0) * cos;
				var dy = (p/2.0) * sin;

				var x0 = c + r1 * sin;
				var y0 = c + r1 * cos;

				var x1 = c + r1 * sin + dx;
				var y1 = c + r1 * cos - dy;

				var x2 = c + r2 * sin + dx;
				var y2 = c + r2 * cos - dy;

				var x3 = c + r2 * sin - dx;
				var y3 = c + r2 * cos + dy;

				var x4 = c + r1 * sin - dx;
				var y4 = c + r1 * cos + dy;

				ctx.moveTo(x1, y1);
				ctx.lineTo(x2, y2);
				ctx.lineTo(x3, y3);
				ctx.lineTo(x4, y4);

			},

			_fill: function(ctx, i) {
				if (this.options.shaded) {
					return this._shaded_fill(ctx, i);
				} else {
					return this._solid_fill(ctx, i);
				}
			},

			_solid_fill: function(ctx, i) {
				return this.colour('spinner','fg', this._alpha_for_segment(i));
			},

			_shaded_fill: function(ctx, i) {
				var c = this._center();
				var gradient = ctx.createRadialGradient(c, c, 0, c, c, this._radius());
				gradient.addColorStop(0.3, this.colour('spinner', 'fg', 0));
				gradient.addColorStop(1, this.colour('spinner', 'fg', this._alpha_for_segment(i)));
				return gradient;

			},

			_alpha_for_segment: function(n) {
				var x = (n / this.options.segments);
				// gotta be a smart way to figure out these constants
				var a = Math.max(0, this.options.spinner_alpha * ((1.0 / (5.0 * ((x / this.options.trail_length) + (1.0/5.8)))) - 0.168));
				return a;
			},

			_radius: function() {
				return this.size/2;
			},

			_center: function() {
				return this._radius();
			},

			_parse_color: function(css_colour) {
				if (css_colour.charAt(0) == '#') {
					css_colour = css_colour.substr(1, 6);
				}
				css_colour = css_colour.replace(/ /g,'');
				css_colour = css_colour.toLowerCase();
				var i, hex = [];
				if (css_colour.length == 3) {
					for (i = 0; i < 3; i++) {
						hex[hex.length] = css_colour.charAt(i) + css_colour.charAt(i);
					}
				} else {
					for (i = 0; i < 3; i++) {
						hex[hex.length] = css_colour.substr(i*2, 2);
					}
				}
				var rgb = [];
				for (i = 0; i < hex.length; i++) {
					rgb[i] = parseInt(hex[i], 16);
				}
				return rgb;
			},

			pause: function() {
				// pauses indeterminate spinner
				this._indeterminate = false;
				// this._spin_angle = 0;
				clearInterval(this._indeterminate_interval);
			},

			stop: function() {
				this.pause();
				this.clear();
			},

			clear: function() {
				this.context().clearRect(0,0,this.size,this.size);
			},

			_disappear_interval: null,

			disappear: function(duration) {
				if (typeof duration === 'undefined') duration = 1000;
				var disappearing = this;
				var orig_alpha = this.options.spinner_alpha;
				var finish_time = (new Date()).valueOf() + duration;
				var disappear = function() {
					if (disappearing.options.spinner_alpha <= 0) {
						clearInterval(disappearing._disappear_interval);
						disappearing.stop();
						disappearing.options.spinner_alpha = orig_alpha;
					} else {
						var now = (new Date()).valueOf(), remaining = (finish_time - now)/duration;
						disappearing.options.spinner_alpha = orig_alpha * remaining;
					}
				};

				this._disappear_interval = setInterval(disappear, 50);
			},

			set_options: function(o) {
				this.options = o;
				this._color = {};
				this._spin_angle = 0;
				if (this._indeterminate) {
					this.stop();
					this.indeterminate();
				} else {
					this._redraw();
				}
			}
		};
	};
	return Progress;
})(jQuery, Spontaneous);