/**
 * This file provide some functions which will be used on the MyOnlineSSH web site.
 * 
 * @author Samuel ROZE <samuel.roze@gmail.com>
 */

/**
 * Objet contenant les principales fonctionnalités JS de MyOnlineSSH.
 * 
 */
var mossh = {
	/**
	 * ID du dernier terminal créé.
	 * 
	 */
	id: 0,
	
	/**
	 * Les objets des terminaux.
	 * 
	 */
	terms: new Array(),
	
	/**
	 * Créé un nouveau terminal.
	 * 
	 */
	newTerm: function ()
	{
		// On créé un nouvel identifiant pour le terminal
		var id = this.id++;
		
		// On désélectionne les autres terminaux
		this.hideTerms();
		
		// On créé le nouveau div, cloné à partir du modèle
		var div = $('<div class="term" id="term'+id+'"></div>').appendTo('div.terms');
		
		// On clone le formulaire de connexion et on l'ajoutes au div du nouveau formulaire.
		// On l'affiche.
		var form = $("div.new div#connection_contener").clone().attr('id', 'connection_contener'+id).css('display', '')
			.appendTo(div);
		
		// On hook le submit du formulaire
		$('form', form).submit(function(){
			set_connection_message(div, _('Chargement...'), 'loading');
			if (check_form(div)) {
				// On parse le formulaire
				var form_data = parse_form(div);
				
				// Le formulaire est vérifié, on envoie les informations
				$.post(
					$(this).attr('action'),
					form_data,
					// OnSuccess function
					function (data, textStatus) {
						// On analyse le résultat
						if (typeof(data) != 'object') {
							set_connection_message(div, _('Données corrompues du relais HTTP'), 'error');
						} else {
							// Si c'est une erreur, on l'affiche
							if (data['rt'] == 'error') {
								set_connection_message(div, _(data['data']), 'error');
							} else if (data['rt'] == 'success') {
								// La connexion s'est bien passée!
								mossh.terms[id] = new mosshTerm(id, data['id'], form_data);
							} else {
								set_connection_message(div, _('Impossible d\'analyser le retour du relais'), 'error');
							}
						}
					},
					'json'
				);
				
				// On track la création du terminal
				piwikTracker.setDocumentTitle("term/new");
				piwikTracker.trackPageView(); 
			}
			return false;
		});
		
		// On affiche le message d'information de base
		set_connection_message(div, _('Connectez-vous'), 'info');
		
		// On vide les champs texte
		$('input[type=text]', form).not('input[name=port]').val('');
		
		// On créé le nouvel onglet
		$('div.close', 
			$('<li class="selected" name="term'+id+'"><span class="term"><em>Nouveau terminal'+
				(id != 0 ? ' '+id : '')+
			'</em></span><div class="close"></div></li>').appendTo('ul#lterms')
			.click(function(e){
				// Change de terminal
				mossh.switchTo(id);
			})
		).click(function(e){
			// On désactive tout ce qui correspond au terminal
			if (typeof(mossh.terms[id]) == "object") {
				mossh.terms[id].remove();
			} else {
				// On supprime le div du terminal
				div.remove();
				
				// On supprime l'onglet
				$('ul#lterms li[name=term'+id+']').remove();
				
				// On créé un nouveau terminal si n'y en a plus
				if ($('ul#lterms li').not('.new').length == 0) {
					mossh.newTerm();
				}
			}
		});
	},
	
	/**
	 * Masque tous les terminaux.
	 * 
	 */
	hideTerms: function ()
	{
		// On désélectionne les onglets
		$('ul#lterms li.selected').removeClass('selected');
		
		// On masque les divs
		$('div.terms div.term').css('display', 'none');
	},
	
	/**
	 * Switch vers un autre terminal.
	 * 
	 */
	switchTo: function (id)
	{
		// On les masques tous
		this.hideTerms();
		
		// On affiche uniquement le voulu
		$('div.terms div#term'+id).css('display', '');
		
		// On sélectionne l'onglet
		$('ul#lterms li[name=term'+id+']').addClass('selected');
	},
	
	/**
	 * Initialise le formulaire et vérifie le navigateur pour l'application.
	 * 
	 */
	init: function () {		
		// Check if cookies are enabled
		if (!navigator.cookieEnabled) {
			$('<div class="information error">'+_('Votre navigateur doit supporter les cookies')+'</div>').appendTo('#messages');
		}
		
		// Les elements ne sont plus désactivés
		$("#connectionForm input,select,textarea").removeAttr("disabled");
		
		// On créé un hook sur le clic du "+"
		$('ul#lterms li#new').click(function(e){
			mossh.newTerm();
		});
	}
};

/**
 * Objet correspondant à un terminal.
 * 
 */
function mosshTerm (id, backid, form_data) {
	// On récupère le container du terminal
	this.div = $('div.terms div#term'+id);
	this.backid = backid;
	this.id = id;
	this.nocache = 1;
	this.form_data = new Array();
	for (var i = 0; i < form_data.length; i++) {
		this.form_data[form_data[i].name] = form_data[i].value;
	}
	
	// On initialise les variables
	this.history = new Array();
	this.historyIndex = 0;
	
	/**
	 * Fonction d'initialisation de l'apparence du terminal.
	 * 
	 */
	this.init = function () {
		var div = this.div;
		var term = this;
		
		// On copie le pre#ssh dans le div du terminal, en supprimant le contener
		// de connexion
		$('div.connection_contener', div).remove();
		$('div.new pre#ssh').clone().appendTo(div).css('visibility', 'visible');
		
		// On renomme l'onglet
		$('ul#lterms li[name=term'+term.id+'] span.term').html(term.form_data['username']+'@'+term.form_data['host']+':'+term.form_data['port']);
		
		// Ensuite, on ajoutes tous les listeners		
		$('input#ssh_input', div).keyup(function (e) {
			$('span#ssh_command', div).text(
				$('input#ssh_input', div).val()
			);
			
			var index = $('input#ssh_input', div).val().length;
			$('input#ssh_input', div).selectRange(index, index);
		});
		
		// On parse toutes les touches pressées
		$('input#ssh_input', div).keydown(function (e) {
			if (e.keyCode == 37) { // Keyboard Left
				var ssh_command_value = $('span#ssh_command', div).text();
				if (ssh_command_value == '') {
					return;
				}
				var ssh_command = ssh_command_value.substring(0, ssh_command_value.length-1);
				var cursor_character = ssh_command_value.substring(ssh_command_value.length-1, ssh_command_value.length);
				var ssh_command_post = $('span#ssh_cursor_character', div).text() + $('span#ssh_command_post', div).text();
			} else if (e.keyCode == 39) { // Keyboard right
				var ssh_command_post_value = $('span#ssh_command_post', div).text();
				if (ssh_command_post_value == '') {
					return;
				}
				var ssh_command = $('span#ssh_command', div).text() + $('span#ssh_cursor_character', div).text();
				var cursor_character = ssh_command_post_value.substring(0, 1);
				var ssh_command_post = ssh_command_post_value.substring(1);
			} else if (e.keyCode == 46) { // Suppr key
				var ssh_command = $('span#ssh_command', div).text();
				var ssh_cursor_character_value = $('span#ssh_cursor_character', div).text()
				var ssh_command_post = $('span#ssh_command_post', div).text().substring(1);
				var cursor_character = $('span#ssh_command_post', div).text().substring(0, 1);
				if (cursor_character == '') {
					return;
				}
			} else if (e.keyCode == 38) { // Keyboard Up
				var ssh_command = term.getHistory(1);
				var ssh_cursor_character = ' ';
				var ssh_command_post = '';
			} else if (e.keyCode == 40) { // Keyboard Down
				var ssh_command = term.getHistory(-1);
				var ssh_cursor_character = ' ';
				var ssh_command_post = '';
			} else if ((e.which == 67 || e.which == 99) && e.ctrlKey) { // Ctrl-C
				// On envoie le break signal
				$.post(
					$('form#ssh_form', div).attr('action'),
					{sshid: term.backid,command:'break'}
				);
				
				var ssh_cursor_character = ' ';
				var ssh_command_post = '';
				var ssh_command = '';
			} else if (e.keyCode == 9) { // Tab
				// TODO Tab key
				alert('Tab key n\'est pas encore supporté');
				return false;
			} else {
				$('span#ssh_command', div).text(
					$('input#ssh_input', div).val()
				);
			}
			// Dispatch values (order is important)
			$('span#ssh_cursor_character', div).text(cursor_character);
			$('input#ssh_input', div).val(ssh_command);
			$('span#ssh_command', div).text(ssh_command);
			$('span#ssh_command_post', div).text(ssh_command_post);
		});
		
		// Soumission d'une nouvelle commande
		$('form#ssh_form', div).submit(function (e) {
			var command = $('span#ssh_command', div).text();
			
			if ($('span#ssh_command_post', div).text() != ' '
				&& $('span#ssh_command_post', div).text() != '') {
				command += $('span#ssh_cursor_character', div).text();
				command += $('span#ssh_command_post', div).text();
			} else if ($('span#ssh_cursor_character', div).text() != ' ') {
				command += $('span#ssh_cursor_character', div).text();
			}
			
			// On envoie la commande
			$.post(
				$(this).attr('action'),
				{sshid: term.backid,command:'send',data:command},
				function (data, textStatus) {
					// La commande a été envoyée avec succès
					// On récupère le résultat
					if (typeof(data) != 'object' || !data) {
						alert('Une erreur de connexion au relais est apparue. Le terminal va être fermé.');
						term.remove();
					}
					
					// S'il y a eu une erreur, on le fait savoir
					if (data['rt'] == 'error') {
						// On ferme le terminal
						if (confirm('Une erreur est apparue: '+data['data']+"\n"+'Voulez-vous fermer le terminal?')) {
							term.remove();
						}
					}
				}, 'json'
			);
			
			// On track l'envoie d'une commande
			piwikTracker.setDocumentTitle("term/send");
			piwikTracker.trackPageView(); 
			
			// On réactualise l'historique
			term.history.unshift(command);
			term.historyIndex = 0;
			
			// Clean values
			$('span#ssh_command', div).text('');
			$('span#ssh_cursor_character', div).text(' ');
			$('span#ssh_command_post', div).text('');
			$('input#ssh_input', div).val('');
			$('input#ssh_input', div).focus();
			
			// On retourne "false" pour par que le formulaire soit réellement envoyé.
			return false;
		});
		
		// S'il y a un focus sur la commande, on créé le focus sur le champ de texte
		$('span#ssh_command', div).focus(function (e) {
			$('input#ssh_input', div).focus();
		});
		$('pre#ssh', div).focus(function(e) {
			if (e.target == this) {
				$('span#ssh_command', div).focus();
				
				var cursorFunc = function () {
					var cursor = $('span#ssh_cursor_character', div);
					if (cursor.css('visibility') == 'visible') {
						cursor.css('visibility', 'hidden');
					} else {
						cursor.css('visibility', 'visible');
					}
					setTimeout(cursorFunc, 600);
				};
				cursorFunc();
			}
		}).click(function (e) {
			if (e.target == this) {
				$('span#ssh_command', div).focus();
			}
		}).blur(function (e) {
			MyOnlineSSHSocket.cursorFunc = function () {
				$('span#ssh_cursor_character', div).css('visibility', 'hidden');
			};
		});
		
		// Quand la fenêtre change de taille, on redimentionne à nouveau le terminal
		$(window).resize(this.resizeTerm);
		
		// On fixe la taile du terminal
		this.resizeTerm();
		
		// On affiche les bonnes couleurs
		this.setColors();
		
		// On lance la récupération automatique
		var recvFunction = function () {
			$.get(
				$('pre#ssh', div).attr('action'),
				{sshid: term.backid, nocache: term.nocache++},
				function (data, textStatus) {
					// On récupère le résultat
					if (typeof(data) != 'object' || !data) {
						alert('Une erreur de connexion au relais est apparue. Le terminal va être fermé.');
						term.remove();
					}
					
					// Ce sont des données à afficher à l'écran
					if (data['rt'] == 'recv') {
						// On récupère la donnée
						var result = data['data'];
						
						// On l'affiche
						$('span#ssh_content', div).html($('span#ssh_content', div).html()+result);
						
						// On met le curseur sur le ssh_input
						$('input#ssh_input', div).focus();
						
						// On relance la fonction dans 1ms
						setTimeout(recvFunction, 1);
					} else if (data['rt'] == 'error') {
						// On ferme le terminal
						if (confirm('Une erreur est apparue: '+data['data']+"\n"+'Voulez-vous fermer le terminal?')) {
							term.remove();
						}
					}
				}, 'json'
			);
			
			// On track la réception d'un message
			piwikTracker.setDocumentTitle("term/recv");
			piwikTracker.trackPageView(); 
		};
		recvFunction();
	};
	
	/**
	 * Redimensionne le terminal pour la fenêtre du client.
	 * 
	 */
	this.resizeTerm = function ()
	{
		// On calcul la taille du terminal
		var width = $('div#page').width() - 120;
		var height = $(window).height() - $('div#header').height() - 40;
	
		// On l'envoie au relais pour qu'il change la taille distante
		$('pre#ssh', this.div).width(width).height(height).css('overflow', 'auto');
		$(this.div).width(width).height(height);
		$('div.ad').height(height);
	};

	/**
	 * Return the command history.
	 * 
	 * @param integer Index of the command in history
	 * @return string The command
	 */
	this.getHistory = function (difference)
	{
		this.historyIndex += difference;
		
		if (this.historyIndex <= 0) {
			this.historyIndex = 0;
			return '';
		} else if (this.history[this.historyIndex-1] == undefined) {
			this.historyIndex -= difference;
		}
		return this.history[this.historyIndex-1]
	};

	/**
	 * Apply the colors.
	 * 
	 * @return void
	 */
	this.setColors = function ()
	{
		$('pre#ssh', this.div).css('background', '#'+this.colors.background);
		$('form#ssh_form input#ssh_input', this.div).css('background', '#'+this.colors.background);
		$('pre#ssh span#ssh_content, pre#ssh span#ssh_command, pre#ssh span#ssh_command_post', this.div)
			.css('color', '#'+this.colors.color);
		$('pre#ssh span#ssh_cursor span#ssh_cursor_character', this.div)
			.css('background', '#'+this.colors.cursor.background)
			.css('color', '#'+this.colors.cursor.color);
	};
	
	/**
	 * Ferme le terminal.
	 * 
	 */
	this.remove = function ()
	{
		// On supprime le div du terminal actuel
		this.div.remove();
		
		// On supprime l'onglet
		$('ul#lterms li[name=term'+this.id+']').remove();
		
		// On créé un nouveau terminal si n'y en a plus
		if ($('ul#lterms li').not('.new').length == 0) {
			mossh.newTerm();
		}
	}
	
	/**
	 * Object which define colors which will be used for the SSH
	 * command line.
	 * 
	 * @var object
	 */
	this.colors = {
		background: '000000',
		color: 'ffffff',
		cursor: {
			background: 'ffffff',
			color: '000000'
		}
	};
	
	// On lance l'initialisation du terminal
	this.init();
};

/**
 * Prototype pour jQuery: permet de sélectionner une partie de texte
 * de "start" à "end".
 * 
 */
$.fn.selectRange = function(start, end) {
    return this.each(function() {
        if(this.setSelectionRange) {
            this.focus();
            this.setSelectionRange(start, end);
        } else if(this.createTextRange) {
            var range = this.createTextRange();
            range.collapse(true);
            range.moveEnd('character', end);
            range.moveStart('character', start);
            range.select();
        }
    });
};

/**
 * This function is called when the form is submited and have to check
 * informations, prepare screen to SSH and call the function which will
 * initiate the socket.
 * 
 * @param object div Le conteneur du terminal
 * @return void
 */
function check_form (div)
{
	// On récupère le formulaire
	var form = $('form', div);
	
	if ($('#connection_field_host', form).val() == '') {
		set_connection_message(div, _('L\'adresse du serveur est vide'), 'error');
	} else if ($('#connection_field_port', form).val() == '') {
		set_connection_message(div, _('Veuillez spécifier le numéro de port'), 'error');
	} else if ($('#connection_field_username', form).val() == '') {
		set_connection_message(div, _('Le nom d\'utilisateur est vide'), 'error');
	} else {
		var str = $("input[name='authentification']:checked", form).val();
		if (str == undefined) {
			set_connection_message(div, _('Séléctionnez votre méthode de connexion'), 'error');
		} else if (str == 'password') {
			if ($('#connection_field_password', form).val() == '') {
				set_connection_message(div, _('Le mot de passe est vide'), 'error');
			} else {
				return true;
			}
		} else if (str == 'private-key') {
			set_connection_message(div, _('L\'authentification par clé n\'est pas encore supporté'), 'error');
		} else {
			set_connection_message(div, _('Type d\'authentification invalide'), 'error');
		}
	}
	return false;
}

/**
 * Prints a message error in the top of the connexion div
 * 
 * @param message
 * @return void
 */
function set_connection_message (div, message, type)
{
	var obj = $('#connection_message', div);
	
	if (message == false) {
		obj.slideUp("slow");
	} else {
		obj.html('<div class="'+type+'">'+message+'</div>');
		obj.slideDown("slow");
	}
	
}

/**
 * This function will be used when changing the authentification
 * method in the connection form.
 * 
 * @param div_id
 * @return void
 */
function switch_auth (div_id)
{
	$("div#connection div.auth_method").css({display:"none"});
	$("div#connection div.auth_method#"+div_id).css({display:"block"});
}

/**
 * Transforme le formulaire en donnée jSON.
 * 
 * @param div
 */
function parse_form (div)
{
	var form = $('form', div);
	var object = {};
	
	// On récupère les données.
	return form.serializeArray();
}

/**
 * Set some of functions/methods/operations at start-up, which init some
 * MyOnlineSSH divs.
 * 
 */
$(document).ready(function(){
	// On initialise les objets
	mossh.init();
	
	// On lance un nouveau terminal
	mossh.newTerm();
});


