function Form () {
	Form.prototype.node = null;

	Form.prototype.init = function (node)
	{
		Form.prototype.node = (node) ? node.find('form') : $('form');

		if(!Form.prototype.node) {
			return;
		}
		
		Form.prototype.validation_rules();

		Form.prototype.node.each(function(){
			var form = $(this);
			var options = validation.get_rules(form.attr('id'));
			
			if (node) {
				if (form.attr('id') != 'paypal_form') {
					options['submitHandler'] = gui.dialog.form_submit;
				}
				else {
					options['submitHandler'] = function(){
						form.find('input[type=submit]').click();
					}
				}
			}
			
			// set the validator
			form.validate(options);
		});
	};
	
	Form.prototype.validation_rules = function ()
	{
		jQuery.validator.addMethod("matches", function(value, element, param) 
		{
			if ($('#' + param).val() != $(element).val()) {
				return false;
			}
			
			return true;
		}, 'Your passwords do not match');
	};
};

var validation = {
	/**
	 * Validation rules for page_form
	 *
	 */
	page_form: {
		rules: {
			title: {
				required: true,
				remote: {
					url: '/validate/page',
					type: 'post',
					data: {
						test_id: function () {
							return $('input[name=test_id]').val();
						},
						id: function () {
							return $('input[name=id]').val();
						}
					}
				}
			},
			screenshot: {
				required: function() {
					return ($('input[name=location]').val() == "http://"
							|| $('input[name=location]').val() == "");
				}
			}
		},
		messages: {
			title: 'This is not a valid title',
			screenshot: 'Please upload your screenshot image or specify an URL to your webpage'
		}
	},

	/**
	 * Validation rules for test_form
	 *
	 */
	test_form: {
		rules: {
			title: {
				required: true,
				remote: {
					url: '/validate/test',
					type: 'post',
					data: {
						id: function () {
							return $('input[name=id]').val();
						}
					}
				}
			},
			description: {
				required: true
			},
			return_page: {
				required: true,
				url: true
			}
		},
		messages: {
			title: 'This is not a valid title',
			description: 'This is not a valid description',
			return_page: 'This is not a valid website'
		}
	},
	
	task_form: {
		rules: {
			task: {
				required: true
			}
		},
		messages: {
			task: 'This is not a valid task'
		}
	},

	login_form: {
		rules: {
			login_username: {
				required: true
			},
			login_password: {
				required: true
			}
		},
		messages: {
			login_username: 'This is not a valid username',
			login_password: 'This is not a valid password'
		}
	},
	
	address_form: 
	{
		rules: {
			firstname: 'required',
			lastname: 'required',
			address: 'required',
			postalcode: 'required',
			city: 'required',
			country: 'required'
		},
		messages: 
		{
			firstname: 'First name is required',
			lastname: 'Last name is required',
			address: 'Address is required',
			postalcode: 'Postal/Zip code is required',
			city: 'City is required',
			country: 'Country is required'
		}
	},
	
	register_form:
	{
		rules: {
			agree: {
				required: true
			},
			register_username: {
				required: true,
				remote: {
					url: '/validate/register',
					type: 'post'
				}
			},
			register_email: {
				required: true,
				remote: {
					url: '/validate/email',
					type: 'post',
					data: {
						id: function(){
							return $('input[name=register_email]').val();
						}
					}
				}
			},
			register_password: {
				required: true
			},
			register_password_check: {
				required: true,
				matches: 'register_password'
			}
		},
		messages:
		{
			agree: 'Please accept our policy'
		}
	},
	

	/**
	 * Get the validation rules for the given form id
	 */
	get_rules: function (form_name)
	{
		return (validation[form_name]) ? validation[form_name] : {};
	}
};

function Dialog () {
	Dialog.prototype.node = false;
	Dialog.prototype.link = false;
	Dialog.prototype.submit = false;
	Dialog.prototype.title = false;

	/**
	 * Initializes the dialog dijit
	 */
	Dialog.prototype.init = function ()
	{
		// get all the links that require a dialog
		var nodes = $("a.dialog");

		// if there are no links, do nothing
		if (!nodes.length) {
			return;
		}

		// connect event-handlers to the links
		nodes.bind("click", Dialog.prototype.load);
	};

	/**
	 * Initialize the dialog
	 */
	Dialog.prototype.load = function (e)
	{
		// prevent the link to go to the specified url
		e.preventDefault();
		
		gui.waitCursor();

		// set the dialog link
		Dialog.prototype.node = $("#dialog")
		Dialog.prototype.link = $(this);
		Dialog.prototype.title = Dialog.prototype.link.attr('title');

		// instanciate the dialog if this hasn"t been done before
		if (Dialog.prototype.node) {
			Dialog.prototype.node.dialog('destroy');
		}

		var div = document.createElement('div');
		$(div).attr('id', 'dialog')
				.load(Dialog.prototype.link.attr('href'), null, Dialog.prototype.show)
				.appendTo('body');

		// set the new dialog node
		Dialog.prototype.node = $(div);
	};

	/**
	 * Show the dialog
	 */
	Dialog.prototype.show = function ()
	{
		// instanciate the dialog if this hasn"t been done before
		if (Dialog.prototype.node) {
			Dialog.prototype.node.dialog('destroy');
		}

		var form = Dialog.prototype.node.find('form');

		// find the submit button
		Dialog.prototype.submit = form.find('input[type=submit]');

		var label = Dialog.prototype.submit.val();
		var buttons = {};

		if(label)
		{
			buttons[label] = function () {
				// @TODO: disable the submit button here
				Dialog.prototype.submit.submit();
			};
			buttons['Cancel'] = function () {
				Dialog.prototype.node.dialog('close');
			};

			// hide the original buttons
			Dialog.prototype.submit.parent().hide();
			form.find('#' + Dialog.prototype.submit.attr('id') + '-label').hide();
		}

		// show dialog
		Dialog.prototype.node.dialog({
			bgiframe: true,
			width: 560,
			resizable: false,
			modal: true,
			buttons: buttons,
			title: Dialog.prototype.title,
			open: function ()
			{
				// set external links
				gui.set_links(Dialog.prototype.node);
				gui.form.init(Dialog.prototype.node);
			}
		});
		
		gui.defaultCursor();
	};

	/**
	 * Submit handler for forms
	 */
	Dialog.prototype.form_submit = function (form)
	{
		var url = $(form).attr('action') + '?ajax=' + Number(new Date());

		// disable the submit button when form is being submitted
		$(form).find('input[type=submit]').attr('disabled', 'disabled');

		// disable the dialog buttons
		$('.ui-dialog-buttonpane button').attr('disabled', 'disabled').addClass('ui-state-disabled');
		$('.ui-dialog-buttonpane button:first').html('Please wait...');

		// submit the form via ajax (scans for file upload fields)
		$(form).ajaxSubmit({
			url: url,
			type: 'post',
			dataType: 'json',
			success: function(data){
				if (data['complete'] == 'changelogofailed') {
					$('body').css('cursor', 'default');
					$(form).find('input[type=submit]').removeAttr('disabled', 'disabled');
					$('.ui-dialog-buttonpane button').removeAttr('disabled', 'disabled').removeClass('ui-state-disabled');
					
					$("#changelogoerror").html(data['msg']);
					$("#changelogoerror").show();
					return;
				}
				
				// for reset_custom_pagefull, save then close the dialog
				if (data['complete'] == 'reset_custom_pagefull') {
					Dialog.prototype.node.dialog('close');
					DefaultPagefull(data['test_id']);
					return;
				}
				
				// for reset_custom_intro, save then close the dialog
				if (data['complete'] == 'reset_custom_intro') {
					Dialog.prototype.node.dialog('close');
					DefaultIntro(data['test_id']);
					return;
				}
				// for reset_custom_thanks, save then close the dialog
				if (data['complete'] == 'reset_custom_thanks') {
					Dialog.prototype.node.dialog('close');
					DefaultThanks(data['test_id']);
					return;
				}
				// close the dialog if the link is not a sequence of pages
				// or the returned data equals true
				if (data['complete']) {
					Dialog.prototype.node.dialog('close');
					gui.goToUrl(data['url']);
				}
				else {
					gui.waitCursor();
					Dialog.prototype.node.load(data['url'], null, Dialog.prototype.show);
					
					if (data.data['title']) {
						Dialog.prototype.title = data['data']['title'];
					}
				}
				
			}
		});
	};
};

function Task () {
	Task.prototype.link = false;
	Task.prototype.node = false;

	Task.prototype.init = function ()
	{
		// get all the "New task" links
		var nodes = $("a.new_task");

		// if none are found, do nothing
		if (!nodes.length) {
			return;
		}

		// set checkbox functionality
		Task.prototype.init_toggle.call(this);

		// for each list that is found, attach onclick handler
		nodes.bind("click", Task.prototype.create);
		
		var pages = $('#content ul.pages li.item');
								
		if(!pages.not(':has(input:checked)').size()) {
			$('#content #start_test, #content #preview_test').show();
			$('#content #start_test label, #content #preview_test label').hide();
			$('#content #start_test a, #content #preview_test a').show();
			$('#content #no_tasks').hide();
		} else {
			$('#content #no_tasks').show();
			$('#content #start_test, #content #preview_test').show();
			$('#content #start_test a, #content #preview_test a').hide();
			$('#content #start_test label, #content #preview_test label').show();
			$('#content #start_test label, #content #preview_test label').css('display','block');
		}
	};

	Task.prototype.create = function (e)
	{
		e.preventDefault();
		
		Task.prototype.link = $(this);

		var span = document.createElement('span');
		Task.prototype.node = $(span);
		Task.prototype.link.parent().prepend(Task.prototype.node);

		$(Task.prototype.node).editable(Task.prototype.link.attr('href'), {
			type: 'text',
			name: 'title',
			width: 350,
			cancel: 'Cancel',
			submit: 'Save',
			style: 'display:inline; float:left;',
			callback: Task.prototype.submit,
			onreset: Task.prototype.reset
		});

		Task.prototype.link.hide();
		Task.prototype.node.click();
	};

	Task.prototype.init_toggle = function ()
	{
		var nodes = $('#content input[type=checkbox]');

		if(!nodes.length) {
			return;
		}
		
		// make tasks draggable
		$('ul.tasks').disableSelection().sortable({
			axis: 'y',
			helper: 'clone',
			placeholder: 'clone',
			helper: 'clone',
			forcePlaceholderSize: true,
			items: 'li',
			distance: 5,
			cursorAt: 'top',
			opacity: 0.8,
			stop: function (e, ui) { 
				var order = $(this).sortable('serialize').replace(/task\[\]=/g, '').replace(/&/g, ',');
				var page = $(this).parent().attr('id').replace('page_', '');
				custom.rpc.task.orderTasks(page, order).addCallback(
					function () { 
						gui.highlightSingle(ui.item); 
					}
				);
			}
		});
		
		$('ul.tasks li').css('cursor', 'move');

		nodes.bind('click', Task.prototype.toggle);
		// unbind for checkbox show_btn_next only
		for(x=1;x< nodes.length;x++)
		{
			if(nodes[x]['id'] == 'show_btn_next') 
				$("#show_btn_next").unbind('click', Task.prototype.toggle);
		}
	};

	/**
	 * Toggle task on or off
	 */
	Task.prototype.toggle = function (e)
	{
		// don't enable or disable the checkbox
		e.preventDefault();
		e.stopPropagation();

		// capture the node
		var node = $(this);
		
		// set the wait cursor
		gui.waitCursor();
		
		// setup callback function
		var callback = function (status)
		{
			// status needs to be 0 or more (-1 means error)
			if(status >= 0)
			{	
				// set the default cursor
				gui.defaultCursor();
				
				// check/uncheck the checkbox
				(status == 1) ? node.attr('checked', true) : node.attr('checked', false);

				var count = $('#content #task_count');
				var pages = $('#content ul.pages li.item');
				var total = $('#content ul.pages input:checked').size();
				gui.highlightSingle(count.text(total).parent());
				
				if(!pages.not(':has(input:checked)').size()) {
					$('#content #start_test, #content #preview_test').show();
					$('#content #no_tasks').hide();
					$('#content #start_test label, #content #preview_test label').hide();
					$('#content #start_test a, #content #preview_test a').show();
				} else {
					gui.fadeInSingle('#content #no_tasks');
					gui.highlightSingle('.actions');
					$('#content #start_test a, #content #preview_test a').hide();
					$('#content #start_test label, #content #preview_test label').show();
					$('#content #start_test label, #content #preview_test label').css('display','block');
				}
			} 
		};
		
		// create task id and execute RPC call
		var task_id = $(e.target).parent().attr('id').replace('task_', '');
		custom.rpc.task.toggleTask(task_id).addCallback(callback);
	};

	Task.prototype.submit = function ()
	{
		Task.prototype.reset.call(this);
		gui.refresh();
	};

	Task.prototype.reset = function ()
	{
		gui.task.node.remove();
		gui.task.link.show();
	};
};

function edit_task(task, id, page, self)
{
	if (task.length > 0) {
		custom.rpc.task.editTask(task, id, page);
	} else {
		custom.rpc.task.deleteTask(id, page).addCallback(function (msg)
		{
			if (msg > 0)
			{
				var parent = self.domNode.parentNode;
				parent.parentNode.removeChild(parent);
			}
		});
	}
}

function Test () {
	Test.prototype.identifier = false;

	Test.prototype.init = function ()
	{
		Test.prototype.init_point_color.call(this);

		var nodes = $("#content a.show-list");

		if (!nodes.length) {
			return;
		}

		nodes.bind("click", Test.prototype.show_list);
	};

	Test.prototype.edit_description = function (value, test_id)
	{
		custom.rpc.test.editDescription(value, test_id);
	};

	Test.prototype.edit_title = function (value, test_id)
	{
		custom.rpc.test.editTitle(value, test_id).addCallback(gui.goToUrl);
	};

	Test.prototype.show_list = function (e)
	{
		e.preventDefault();

		Test.prototype.identifier = ($(this).attr("rel") != "") ? $(this).attr("rel") : false;

		var nodes = $("#content div.test-list");

		if(Test.prototype.identifier) {
			nodes.each(Test.prototype.animate);
		}
		else {
			gui.fadeInList(nodes);
		}
	};

	Test.prototype.init_point_color = function ()
	{
		var nodes = $("#content a.point_color");

		if(!nodes.length) {
			return;
		}

		nodes.bind("click", Test.prototype.change_point_color);
	};

	Test.prototype.change_point_color = function (e)
	{
		e.preventDefault();

		var node = $(this);
		if(!node.hasClass('selected'))
		{
			var task_id = node.parent().parent().attr('id').replace('task_', '');
			var color = node.find('span').attr('class');

			custom.rpc.task.changePointColor(task_id, color).addCallback(
				function(result)
				{
					if(result)
					{
						node.parent().find('.selected').removeClass('selected');
						node.addClass('selected');
					}
				}
			);
		}
	};

	Test.prototype.animate = function (e) {
		var node = $(this);
		if(!node.hasClass(Test.prototype.identifier)) {
			gui.fadeOutSingle(node);
		} else {
			gui.fadeInSingle(node);
		}
	};
};

function Confirmation () {
	/**
	 * Initialize the confirmations boxes
	 */
	Confirmation.prototype.init = function ()
	{
		// get a list of links that require confirmation
		// and add onclick handlers to the links
		$("#sidebar a.require-confirmation").bind("click", Confirmation.prototype.toggle);
	};

	/**
	 * Show or hide a confirmation box
	 */
	Confirmation.prototype.toggle = function (e)
	{
		// get the nodes
		var nodes = $(this).parent().find('div.confirmation');

		// check if there are confirmation blocks
		if (!nodes.length) { return; }

		// disable default behavior
		e.preventDefault();

		// close the confirmation box when "no" is clicked
		$(e.target).parent().find("a.decline").bind("click", function (e) {
			e.preventDefault();
			gui.fadeOutList(nodes);
		});

		// toggle confirmation on and off
		if (nodes.css("display") == "block") {
			gui.fadeOutList(nodes);
		} else {
			gui.fadeInList(nodes);
		}
	};
};

function Message () {
	Message.prototype.init = function ()
	{
		// attach the event handler
		$("#messages").bind("click", Message.prototype.fade);
	};

	/**
	 * Do a fadeout on the message node
	 */
	Message.prototype.fade = function (e)
	{
		gui.fadeOutSingle(this);
	};
};

function Gui() {
	Gui.prototype.confirmation = new Confirmation();
	Gui.prototype.dialog = new Dialog();
	Gui.prototype.message = new Message();
	Gui.prototype.form = new Form();
	Gui.prototype.task = new Task();
	Gui.prototype.test = new Test();

	/**
	 * Initialize all JavaScript functionality
	 */
	Gui.prototype.init = function ()
	{
		Gui.prototype.set_links.call(this);
		Gui.prototype.form.init();
		Gui.prototype.message.init();
		Gui.prototype.test.init();
		Gui.prototype.task.init();
		Gui.prototype.dialog.init();
		//gui.confirmation.init();
	};

	/**
	 * Set the external links in this document
	 * @param node
	 */
	Gui.prototype.set_links = function (node)
	{
		// get all external links
		$((!node) ? '#container' : node).find("a.external").attr("target", "_blank");
	};

	/**
	 * Fade out a list of elements
	 * @param nodes
	 */
	Gui.prototype.fadeOutList = function (nodes) {
		$(nodes).animate({opacity: "hide", height: "hide"}, "fast");
	};

	/**
	 * Fade in a list of elements
	 * @param nodes
	 */
	Gui.prototype.fadeInList = function (nodes) {
		$(nodes).animate({opacity: "show", height: "show"}, "fast");
	};

	/**
	 * Fade out a single element
	 * @param node
	 */
	Gui.prototype.fadeOutSingle = function (node) {
		$(node).animate({opacity: "hide", height: "hide"}, "fast");
	};

	/**
	 * Fade in a single element
	 * @node node
	 */
	Gui.prototype.fadeInSingle = function (node) {
		$(node).animate({opacity: "show", height: "show"}, "fast");
	};
	
	/**
	 * Highlight a single element
	 * @param node
	 */
	Gui.prototype.highlightSingle = function (node) {
		$(node).effect('highlight', {}, 750);
	};

	/**
	 * Select all text inside an input field
	 * @param node
	 */
	Gui.prototype.selectAll = function (node) {
		$(node).focus().select();
	};
	
	/**
	 * Set the wait cursor
	 */
	Gui.prototype.waitCursor = function ()
	{
		$('body').css('cursor', 'wait');
	};
	
	/**
	 * Set the default cursor
	 */
	Gui.prototype.defaultCursor = function ()
	{
		$('body').css('cursor', 'default');
	};
	
	/**
	 * Refresh the current page
	 */
	Gui.prototype.refresh = function () {
		Gui.prototype.goToUrl(window.location.href);
	};
	
	/**
	 * Go to the specified URL
	 * @param string url
	 */
	Gui.prototype.goToUrl = function (url) {
		if(url !== "") {
			window.location.href = url;
		}
	};
};

// attach the onload function
var gui = new Gui();
$(document).ready(gui.init);