/*
 * global variables
 */

// cloud.call console strings
var call_line1 = "import cloud";
var call_line2 = "from myfuncs import f1,f2,f3";
var call_line3 = "id1 = cloud.call(f1)";
var call_line4 = "id2 = cloud.call(f2, x)";
var call_line5 = "id3 = cloud.call(f3, y, z)";

// cloud.status console strings
var status_line1 = "from myfuncs import fib";
var status_line2 = "id = cloud.call(fib, 8)";
var status_line3 = "print id";
var status_line4 = "cloud.status(id)";
var status_line5 = "cloud.result(id)";

// cloud.map console strings
var map_line1 = "cloud.map(f, [x, y, z])";
var map_line2 = "[1, 2, 3]  <-- job ids";
var map_line3 = "cloud.status([1, 2, 3])";
var map_line4 = "['done','done','processing']";

// chapters descriptions
var call_desc = "Use cloud.call to execute your code on the cloud";
var status_desc = "Follow your jobs using cloud.status/cloud.result";
var map_desc = "Execute a function on multiple arguments using cloud.map";

var prompt_line = 0;
var NUM_LINES = 4;

var last_timeout = null;


/*
 * initializing functions
 */

function init_and_jump(callback_fn) {
	// set intro div's visibility to hidden
	$('#tour_intro').css('visibility', 'hidden');
	$('#tour_panels').css('visibility', 'visible');

	// reset the console
	reset_console();

	// remove the back and next button
	hide_controls();

	// stop all the servers
	stop_all_servers();
	logo_off();

	// call the first function of the chapter animation
	callback_fn();
}

function reset_console() {
	clearTimeout(last_timeout);
	$('#console > span').empty();
	prompt_line = 0;
	advance_line(true);
}


/*
 * animation functions for cloud.call
 */

function call_step0() {
	describe(call_desc);
	set_active_chapter('cloud_call');
	activate_cont(null, call_step1);
	legend('In order to use PiCloud, first import our library.<br><br>Click next when ready...');
}

function call_step1() {
	hide_controls();
	type_line(call_line1, 50, true, function() { 
		activate_cont(function() { init_and_jump(call_step0); }, call_step2);
		legend("Next, define or import the functions you'd like to run on the cloud.");
	});
}

function call_step2(){
	hide_controls();
	type_line(call_line2, 50, true, function() {
		activate_cont(function() { init_and_jump(call_back1); }, call_step3);
		legend("cloud.call runs your function on PiCoud's high-performance, auto-scaling cloud.");
	});
}

function call_back1() {
	type_line(call_line1, 0, true, function() {
		activate_cont(function() { init_and_jump(call_step0); }, call_step2);
		legend("Next, define or import the functions you'd like to run on the cloud.");
	});
}

function call_step3() {
	hide_controls();
	type_line(call_line3, 50, true, function(){
		send_job(1, 'f1()', function() {
			activate_cont(function() { init_and_jump(call_back2); }, call_step4);
			legend('As you submit additional jobs, PiCloud automatically distributes them across the cloud so they can be run in parallel.');
		});
	});
}

function call_back2() {
	type_line(call_line1, 0, true);
	type_line(call_line2, 0, true, function() {
		activate_cont(function() { init_and_jump(call_back1); }, call_step3);
		legend("cloud.call runs your function on PiCoud's high-performance, auto-scaling cloud.");
	});
}

function call_step4() {
	hide_controls();
	type_line(call_line4, 50, true, function() { send_job(2, 'f2(x)', call_step5); });
}

function call_step5() {
	type_line(call_line5, 50, true, function() {
		send_job(3, 'f3(y, z)', function() {
			activate_cont(function() { init_and_jump(call_back3); }, function () { init_and_jump(status_step0); });
			legend('Click next to find out how you can retrieve the return value of submitted jobs.');
		});
	});
}

function call_back3() {
	type_line(call_line1, 0, true);
	type_line(call_line2, 0, true);
	type_line(call_line3, 0, true, function(){
		server_run(1, 'f1()');
		activate_cont(function() { init_and_jump(call_back2); }, call_step4);
		legend('As you submit additional jobs, PiCloud automatically distributes them across the cloud so they can be run in parallel.');
	});
}


/*
 * animation functions for cloud.status/cloud.result
 */

function status_step0() {
	describe(status_desc);
	set_active_chapter('cloud_result');
	activate_cont(null, status_step1);
	legend('Each job submitted to PiCloud is assigned a job id, which is returned to the user.');
}

function status_step1() {
	hide_controls();
	type_line(status_line1, 50, true, status_step2);
}

function status_step2() {
	type_line(status_line2, 50, true, function() {
		send_job(1, 'fib(8)', function() {
			activate_cont(function() { init_and_jump(status_step0); }, status_step3)
			legend('The job id is simply an integer that uniquely identifies a submitted job.');
		});
	});
}

function status_step3() {
	hide_controls();
	type_line(status_line3, 50, false, function() {
		type_line('7 <-- job id', 0, true);
		activate_cont(function() { init_and_jump(status_back1); }, status_step4);
		legend('The job id can be used to check the status of the job via cloud.status.');
	});
}

function status_back1() {
	type_line(status_line1, 0, true);
	type_line(status_line2, 0, true, function() {
		server_run(1, 'fib(8)')
		activate_cont(function() { init_and_jump(status_step0); }, status_step3)
		legend('The job id is simply an integer that uniquely identifies a submitted job.');
	});
}

function status_step4() {
	hide_controls();
	type_line(status_line4, 50, false, status_step5);
}

function status_step5() {
	query_job(function() {
		print_status(1);
		activate_cont(function() { init_and_jump(status_back2); }, status_step6);
		legend('You can also use the job id to retrieve the return value of your function using cloud.result.');
	});
}

function status_back2() {
	type_line(status_line2, 0, true);
	type_line(status_line3, 0, false, function() {
		server_run(1, 'fib(8)');
		type_line('7 <-- job id', 0, true);
		activate_cont(function() { init_and_jump(status_back1); }, status_step4);
		legend('The job id can be used to check the status of the job via cloud.status.');
	});
}

function status_step6() {
	hide_controls();
	type_line(status_line5, 50, false, status_step7);
}

function status_step7() {
	var arrow = $('#arrow');
	arrow.show('lat_blind', { direction: 'right' }, 400, logo_on).hide('lat_blind', { direction: 'right' }, 400, function() {
		activate_cont(function() { init_and_jump(status_back3); }, status_step8);
		legend('If the job is still running, as in this case, cloud.result will block and wait until the job is done.');
	});
}

function status_back3() {
	type_line(status_line3, 0, false)
	type_line('7 <-- job id', 0, true);
	server_run(1, 'fib(8)');
	type_line(status_line4, 0, false);
	print_status(1);
	activate_cont(function() { init_and_jump(status_back2); }, status_step6);
	legend('You can also use the job id to retrieve the return value of your function using cloud.result.');
}

function status_step8() {
	hide_controls();
	server_stop(1);

	var arrow_back = $('#arrow_back');
	arrow_back.show('lat_blind', { direction: 'left' }, 400, logo_off).hide('lat_blind', { direction: 'left' }, 400);
	type_line('21 <-- return value of fib(8)', 0, true, function() {
		activate_cont(function() { init_and_jump(status_back4); }, status_step9);
		legend("When a job has finished executing, its status changes from 'processing' to 'done'.");
	});
}

function status_back4() {
	type_line('7 <-- job id', 0, true);
	server_run(1, 'fib(8)');
	type_line(status_line4, 0, false);
	print_status(1);
	type_line(status_line5, 0, false, logo_on);
	activate_cont(function() { init_and_jump(status_back3); }, status_step8);
	legend('If the job is still running, as in this case, cloud.result will block and wait until the job is done.');
}

function status_step9() {
	hide_controls();
	type_line(status_line4, 50, false, status_step10);
}

function status_step10() {
	query_job(function() {
		print_status(1);
		activate_cont(function() { init_and_jump(status_back5); }, function() { init_and_jump(map_step0); });
		legend('Click next to learn about cloud.map.');
	});
}

function status_back5() {
	server_run(1, 'fib(8)');
	type_line(status_line4, 0, false);
	print_status(1);
	server_stop(1);
	type_line(status_line5, 0, false);
	type_line(status_line4, 0, false);
	type_line('21 <-- return value of fib(8)', 0, true, function() {
		activate_cont(function() { init_and_jump(status_back4); }, status_step9);
		legend("When a job has finished executing, its status changes from 'processing' to 'done'.");
	});
}


/*
 * animation functions for cloud.map
 */

function map_step0() {
	describe(map_desc);
	set_active_chapter('cloud_map');
	legend('You can easily map any of your functions over multiple arguments using cloud.map.');
	activate_cont(null, map_step1);
}

function map_step1() {
	hide_controls();
	type_line(map_line1, 50, false, function() {
		var arrow = $('#arrow');
		arrow.show('lat_blind', { direction: 'right' }, 500, function() {
			logo_on();
			type_line(map_line2, 0, true);
		}).hide('lat_blind', { direction: 'right' }, 500, function() {
			distribute(1, 'f(x)');
			distribute(2, 'f(y)');
			distribute(3, 'f(z)', function() {
				activate_cont(function() { init_and_jump(map_step0); }, map_step2);
				legend('As with jobs submitted via cloud.call, you can check the status of mapped jobs using cloud.status.');
			});
		});
	});
}

function map_step2() {
	hide_controls();
	server_stop(1);
	server_stop(2);
	type_line(map_line3, 50, false, function() {
		query_job(function() {
			type_line(map_line4, 0, true, function() {
				$('#tour_cont').empty().append('Try PiCloud!');
				activate_cont(function() { init_and_jump(map_back1); }, map_step3);
				legend('Thank you taking this tour. <br><br>Click next to try PiCloud!');
			});
		});
	});
}

function map_back1() {
	type_line(map_line1, 0, false)
	type_line(map_line2, 0, true);
	server_run(1, 'f(x)');
	server_run(2, 'f(y)');
	server_run(3, 'f(z)');
	activate_cont(function() { init_and_jump(map_step0); }, map_step2);
	legend('As with jobs submitted via cloud.call, you can check the status of mapped jobs using cloud.status.');
}

function map_step3() {
	window.location.href = "/accounts/register/?ignore=True";
}


/*
 * utility functions
 */

function type_line(text_str, speed, prompt, callback) {
	line = $('#console_line' + prompt_line);
	$('#console_line' + prompt_line + '> span').remove();
	if (speed)
		type(line, text_str, 0, speed, function() {
			advance_line(prompt);
			if (callback) callback();
		});
	else {
		line.append(text_str);
		advance_line(prompt);
		if (callback) callback();
	}
}

function type(obj, text_str, index, speed, callback) {
	if (index < text_str.length) {
		obj.append(text_str.charAt(index));
		last_timeout = setTimeout(function() { type(obj, text_str, index+1, speed, callback); }, speed);
	} else
		if (callback) callback();
}

function advance_line(prompt) {
	prompt_line++;
	if (prompt_line > NUM_LINES) {
		for (i = 1; i < NUM_LINES; i++)
			$('#console_line' + i).html($('#console_line' + (i+1)).html());
		prompt_line = NUM_LINES;
	}
	if (prompt) {
		$('#console_line' + prompt_line).html('> ');
		$('#console_line' + prompt_line).append('<span class="blink">_</span>');
	}
	else
		$('#console_line' + prompt_line).empty();
}

function logo_on() {
	$('#pi_logo').css('display', 'none');
	$('#pi_logo_on').css('display', 'block');
}

function logo_off() {
	$('#pi_logo_on').css('display', 'none');
	$('#pi_logo').css('display', 'block');
}

function server_run(server_num, func_str) {
	$('#idle' + server_num).css('display', 'none');
	$('#processing' + server_num).css('display', 'block');
	$('#button_on' + server_num).css('display', 'block');
	$('#func' + server_num).html(func_str);
}

function server_stop(server_num) {
	$('#processing' + server_num).css('display', 'none');    
	$('#button_on' + server_num).css('display', 'none');
	$('#idle' + server_num).css('display', 'block');
	$('#func' + server_num).empty();
}

function stop_all_servers() {
	for (i = 1; i < 4; i++)
		server_stop(i);
}

function start_all_servers(max_duration) {
	for (i = 1; i < 4; i++)
		server_run(i);
}

function distribute(server_num, func_str, callback) {
	$('#arrow' + server_num).show('lat_blind', { direction: 'right' }, 500, function() {
		logo_off();
		server_run(server_num, func_str);
	}).hide('lat_blind', {direction: 'right' }, 500, callback);
}

function send_job(server_num, func_str, callback) {
	$('#arrow').show('lat_blind', { direction: 'right' }, 400, function() {
		logo_on();
		distribute(server_num, func_str, callback);
	}).hide('lat_blind', { direction: 'right' }, 400);
}

function query_job(callback) {
	var arrow = $('#arrow');
	var arrow_back = $('#arrow_back');

	arrow.show('lat_blind', { direction: 'right' }, 400, logo_on).hide('lat_blind', { direction: 'right' }, 400, function() {
		arrow_back.show('lat_blind', { direction: 'left' }, 300, logo_off).hide('lat_blind', { direction: 'left' }, 300, callback);
	});
}

function print_status(server_num) {
	var status = ($('#processing' + server_num).css('display') == 'block') ? "'processing'" : "'done'";
	type_line(status, 0, true);
}

function activate_cont(back_fn, next_fn) {
	if (back_fn)
		$('#tour_back').unbind('click').bind('click', back_fn).css('visibility', 'visible');
	else
		$('#tour_back').unbind('click').css('visibility', 'hidden');
	$('#tour_next').unbind('click').bind('click', next_fn);
	$('#control_buttons').fadeIn(750, function() {
		if (jQuery.browser.msie)
			this.style.removeAttribute("filter");
	});
}

function describe(desc_str) {
	$('#desc_bar').html(desc_str).removeAttr('style');
}

function legend(legend_str) {
	$('#legend').html(legend_str);
}

function set_active_chapter(chapter_name) {
	$('.chapter').each(function() {
		id = $(this).attr('id');
		if (id == chapter_name) {
			$(this).addClass('chapter_selected chapter');
		} else
			$(this).removeClass('chapter_selected');
	});
}

function hide_controls() {
	$('#control_buttons > *').unbind('click');
	$('#control_buttons').hide();
}


/* 
 * things to do when the page first loads
 */

(function($) {
	$.effects.lat_blind = function(o) {
		return this.queue(function() {
			// Create element
			var el = $(this), props = ['position','top','left'];

			// Set options
			var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
			var direction = o.options.direction || 'left'; // Default direction

			// Adjust
			$.effects.save(el, props); el.show(); // Save & Show
			var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
			var distance = wrapper.width();
			var left = wrapper.position()['left'];
			var animation = {};
			if(mode == 'show') {
				wrapper.css('width', 0); // Shift
				animation['width'] = distance;
				if (direction == 'left') {
					wrapper.css('direction', 'rtl');  // anchor element to the right
					wrapper.css('left', left + distance);
					animation['left'] = left;
				} else {
					wrapper.css('direction', 'ltr');
				}
			} else {
				animation['width'] = 0;
				if (direction != 'left') {
					wrapper.css('direction', 'rtl');
					animation['left'] = left + distance;
				} else {
					wrapper.css('direction', 'ltr');
				}
			}

			// Animate
			wrapper.animate(animation, o.duration, o.options.easing, function() {
				if(mode == 'hide') el.hide(); // Hide
				$.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
				if(o.callback) o.callback.apply(el[0], arguments); // Callback
				el.dequeue();
			});
		});
	};
})(jQuery);

$(function() {

	// set callbacks for each chapter button
	$('#cloud_call').bind('click', function() {
		if ($('#control_buttons').css('display') == 'block')
			init_and_jump(call_step0);
	}).hover(
		function() { $('#cloud_call *').each(function() { if ($(this).css('background-color') != 'transparent') $(this).css('background-color', '#e16610'); }); },
		function() { $('#cloud_call *').each(function() { if ($(this).css('background-color') != 'transparent') $(this).css('background-color', '#d15600'); }); }
	);
	$('#cloud_result').bind('click', function() {
		if ($('#control_buttons').css('display') == 'block')
			init_and_jump(status_step0);
	}).hover(
		function() { $('#cloud_result *').each(function() { if ($(this).css('background-color') != 'transparent') $(this).css('background-color', '#83981a'); }); },
		function() { $('#cloud_result *').each(function() { if ($(this).css('background-color') != 'transparent') $(this).css('background-color', '#73880a'); }); }
	);

	$('#cloud_map').bind('click', function() {
		if ($('#control_buttons').css('display') == 'block')
			init_and_jump(map_step0);
	}).hover(
		function() { $('#cloud_map *').each(function() { if ($(this).css('background-color') != 'transparent') $(this).css('background-color', '#5f7c8b'); }); },
		function() { $('#cloud_map *').each(function() { if ($(this).css('background-color') != 'transparent') $(this).css('background-color', '#4f6c7b'); }); }
	);

});
