/* Widgets */

angular.widget('abn:cloud', function(compileEl){
	var compiler = this;
	
	var id = compileEl.attr('cloud-id'),
		scale = parseFloat(compileEl.attr('scale')) || 1,
		top = parseInt(compileEl.attr('top')) || 0,
		cloudEl = $('<span />', { id: id, 'class': 'cloud' });
		
	compileEl.html(cloudEl);
	
	compiler.directives(true);

	linkFn.$inject = ['World', '$defer'];
	function linkFn(World, $defer, linkEl){
		this.World = World;
		var scope = this,
			baseDuration = 20000,
			duration = baseDuration,
			baseOpacity = 0.8,
			parentEl = linkEl.parent();
		
		function animateCloud(){
			if (cloudEl.offset().left > parentEl.width())
				cloudEl.css('left', -cloudEl.width() * scale);

			cloudEl.animate({ left: '+=' + cloudEl.width() * scale }, {
				duration: duration,
				easing: 'linear',
				complete: function(){
					animateCloud();
				}
			});
		}

		// Defer to use parent element's rendered width
		$defer(function(){
			cloudEl.css({ scale: scale }).css({
				left: Math.random() * parentEl.width(),
				top: top
			});
			
			scope.$watch('World.getWindSpeed()', function(scope, speed){
				duration = baseDuration / speed;			
			});

			scope.$watch('World.getBaseCloudOpacity()', function(scope, opacity){
				cloudEl.css('opacity', baseOpacity * scale);
			});
			
			animateCloud();
		});
	};
	return linkFn;
});

angular.widget('abn:scenery', function(compileEl){
	var compiler = this;
	
	var id = compileEl.attr('scenery-id'),
		type = compileEl.attr('type'),
		sceneryEl = $('<span />', { id: id, 'class': 'scenery ' + type })	
			.append($('<span />', { 'class': 'element' }))
			.append($('<span />', { 'class': 'shadow' }));
	
	compileEl.html(sceneryEl);
	
	linkFn.$inject = ['World'];
	function linkFn(World, linkEl){
		this.World = World;
		
		function applyShadowTransform(scale, skew){
			linkEl.find('.shadow').css({
				skewX: skew + 'deg',
				scaleY: scale,
				origin: ['50%', 0]
			});
		}
		
		this.$watch('World.getShadowTransform()', function(scope, transform){
			applyShadowTransform(transform.scale, transform.skew);
		});
	
		this.$watch('World.getShadowOpacity()', function(scope, opacity){
			linkEl.find('.shadow').css('opacity', opacity);
		});
	};
	return linkFn;
});

angular.widget('abn:turbine', function(compileEl){
	var compiler = this;
		
	var turbineEl = $('<span id="turbine" />')
		.append('<span class="element"><span class="blades"></span></span>')
		.append('<span class="shadow"><span class="blades"></span></span>');

	compileEl.html(turbineEl);
	
	compiler.directives(true);

	linkFn.$inject = ['World', '$defer'];
	function linkFn(World, $defer, linkEl){
		this.World = World;
		var scope = this,
			baseDuration = 8000,
			duration = baseDuration,
			bladesEl = linkEl.find('.element .blades'),
			shadowBladesEl = linkEl.find('.shadow .blades');
			
		function rotateBlades(){
			function rotate(el, sign){
				el.animate({ rotate: sign + '=360deg' }, {
					duration: duration,
					easing: 'linear',
					complete: function(){
						rotate(el, sign);
					}
				});
			}
			
			rotate(bladesEl, '+');
			rotate(shadowBladesEl, '-');
		}
		
		function applyShadowTransform(scale, skew){
			linkEl.find('.shadow').css({
				skewX: skew + 'deg',
				scaleY: scale,
				origin: ['50%', 0]
			});
		}
		
		this.$watch('World.getShadowTransform()', function(scope, transform){
			applyShadowTransform(transform.scale, transform.skew);
		});

		this.$watch('World.getShadowOpacity()', function(scope, opacity){
			linkEl.find('.shadow').css('opacity', opacity);
		});
		
		this.$watch('World.getWindSpeed()', function(scope, speed){
			duration = baseDuration / speed;			
		});
		
		rotateBlades();
	};
	return linkFn;
});

angular.directive('abn:submit-ajax', function(expr, compileEl){
	var compiler = this;
	
	compiler.descend(true);
	compiler.directives(true);

	linkFn.$inject = ['FormErrors', 'Config'];
	function linkFn(FormErrors, Config, linkEl){
		var scope = this,
			formScope = linkEl.data('$form'),
			buttonEl = linkEl.find('button[type=submit]');
		
		buttonEl.attr('formnovalidate', true);
		
		linkEl.submit(function(e){
			if (formScope.$valid) {
				scope.$apply(expr);
				scope.$emit(Config.AJAX_REQUEST_EVENT);
				linkEl.filter(':visible').fadeTo(250, 0.5);
				buttonEl.attr('disabled', true);
			} else {
				FormErrors(formScope, linkEl[0]);
			}
			e.preventDefault();
		});
		
		function restore(){
			linkEl.filter(':visible').fadeTo(250, 1);
			buttonEl.attr('disabled', false);
		}
		
		this.$on(Config.AJAX_ERROR_EVENT, function(e, xhr){
			restore();
			buttonEl.closest('.dialog, .window, .panel')
				.effect('shake', { distance: 10, times: 2 }, 100);

			e.cancel(); // Stop propagation
		});
		
		this.$on(Config.AJAX_SUCCESS_EVENT, function(e, data){
			restore();
			e.cancel(); // Stop propagation
		});
	}
	return linkFn;
});

angular.directive('abn:show-ajax-error', function(expr, compileEl){
	var compiler = this;
	
	var statusCode = parseInt(expr);
	
	compiler.descend(true);
	compiler.directives(true);

	linkFn.$inject = ['Config'];
	function linkFn(Config, linkEl){
		var scope = this;
		
		linkEl.hide();
		
		this.$on(Config.AJAX_ERROR_EVENT, function(e, xhr){
			if (statusCode == xhr.status) linkEl.show();
		});
		
		this.$on(Config.AJAX_REQUEST_EVENT, function(e, data){
			linkEl.hide();
		});
	}
	return linkFn;
});

