function NodeConnection() {
	var selfRef = this;

	this.socketIO = {
		connection: {
			status: null,  // connected, denied
			connected: false,
			retries: 0
		},
		socket: io(socketIO, {secure: true, reconnection: false}),
		token: jQuery("#page-information").data('node-token'),
		intervals: {
			reconnect: null,
			reconnectCountdown: null
		}
	}

	this.caches = {
		exchangeRates: {
			timestamp: null,
			data: null,
			rebuilding: false
		}
	}

	this.processes = {
		helpQuery: null
	}

	//console.log(this.socketIO);

	this.socketIO.socket.on('connect', function () {
		var connectionTroubleContainer = jQuery(".cv-trouble-connecting"),
			connectionTroubleOptions = connectionTroubleContainer.data('cv-options'),
			reconnectStatusMessageContainer = jQuery(".cv-reconnect-status-message"),
			messages = reconnectStatusMessageContainer.data('cv-messages');

		connectionTroubleContainer.removeClass('active');
		selfRef.socketIO.connection.connected = true;
		selfRef.authentificate();
	});

	this.socketIO.socket.on('cv_notification', function(data) {
		console.log(data);
		CV.Dashboard.showSiteNotification(data.message, 'instruction', 'envelope-o');
	});

	this.socketIO.socket.on('disconnect', function (data) {
		/*if (data == 'transport close')
		{
			// console.log("Lost connection because of server disconnect!");
			// server disconnected
			selfRef.socketIO.intervals.reconnect = setInterval(function() {
				//console.log("Trying to reconnect!");
				if (selfRef.socketIO.connection.connected == true) 
				{
					clearInterval(selfRef.socketIO.intervals.reconnect);
					return;
				}
				selfRef.socketConnect();
			}, 1000);
		}*/
		console.log("Lost connection to the server", data);
		selfRef.socketConnect();
		selfRef.socketIO.connection.connected = false;
	});

	this.socketIO.socket.on('connect_error', function (data) {
		console.log("Connection error");
		console.log(data);

		var connectionTroubleContainer = jQuery(".cv-trouble-connecting"),
			connectionTroubleOptions = connectionTroubleContainer.data('cv-options'),
			reconnectStatusMessageContainer = jQuery(".cv-reconnect-status-message"),
			messages = reconnectStatusMessageContainer.data('cv-messages');
		
		reconnectStatusMessageContainer.html(messages.waitingForReconnect);
		reconnectStatusMessageContainer.find('.cv-reconnect-in-seconds').html(connectionTroubleOptions.retry);
		jQuery(".cv-trouble-connecting").addClass('active');
		selfRef.socketIO.intervals.reconnectCountdown = setInterval(function() {
			var countdownContainer = jQuery(".cv-trouble-connecting .cv-reconnect-in-seconds"),
				countdownTimer = countdownContainer.html(),
				reconnectStatusMessageContainer = jQuery(".cv-reconnect-status-message"),
				messages = reconnectStatusMessageContainer.data('cv-messages');

			countdownTimer = parseInt(countdownTimer);
			countdownTimer--;
			if (countdownTimer == 0)
			{
				reconnectStatusMessageContainer.html(messages.reconnectingNow);
				clearInterval(selfRef.socketIO.intervals.reconnectCountdown);
				selfRef.socketConnect();
			} else {
				countdownContainer.html(countdownTimer);
			}
		}, 1000);

		/*selfRef.socketIO.connection.retries++;
		if (selfRef.socketIO.connection.retries < 5) setTimeout(function() {
			selfRef.socketConnect();
		}, 5000);*/
	});

	this.socketIO.socket.on('cv_auth_response', function(data) {
		if (data.connected == true)
		{
			selfRef.fetchExchangeRates();
		} else {
			this.socketIO.connection.status = 'denied';
		}
	});

	/** The node server answers for the admin counters **/
	this.socketIO.socket.on('cv_fetch_admin_counters_response', function(data) {
		console.log(data);
		if (data.result == false)
		{
			jQuery(".cv-dashboard-menu-text-count-box-number").html("-");
		} else if (data.result == true)
		{
			jQuery.each(data.data, function(key, value) {
				jQuery(".cv-dashboard-menu-text-count-box-number[data-cv-group='"+key+"']").html(value);
			});
		}
	});

	/** The node server pushes the exchange rates to the client **/
	this.socketIO.socket.on('cv_exchange_rates_push', function(data) {

		selfRef.caches.exchangeRates = {
			timestamp: Math.floor(Date.now() / 1000),
			data: data
		};

		selfRef.updateExchangeRates();
	});

	/** The node server replies with the help input filter **/
	this.socketIO.socket.on('cv_help_input_filter_response', function(data) {

		console.log("Response received");

		var resultContainer = jQuery(".cv-help-box-result-contents"),
			resultContainerMessage = resultContainer.find(".cv-help-box-standard-messages"),
			resultMessages = resultContainer.data('cv-possible-messages'),
			ulContainer = jQuery("<ul></ul>").addClass('cv-help-box-standard-message-ul');

		if (typeof data.data['helpQueryId'] == 'undefined') return;
		if (typeof data.data['query-results'] == 'undefined') return;
		if (data.data['helpQueryId'] != selfRef.processes.helpQuery) {
			console.log("Invalid query type", selfRef.processes.helpQuery, data.data['helpQueryId']);
			return;
		}

		if (data.data['query-results'].length == 0)
		{
			resultContainerMessage.html(resultMessages['no-results']);
			return;
		}


		console.log(data.data['query-results']);
		
		for (var i=0;i<data.data['query-results'].length; i++)
		{
			var questionObj = data.data['query-results'][i];
			var li = jQuery("<li></li>").addClass('cv-help-box-standard-message-ul-li');

			li.html(questionObj['question_en']);
			li.data('cv-question-id', questionObj.id);
			li.click(function() {
				var qId = jQuery(this).data('cv-question-id'),
					langCode = siteLanguage;

				console.log(qId);
				jQuery('.cv-help-columns-wrapper').addClass('active');
				jQuery('.cv-help-question-box-title').html(jQuery(this).html());
				jQuery('.cv-help-question-box-text').html('<i style="margin-right: 10px;" class="fa fa-spinner fa-spin" aria-hidden="true"></i>' + jQuery('.cv-help-columns-wrapper').data('cv-loading-message'));
				Account.getKnowledgeBaseQuestion(qId, langCode);
			});
			ulContainer.append(li);
		}

		resultContainerMessage.html('');
		resultContainerMessage.append(ulContainer);
		resultContainerMessage.css('height', ulContainer.outerHeight(true))
		console.log(ulContainer);
	});

	this.socketIO.socket.on('cv_get_knowledgebase_question_response', function(data) {
		console.log(data);
		if (data.result == false) {
			jQuery('.cv-help-columns-wrapper').removeClass('active');
			CV.Dashboard.showSiteNotification(data.error, 'error', 'times');
		} else {
			jQuery('.cv-help-question-box-text').html(data.text);
			jQuery('.cv-help-question-box-title').html(data.question);

			if (data.error != null) CV.Dashboard.showSiteNotification(data.error, 'error', 'times');
		}
	});
}

NodeConnection.prototype.socketConnect = function() {
	if (this.socketIO.connection.status != 'denied') this.socketIO.socket.connect();
}

NodeConnection.prototype.getAdminCounters = function() {
	var selfRef = this;

	this.socketIO.socket.emit('cv_fetch_admin_counters', {});
	jQuery(".cv-dashboard-menu-text-count-box-number").html("<i class='fa fa-spin fa-spinner'></i>");
}

NodeConnection.prototype.helpInputRequest = function(inputContent) {
	var sendData = {
		helpQuery: guid(),
		sQuery: inputContent
	};

	this.processes.helpQuery = sendData.helpQuery;

	this.socketIO.socket.emit('help_input_filter', sendData);
}

NodeConnection.prototype.authentificate = function() {
	var selfRef = this;
	console.log(this.socketIO.token);
	if (this.socketIO.token.token == false) {
		Ajax.ajaxPost(apiUrl+"socketIO/gettoken/", {}, false, function(data, dataArray, sendData) {
			if (dataArray.result == true) selfRef.socketIO.socket.emit('cv_auth', dataArray.token);
			else selfRef.socketIO.connection.status = 'denied';
		});
	} else this.socketIO.socket.emit('cv_auth', this.socketIO.token);
}

/** Get the current exchange rate for a specific symbol
 *
 * @param str symbol (the symbol to get the ask/bid for)
 *
 * @returns void
 */
NodeConnection.prototype.getSpecificExchangeRate = function(symbol) {
	this.socketIO.socket.emit('cv_get_exchange_rate', {symbol: symbol});
}

/** Fetch the cache of exchange rates
 *
 * @returns void
 */
NodeConnection.prototype.fetchExchangeRates = function() {
	if (this.caches.exchangeRates.rebuilding === true) return;

	if (this.isCacheExpired('exchangeRates', 60)) {
		this.caches.exchangeRates.rebuilding = true;
		this.socketIO.socket.emit('cv_fetch_exchange_rates');
	}
}

//emit - kuldes
//on - kapas
/** Request knowledge base question from the server
 *
 * @
 *
 */
NodeConnection.prototype.getKnowledgeBaseQuestion = function(questionId, languageCode) {
	this.socketIO.socket.emit('cv_get_knowledgebase_question', {questionId: questionId, languageCode: languageCode});
}


/** Get cache expiry
 *
 * @parameter str cache  (the cache to get the expiration)
 * @optional  int expiry (the number of seconds that needs to pass to consider this cache expired)
 *
 * @returns bool
 */
NodeConnection.prototype.isCacheExpired = function(cache, expiry) {
	var currentTimestamp = Math.floor(Date.now() / 1000),
		tempTimestamp = null;

	if (typeof expiry == 'undefined') expiry = 60;

	if (this.caches[cache].timestamp === null) return true;
	else {
		tempTimestamp = this.caches[cache].timestamp + expiry;
		if (tempTimestamp < currentTimestamp) return true;
		else return false;
	}
	
}

/** Visually update the exchange rates from the cache
 *
 * @returns void
 */
NodeConnection.prototype.updateExchangeRates = function() {
	var exchangeRates,
		selfRef = this;

	if (this.caches.exchangeRates.rebuilding === true)
	{
		setTimeout(function() {
			selfRef.updateExchangeRates();
		}, 500);
		return;
	} else if (this.caches.exchangeRates.data === null)
	{
		this.fetchExchangeRates();
		setTimeout(function() {
			selfRef.updateExchangeRates();
		}, 500);
		return;
	}

	exchangeRates = this.caches.exchangeRates.data;

	jQuery(".cv-exchange-rate-container").each(function() {
		var exchangeRateContainer = jQuery(this).find('.cv-exchange-rates-number'),
			trendContainer = jQuery(this).find('.cv-exchange-rates-trend'),
			dateContainer = jQuery(this).find('.cv-exchange-rates-date'),
			symbol = exchangeRateContainer.data('cv-rate-symbol'),
			currencies = symbol.split("/"),
			invSymbol = currencies[1]+"/"+currencies[0],
			updateCallback = exchangeRateContainer.data('cv-update-callback'),
			trendData,
			trendClass,
			dateData,
			dateFormat;

		
		console.log(exchangeRateContainer);
		console.log(exchangeRates);
		if (exchangeRates.hasOwnProperty(symbol)) data = exchangeRates[symbol];
		else if (exchangeRates.hasOwnProperty(invSymbol))
		{
			exchangeRates[symbol] = JSON.parse(JSON.stringify(exchangeRates[invSymbol]));
			exchangeRates[symbol].ask.value = math.divide(math.bignumber(1), math.bignumber(exchangeRates[invSymbol].bid.value));
			exchangeRates[symbol].bid.value = math.divide(math.bignumber(1), math.bignumber(exchangeRates[invSymbol].ask.value));
			exchangeRates[symbol].median.value = math.divide(math.bignumber(1), math.bignumber(exchangeRates[invSymbol].median.value));
			data = exchangeRates[symbol];
		} else {
			console.error("No symbol found: " + currencies[0] + "/" + currencies[1]);
			return;
		}
		
		console.log(data);
		if (exchangeRateContainer.data('cv-rate-type') == 'ask') {
			exchangeRateContainer.data('cv-last-rate', data.ask.value);
			animate_exchange_rate(exchangeRateContainer, data.ask.value, 1000, exchangeRateContainer.data('cv-rate-fraction'));
		} else if (exchangeRateContainer.data('cv-rate-type') == 'bid') {
			exchangeRateContainer.data('cv-last-rate', data.bid.value);
			console.log(jQuery(this).data('cv-rate-fraction'));
			animate_exchange_rate(exchangeRateContainer, data.bid.value, 1000, exchangeRateContainer.data('cv-rate-fraction'));
		}

		if (updateCallback != null)
		{
			if (typeof updateCallback != 'function') updateCallback = getFunction(updateCallback);
			if (typeof updateCallback == 'function') updateCallback();
		}

		if (trendContainer.length > 0)
		{
			if (exchangeRateContainer.data('cv-rate-type') == 'ask') trendData = data.ask;
			else if (exchangeRateContainer.data('cv-rate-type') == 'bid') trendData = data.bid;
			else if (exchangeRateContainer.data('cv-rate-type') == 'median') trendData = data.median;
			else return;


			if (trendData.trend == 'up') trendClass = 'fa-arrow-up';
			else if (trendData.trend == 'down') trendClass = 'fa-arrow-down';
			else if (trendData.trend == 'stable') trendClass = 'fa-minus';
			else return;
	
			trendContainer.find('i').removeClass('fa-arrow-up').removeClass('fa-arrow-down').removeClass('fa-minus').addClass(trendClass);
		}

		if (dateContainer.length > 0)
		{
			dateFormat = dateContainer.data('cv-date-format');
			if (dateFormat == null) dateFormat = 'DD.MM.YYYY, HH:mm';
			dateData = moment(data.timestamp, 'YYYY-MM-DD HH:mm:ss');
			dateContainer.html(dateData.format(dateFormat));
		}
	});
}

/*NodeConnection.prototype.updateExchangeRateRooms = function() {
	var rooms = [];
	jQuery(".cv-room-entity").each(function() {
		rooms.push(jQuery(this).data('cv-room'));
	});
	
	rooms = array_unique(rooms);
	this.socketIO.socket.emit('cv_set_exchange_rate_rooms', {rooms: rooms});
}*/