/**
 * @copyright Franson Technology AB, Sweden, 2008
 *
 * @author Fredrik Blomqvist
 *
 * Signup handler script
 *
 * The script should be able to be included
 * on all html-pages with properly id-tagged input fields.
 * Only dependency is on the GpsGate.Server.SignUp proxy
 *
 * todo: howto localize some of the messages? ("- other -", "- choose -" selects. rely on hidden fields?)
 *
 */

var GpsGate = GpsGate || {};

// expose some callback (event) hooks if the html needs
// to do some more dynamic content changes, i.e change
// images etc when the settings are altered.
GpsGate.SignUp =
{
	onDeviceChanged: function(device)
	{
		// default NOP
	},

	onOperatorChanged: function(operator)
	{
		// default NOP
	},

	onCountryChanged: function(country)
	{
		// default NOP
	},


	onBeginCommit: function(settings)
	{
		// default NOP
	},

	onCommitOk: function()
	{
		// default NOP
	},

	onCommitError: function()
	{
		// default NOP
	}
};


window.onload = function()
{
	//----------- generic functions

	function $(idOrNode)
	{
		if (typeof(idOrNode) == 'string')
			return document.getElementById(idOrNode);
		return idOrNode;
	}

	function trimStr(str)
	{
		return str.replace(/^\s+|\s+$/g, '');
	}

	function setNodeVisible(node, visible)
	{
		node = $(node);
		if (node) node.style.display = visible ? 'block' : 'none';
	}

	function show(node)
	{
		setNodeVisible($(node), true);
	}
	function hide(node)
	{
		setNodeVisible($(node), false);
	}

	//--------------------

	// verify that all prerequisites are present
	// messages intended for the persons deploying the signup-page.

	// todo: verify the actual DOM-types also?
	function validateAllFieldsExist()
	{
		var message = '';

		//---- mandatory fields ----
		if ($('GpsGate_usernameField') == null)
		{
			message += '- Username field doesn\'t exist\n';
		}
		// GpsGate_usernameErrorMessageField
		if ($('GpsGate_emailField') == null)
		{
			message += '- E-mail field doesn\'t exist\n';
		}
		// GpsGate_emailErrorMessageField

		if ($('GpsGate_deviceSelect') == null)
		{
			message += '- Device select list doesn\'t exist\n';
		}
		// GpsGate_deviceDescriptionField
		// GpsGate_deviceErrorMessageField
		if ($('GpsGate_imeiField') == null)
		{
			message += '- IMEI field doesn\'t exist\n';
		}
		// GpsGate_imeiErrorMessageField
		if ($('GpsGate_phoneNrField') == null)
		{
			message += '- Phone number field doesn\'t exist\n';
		}
		// GpsGate_phoneNrErrorMessageField

		if ($('GpsGate_countrySelect') == null)
		{
			message += '- Country select list doesn\'t exist\n';
		}
		if ($('GpsGate_mobileOperatorSelect') == null)
		{
			message += '- Operator select list doesn\'t exist\n';
		}
		// GpsGate_operatorErrorMessageField

		if ($('GpsGate_apnField') == null)
		{
			message += '- APN address field doesn\'t exist\n';
		}
		// GpsGate_apnErrorMessageField
		if ($('GpsGate_apnUsernameField') == null)
		{
			message += '- GPRS username field doesn\'t exist\n';
		}
		if ($('GpsGate_apnPasswordField') == null)
		{
			message += '- GPRS password field doesn\'t exist\n';
		}

		if ($('GpsGate_commitButton') == null)
		{
			message += '- Signup button doesn\'t exist\n';
		}
		if ($('GpsGate_commitOkMessage') == null)
		{
			message += '- Commit OK message field doesn\'t exist\n';
		}

		if ($('GpsGate_errorUsernameAlreadyExists') == null)
		{
			message += '- Username already exists error message field doesn\'t exist\n';
		}

		if ($('GpsGate_errorDeviceAlreadyExists') == null)
		{
			message += '- Device already exists error message field doesn\'t exist\n';
		}
		if ($('GpsGate_errorEmailCoundNotBeSent') == null)
		{
			message += '- Error sending e-mail field doesn\'t exist\n';
		}
		if ($('GpsGate_errorDeviceConfigurationFailed') == null)
		{
			message += '- Error configuring the device error message field doesn\'t exist\n';
		}
		if ($('GpsGate_errorUnknownError') == null)
		{
			message += '- Unknown error message field doesn\'t exist\n';
		}
		var fieldsOk = message.length == 0;

		//---- optional fields ---------

		if ($('GpsGate_commitPendingSpinner') == null)
		{
			message += '- [Optional] Commit pending progress-spinner indicator doesn\'t exist\n';
		}
		if ($('GpsGate_loadingSpinner') == null)
		{
			message += '- [Optional] Loading progress-spinner indicator doesn\'t exist\n';
		}

		//------------------

		if (!fieldsOk)
		{
			alert('Error\n' + message);
		}
		return fieldsOk;
	}

	var htmlOk = validateAllFieldsExist();

	var serviceExists = typeof(GpsGate) != 'undefined' && typeof(GpsGate.Server) != 'undefined' && typeof(GpsGate.Server.SignUp) != 'undefined';
	if (!serviceExists)
	{
		alert('Error: GpsGate.Server.SignUp not loaded');
	}

	if (!htmlOk || !serviceExists)
	{
		// nothing more to do. exiting
		return;
	}

	var _countries = [];
	var _operators = [];
	var _devices = [];

	var countriesLoaded = false;
	var devicesLoaded = false;
	var operatorsLoaded = false; // this will be re-loaded on each country change

	function hideAllMessages()
	{
		hide('GpsGate_usernameErrorMessageField');
		hide('GpsGate_emailErrorMessageField');

		hide('GpsGate_deviceErrorMessageField');

		hide('GpsGate_imeiErrorMessageField');
		hide('GpsGate_phoneNrErrorMessageField');

		hide('GpsGate_operatorErrorMessageField');
		hide('GpsGate_apnErrorMessageField');

		hide('GpsGate_commitPendingSpinner');

		hide('GpsGate_commitOkMessage');

		hide('GpsGate_errorUsernameAlreadyExists');
		hide('GpsGate_errorDeviceAlreadyExists');
		hide('GpsGate_errorEmailCoundNotBeSent');
		hide('GpsGate_errorDeviceConfigurationFailed');
		hide('GpsGate_errorUnknownError');
	}

	function resetControl()
	{
		hideAllMessages();

		countriesLoaded = false;
		devicesLoaded = false;
		operatorsLoaded = false; // this will be re-loaded on each country change
		_countries = [];
		_operators = [];
		_devices = [];

		$('GpsGate_deviceSelect').disabled = true; // until devices are loaded
		setDeviceProtocols([]);

		//setDeviceFieldsEnabled(false);

		$('GpsGate_countrySelect').disabled = true; // until countries loaded
		setCountries([]);
		setOperators([]);

		$('GpsGate_mobileOperatorSelect').disabled = true;
		setApnFields();
		setApnFieldsEnabled(false);
	}

	resetControl();

	// ------- accessors ------------------


	function getUsername()
	{
		return trimStr($('GpsGate_usernameField').value);
	}
	function setUsername(name)
	{
		$('GpsGate_usernameField').value = name || '';
	}

	function getEmail()
	{
		return trimStr($('GpsGate_emailField').value);
	}
	function setEmail(email)
	{
		$('GpsGate_emailField').value = email || '';
	}

	function getIMEI()
	{
		return trimStr($('GpsGate_imeiField').value);
	}
	function setIMEI(imei)
	{
		$('GpsGate_imeiField').value = imei || '';
	}

	function getPhoneNr()
	{
		return trimStr($('GpsGate_phoneNrField').value);
	}
	function setPhoneNr(phoneNr)
	{
		$('GpsGate_phoneNrField').value = phoneNr || '';
	}

	function getSelectedCountry()
	{
		var countryNode = $('GpsGate_countrySelect');

		var option = countryNode.options[countryNode.selectedIndex];
		var idx = parseInt(option.value, 10);
		if (idx < 0)
			return null;

		return _countries[idx];
	}

	function getSelectedDevice()
	{
		var deviceNode = $('GpsGate_deviceSelect');

		var option = deviceNode[deviceNode.selectedIndex];
		var idx = parseInt(option.value, 10);
		if (idx < 0)
			return null;

		return _devices[idx];
	}

	function getSelectedOperator()
	{
		var operatorNode = $('GpsGate_mobileOperatorSelect');

		var option = operatorNode.options[operatorNode.selectedIndex];
		var idx = parseInt(option.value, 10);
		if (idx < 0)
			return null;

		return _operators[idx];
	}


	function setDeviceFieldsEnabled(status)
	{
		$('GpsGate_imeiField').disabled = $('GpsGate_phoneNrField').disabled = !status;
	}

	// disabled
	function setApnFieldsEnabled(status)
	{
	//	$('GpsGate_apnField').disabled = $('GpsGate_apnUsernameField').disabled = $('GpsGate_apnPasswordField').disabled = !status;
	}

	// ------------

	function isValidUsername(username)
	{
		return username != null && trimStr(username).length >= 3; // ?
	}

	function isValidEmail(email)
	{
		// from Expresso
		var re = /([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})/;
		return email != null && re.test(email);
	}

	function isValidIMEI(imei)
	{
		//var re = /^\d{15,17}$/; // just to have something, a real checksum validation is performed on the server
		return imei != null && trimStr(imei).length > 3; /*&& re.test(imei);*/
	}

	function isValidPhoneNr(phoneNr)
	{
		// todo: var re = ...
		return phoneNr != null && trimStr(phoneNr).length > 7;
	}

	function isValidAPN(apn)
	{
		// APN can be very different, doesn't need to look like a web-address (though no spaces(?) for example)
		return apn != null && trimStr(apn).length > 0;
	}

	//-----------------------------------------

	function setCountries(countries)
	{
		var countryNode = $('GpsGate_countrySelect');
		_countries = countries;
		countryNode.length = 0;

		if (countries.length > 1)
			countryNode.options[0] = new Option('- choose -', -1);
		for (var i = 0; i < countries.length; ++i)
		{
			var country = countries[i];
			countryNode.options[countryNode.options.length] = new Option(country, i); // country is a string
		}
		countryNode.options[countryNode.options.length] = new Option('- other -', -2);
	}

	function setOperators(operators)
	{
		$('GpsGate_operatorDescription').innerHTML = '';

		var operatorNode = $('GpsGate_mobileOperatorSelect');
		_operators = operators;
		operatorNode.length = 0;
		if (operators.length > 1)
			operatorNode.options[0] = new Option('- choose -', -1);
		for (var i = 0; i < operators.length; ++i)
		{
			var operator = operators[i];
			operatorNode.options[operatorNode.options.length] = new Option(operator.operator, i);
		}
		operatorNode.options[operatorNode.options.length] = new Option('- other -', -2);
	}

	function setDeviceProtocols(deviceDefinitions)
	{
		$('GpsGate_deviceDescriptionField').innerHTML = ''; // reset this field also

		var deviceNode = $('GpsGate_deviceSelect');
		_devices = deviceDefinitions;
		deviceNode.length = 0;
		if (deviceDefinitions.length > 1) // could perhaps .disable entire control in case of only one?
			deviceNode.options[0] = new Option('- choose -', -1);
		for (var i = 0; i < deviceDefinitions.length; ++i)
		{
			var device = deviceDefinitions[i];
			deviceNode.options[deviceNode.options.length] = new Option(device.name, i);
		}
	}

	function setApnFields(apn, username, password)
	{
		$('GpsGate_apnField').value = apn || '';
		$('GpsGate_apnUsernameField').value = username || '';
		$('GpsGate_apnPasswordField').value = password || '';
	}

	function getApnFields()
	{
		return {
			apn: trimStr($('GpsGate_apnField').value),
			username: trimStr($('GpsGate_apnUsernameField').value),
			password: trimStr($('GpsGate_apnPasswordField').value)
		};
	}

	//------------- end accessors -----------------

	function startLoading()
	{
		show('GpsGate_loadingSpinner');
		$('GpsGate_commitButton').disabled = true;
	}

	function stopLoading()
	{
		hide('GpsGate_loadingSpinner');
		$('GpsGate_commitButton').disabled = false;
	}

	startLoading();

	GpsGate.Server.SignUp.GetDevices('administrators',
		function(devices)
		{
			setDeviceProtocols(devices.deviceDefinitions);

			devicesLoaded = true;
			$('GpsGate_deviceSelect').disabled = false;
			if (countriesLoaded)
			{
				stopLoading();
			}

			updateDeviceFields();
		},
		function(errName, errMessage)
		{
			// ...
			devicesLoaded = false;
			$('GpsGate_deviceSelect').disabled = true;
			show('GpsGate_errorUnknownError');
			hide('GpsGate_loadingSpinner');
		}
	);

	GpsGate.Server.SignUp.GetOperatorCountries('administrators',
		function(result)
		{
			setCountries(result.countries);
			countriesLoaded = true;
			$('GpsGate_countrySelect').disabled = false;
			if (devicesLoaded)
			{
				stopLoading();
			}

			updateOperatorFields();
		},
		function(errName, errMessage)
		{
			// ...
			countriesLoaded = false;
			$('GpsGate_countrySelect').disabled = true;
			show('GpsGate_errorUnknownError');
			hide('GpsGate_loadingSpinner');
		}
	);

	function updateDeviceFields()
	{
		var device = getSelectedDevice();
	//	setDeviceFieldsEnabled(device !== null);

		$('GpsGate_deviceDescriptionField').innerHTML = device !== null ? (device.description || '') : '';

		if (typeof(GpsGate.SignUp.onDeviceChanged) == 'function')
			GpsGate.SignUp.onDeviceChanged(device);
	}

	$('GpsGate_deviceSelect').onchange = function()
	{
		updateDeviceFields();
	};

	$('GpsGate_countrySelect').onchange = function()
	{
		operatorsLoaded = false;

		var otherCountrySelected = this.selectedIndex == this.options.length - 1;

		if (otherCountrySelected)
		{
			// force operator back to '-choose-' mode
			$('GpsGate_mobileOperatorSelect').selectedIndex = $('GpsGate_mobileOperatorSelect').options.length - 1;
		}

		setApnFields();
		setApnFieldsEnabled(otherCountrySelected); // only enabled the fields in 'other' mode

		var country = getSelectedCountry();

		$('GpsGate_mobileOperatorSelect').disabled = country == null;

		if (typeof(GpsGate.SignUp.onCountryChanged) == 'function')
			GpsGate.SignUp.onCountryChanged(country);

		if (country == null)
		{
			setOperators([]);
			return;
		}

		startLoading();

		GpsGate.Server.SignUp.GetNetworksInCountry('administrators',
			country,
			function(operators)
			{
				setOperators(operators.mobileNetworks);
				operatorsLoaded = true;
				stopLoading();
				updateOperatorFields();
			},
			function(errName, errMessage)
			{
				// ...
				operatorsLoaded = false;
				updateOperatorFields();
				show('GpsGate_errorUnknownError');
				hide('GpsGate_loadingSpinner');
			}
		);
	};


	function updateOperatorFields()
	{
		var operatorNode = $('GpsGate_mobileOperatorSelect');
		setApnFieldsEnabled(!operatorNode.disabled && operatorNode.selectedIndex == operatorNode.options.length - 1); // == -other-

		var operator = getSelectedOperator();

		if (typeof(GpsGate.SignUp.onOperatorChanged) == 'function')
			GpsGate.SignUp.onOperatorChanged(operator);

		operator = operator || { apn: '', username: '', password: '', description: '' };
		$('GpsGate_operatorDescription').innerHTML = operator.description || '';

		setApnFields(operator.apn, operator.username, operator.password);
	}


	$('GpsGate_mobileOperatorSelect').onchange = function()
	{
		updateOperatorFields();
	};


	// -------

	$('GpsGate_commitButton').onclick = function()
	{
		hideAllMessages();

		var username = getUsername();
		var email = getEmail();

		var device = getSelectedDevice();
		var imei = getIMEI();
		var phoneNr = getPhoneNr();


		var usernameValid = isValidUsername(username);
		setNodeVisible('GpsGate_usernameErrorMessageField', !usernameValid);

		var emailValid = isValidEmail(email);
		setNodeVisible('GpsGate_emailErrorMessageField', !emailValid);

		var deviceValid = device !== null;
		setNodeVisible('GpsGate_deviceErrorMessageField', !deviceValid);

		var imeiValid = isValidIMEI(imei);
		setNodeVisible('GpsGate_imeiErrorMessageField', !imeiValid);

		var phoneNrValid = isValidPhoneNr(phoneNr);
		setNodeVisible('GpsGate_phoneNrErrorMessageField', !phoneNrValid);

		// operator & apn slightly more complicated since they interrelate
		var apnFields = getApnFields();
		var apnValid = apnFields.apn !== ''; // ok? (username & pwd are allowed to be empty)

		var operator = getSelectedOperator();
		var operatorValid = operator !== null || apnValid;

		var operatorId = operator != null ? operator.id : -1;

		// the "apn-disabled" mode is deprecated
		setNodeVisible('GpsGate_operatorErrorMessageField', !operatorValid /*&& $('GpsGate_apnField').disabled*/); // don't show if apn manually set
		setNodeVisible('GpsGate_apnErrorMessageField', !apnValid /*&& !$('GpsGate_apnField').disabled*/); // don't show if user hasn't editied the field

		var isReady = (
			usernameValid &&
			emailValid &&

			deviceValid &&
			imeiValid &&
			phoneNrValid &&

			apnValid
		);


		if (isReady)
		{
			$('GpsGate_commitButton').disabled = true;
			show('GpsGate_commitPendingSpinner');

			if (typeof(GpsGate.SignUp.onBeginCommit) == 'function')
			{
				GpsGate.SignUp.onBeginCommit({
					'username': username,
					'email': email,
					'device': device,
					'imei': imei,
					'phoneNr': phoneNr,
					'apn': apnFields.apn,
					'gprsUsername': apnFields.username,
					'gprsPassword': apnFields.password,
					'operatorId': operatorId
				});
			}

			GpsGate.Server.SignUp.CommitSignUp('administrators',
				username,
				email,

				device.id,
				imei,
				phoneNr,

				apnFields.apn,
				apnFields.username,
				apnFields.password,

				operatorId,

				function(status)
				{
					// these needs to be reset regardless of error or success above so user can make another try.
					$('GpsGate_commitButton').disabled = false;
					hide('GpsGate_commitPendingSpinner');

					if (status.exceptions != null && status.exceptions.length > 0)
					{
						for (var i = 0; i < status.exceptions.length; ++i)
						{
							switch (status.exceptions[i].errorCode)
							{
								// The server makes the final validation of these params
								// (as seen/mentioned above we only make rudimentary checks before submitting)
								case 'INVALID_USERNAME_FORMAT':
									show('GpsGate_usernameErrorMessageField');
									break;
								case 'INVALID_EMAIL_FORMAT':
									show('GpsGate_emailErrorMessageField');
									break;
								case 'INVALID_IMEI_FORMAT':
									show('GpsGate_imeiErrorMessageField');
									break;
								case 'INVALID_MSISDN_FORMAT':
									show('GpsGate_phoneNrErrorMessageField');
									break;

								// these are "deeper" errors.
								case 'USER_EXIST':
									show('GpsGate_errorUsernameAlreadyExists');
									break;
								case 'DEVICE_EXIST':
									show('GpsGate_errorDeviceAlreadyExists');
									break;

								case 'EMAIL_COULD_NOT_BE_SENT':
									show('GpsGate_errorEmailCoundNotBeSent');
									break;
								case 'DEVICE_CONFIG_UNSUCCESSFUL':
									show('GpsGate_errorDeviceConfigurationFailed');
									break;

								case 'UNKNOWN_ERROR':
								default:
									show('GpsGate_errorUnknownError');
									break;
							}
						}

						if (typeof(GpsGate.SignUp.onCommitError) == 'function')
							GpsGate.SignUp.onCommitError();
					}
					else
					{
						show('GpsGate_commitOkMessage');

						if (typeof(GpsGate.SignUp.onCommitOk) == 'function')
							GpsGate.SignUp.onCommitOk();
					}
				},
				function(errName, errMessage)
				{
					$('GpsGate_commitButton').disabled = false;
					hide('GpsGate_commitPendingSpinner');

					show('GpsGate_errorUnknownError'); // or should this case be separate?

					if (typeof(GpsGate.SignUp.onCommitError) == 'function')
						GpsGate.SignUp.onCommitError();
				}
			);
		}
		else
		{
			// todo: add generic err message? (all the individual err-messages are shown anyway though)
		}
	};

};
