
var Conf = {
	 appName: 'Verdnatura'
	,dsName: 'verdnatura'
	,dsPath: 'HKCU\\Software\\ODBC\\ODBC.INI\\verdnatura'
	,regPath: 'HKCU\\Software\\Verdnatura\\vn-access'
	,remoteUrl: 'https://verdnatura.es/vn-access'
	,dbHost: 'db.verdnatura.es'
	,defaultModule: 'tpv'
	,defaultLocale: 'es'
	,dbName: 'vn2008'
	,maxCorruptSize: 600
};

var Locale = {
	es: {
		"Enter a user name":
			"Introduce un nombre de usuario"
		,"Enter a password":
			"Introduce una contraseña"
		,"Server can't be reached":
			"No se ha podido conectar con el servidor"
		,"Updating":
			"Actualizando"
		,"Bad login":
			"Usuario o contraseña incorrectos"
		,"Application it's already open":
			"La aplicación ya está abierta"
		,"Loading":
			"Cargando"
		,"Error while updating":
			"Error al actualizar"
		,"Microsoft Access 2003 is not installed":
			"Microsoft Access 2003 no está instalado en el sistema"
		,"MDB file not found":
			"No se encontro el fichero MDB"
		,"Cache files have been deleted":
			"Se han borrado todos los ficheros de la caché"
	}
};

var App = {
	shell: new ActiveXObject('WScript.Shell'),
	fso: new ActiveXObject('scripting.filesystemobject'),
	
	init: function() {
		var width = 420;
		var height = 420;

		window.resizeTo(width, height);
		window.moveTo((screen.width - width) / 2, (screen.height - height) / 2);
	},
	
	onLoad: function() {
		// Initializes the global variables
	
		var split = Verdnatura.commandLine.match(/(?:[^\s"]+|"[^"]*")+/g);
		
		if (split.length > 1)
			this.module = split[1].replace(/^"+|"+$/g, '');
		if (!this.module)
			this.module = Conf.defaultModule;

		this.appDir = this.getEnv('ProgramFiles') +'\\'+ Conf.appName;
		this.moduleDir = this.shell.SpecialFolders('AppData') +'\\'+ Conf.appName;
		this.compressFile = this.getEnv('TEMP') +'\\'+ this.module +'.7z';
		this.mdbFile = this.moduleDir +'\\'+ this.module +'.mdb';
		this.lockFile = this.moduleDir +'\\'+ this.module +'.ldb';
		this.certFile = this.appDir +'\\cacert.pem';

		// Creates the necessary registry entries
		
		var configured = this.regRead(Conf.regPath, 'configured');
		
		if (!configured) {
			var path;
			
			// Creates the Access configuration entries
			
			path = 'HKCU\\Software\\Microsoft\\Office\\11.0\\Access\\Settings';
			this.regWrites(path, 'REG_DWORD', {
				'Confirm Document Deletions' : 0,
				'Confirm Action Queries'     : 0,
				'Confirm Record Changes'     : 0
			});
			
			path = 'HKCU\\Software\\Microsoft\\Office\\11.0\\Access\\Security';
			this.regWrite(path, 'Level', 1, 'REG_DWORD');
			
			// Creates the MySQL ODBC connection
			
			var driverPath = this.getEnv('ProgramFiles')
				+'\\MySQL\\Connector ODBC 5.1\\myodbc5.dll';
			
			var params = {
				'Driver'         : driverPath,
				'DESCRIPTION'    : Conf.appName,
				'SERVER'         : Conf.dbHost,
				'DATABASE'       : Conf.dbName,
				'SSLCA'          : this.certFile,
				'SSLVERIFY'      : 1,
				'AUTO_RECONNECT' : 1
			};

			this.createOdbc(
				Conf.dsName,
				'Mysql ODBC 5.1 Driver',
				params
			);
			
			// Marks the application as configured
			
			this.regWrite(Conf.regPath, 'configured', 1, 'REG_DWORD');
		}
		
		// Loads the form data
		
		var user = this.regRead(Conf.dsPath, 'UID');
		var password = this.regRead(Conf.dsPath, 'PWD');
		var remember = this.regRead(Conf.regPath, 'remember');

		if (user)
			this.$('user').value = user;

		if (remember && password) {
			this.$('password').value = password;
			this.$('remember').checked = true;
			this.onEnterClick();
		} else
			this.resetForm(true);
	},

	resetForm: function(clearPassword) {
		if (clearPassword)
			this.$('password').value = '';

		this.$('user').focus();
		this.$('user').select();
	},

	createOdbc: function(dsName, driverName, params) {
		var odbcPath = 'HKCU\\Software\\ODBC\\ODBC.INI\\';
		
		this.regWrites(odbcPath + dsName, 'REG_SZ', params);
		this.regWrite(odbcPath + 'ODBC Data Sources',
			dsName, driverName, 'REG_SZ');	
	},
	
	disableUi: function(disabled, loadMessage) {
		if (disabled)
			this.hideMessage();
		else
			loadMessage = '';
		
		this.$('loading-message').innerHTML = loadMessage;
		
		this.$('user').disabled = disabled;
		this.$('password').disabled = disabled;
		this.$('remember').disabled = disabled;
		this.$('enter').disabled = disabled;
		
		var display = disabled ? 'block' : 'none';
		this.$('background').style.display = display;
		this.$('spinner').style.display = display;
	},

	onCleanCacheClick: function() {
		setTimeout(function() { App.cleanCache(); });
	},

	cleanCache: function() {
		if (this.fso.folderExists(this.moduleDir)) {
			var folder = this.fso.getFolder(this.moduleDir);
			var files = new Enumerator(folder.files);

			for (; !files.atEnd(); files.moveNext()) {
				var file = files.item();
				if (/\.mdb$/.test(file.name) && file.name != 'config.mdb')
				try {
					file.Delete();
				} catch (e) {}
			}
		}

		this.showMessage(_('Cache files have been deleted'), 'notice');
	},

	onKeyPress: function(event) {
		switch (event.keyCode) {
			case 13: // Enter
				this.onEnterPress(event);
				break;
			case 27: // Esc
				window.close();
				break;
		}
	},

	onEnterPress: function(event) {
		var target = event.target || event.srcElement;

		if (target && target.id == 'user' && this.$('password').value == '') {
			this.$('password').focus();
			return;
		}

		this.onEnterClick();
	},
	
	onEnterClick: function() {
		this.disableUi(true, _('Loading'));
		setTimeout(function() { App.login(); });
	},

	login: function() {
		try {
			var user = this.$('user').value;
			var password = this.$('password').value;
	
			if (!user || user === '')
				throw new Error(_('Enter a user name'));
			if (!password || password === '')
				throw new Error(_('Enter a password'));
		
			this.regWrite(Conf.dsPath, 'UID', user, 'REG_SZ');
			this.regWrite(Conf.dsPath, 'PWD', password, 'REG_SZ');

			var version = this.fetchVersion();

			if (version !== null) {
				this.disableUi(true, _('Updating'));
				var remoteFile = version
					? '.archive/'+ this.module +'/'+ version +'.7z'
					: this.module +'.7z?'+ new Date().getTime();
				remoteFile = Conf.remoteUrl +'/'+ remoteFile;
	
				var request = new ActiveXObject('MSXML2.XMLHTTP');
				request.open('GET', remoteFile, true);
				request.onreadystatechange = function() {
					App.onRequestReady(request);
				};
				request.send();
			} else
				this.openMdb();
		} catch (err) {
			this.catchError(err);
		}
	},

	/**
	 * Gets information about the version to download.
	 * 
	 * @return {Number|Boolean} Version number, %false if cannot
	 * fetch or %null if local is up-to-date
	 */
	fetchVersion: function() {
		// Gets the last version number using the MySQL ODBC connection

		var mysqlConn = new ActiveXObject('ADODB.Connection');;

		try {
			mysqlConn.open(this.getOdbcString({
				'Driver': '{MySQL ODBC 5.1 Driver}',
				'Server': Conf.dbHost,
				'Database': Conf.dbName,
				'Uid': this.$('user').value,
				'Pwd': this.$('password').value,
				'Sslverify': 1,
				'Sslca': this.certFile
			}));
		} catch (err) {
			var dbErrors = mysqlConn && mysqlConn.errors;

			if (dbErrors && dbErrors.count > 0) {
				var dbError = dbErrors.item(0);

				switch (dbError.NativeError) {
					case 1045: // Access denied
						clearPassword = true;
						err = new Error(_('Bad login'));
						err.name = 'BadLogin';
						break;
					case 2003: // Can't connect
						err = new Error(_('Server can\'t be reached'));
						break;
					default:
						err = new Error(dbError.description);
				}

				dbErrors.clear();
			}

			throw err;
		}
	
		var sql = "SELECT version FROM versiones WHERE programa = '"+ this.module +"'";
		var rs = mysqlConn.execute(sql);

		var version = rs.EOF ? false : parseInt(rs.fields(0).value);

		rs.close();
		mysqlConn.close();

		if (this.$('previous-version').checked && version > 1)
			version -= 1;

		// Checks if it's already open

		if (this.fso.fileExists(this.lockFile))
		try {
			this.fso.deleteFile(this.lockFile);
			return version;
		} catch (e) {
			throw new Error(_('Application it\'s already open'));
		}

		// Checks if MDB exists

		if (!this.fso.fileExists(this.mdbFile))
			return version;

		// If it's abnormaly bigger, maybe is corrupted, so force download

		var file = this.fso.getFile(this.mdbFile);
		
		if (file.size > Conf.maxCorruptSize * 1024 * 1024)
			return version;

		// Obtains the local version number from the MDB file

		var localVersion;

		try {
			var mdbConn = new ActiveXObject('ADODB.Connection');
			mdbConn.open(this.getOdbcString({
				'Provider': 'Microsoft.Jet.OLEDB.4.0',
				'Data Source': this.mdbFile
			}));

			var oRs = new ActiveXObject('ADODB.Recordset');
			oRs.Open('SELECT Version FROM tblVariables', mdbConn);
			localVersion = oRs.EOF ? false : parseInt(oRs('Version'));
			
			oRs.close();
			mdbConn.close();
		} catch (e) {}
		
		// Determines if should download

		return !localVersion || version === false || localVersion != version
			? version
			: null;
	},

	getOdbcString: function(options) {
		var optionsArray = [];
		for (var option in options)
			optionsArray.push(option +'='+ options[option]);
		return optionsArray.join(';');
	},
	
	onRequestReady: function(request) {
		if (request.readyState !== 4)
			return;
		
		try {
			if (request.status !== 200)
				throw new Error('HTTP: '+ request.statusText);

			if (this.fso.fileExists(this.compressFile))
				this.fso.deleteFile(this.compressFile);

			var stream = new ActiveXObject('ADODB.Stream');
			stream.open();
			stream.Type = 1; //adTypeBinary
			stream.write(request.responseBody);
			stream.Position = 0;
			stream.saveToFile(this.compressFile, 2);
			stream.close();
			
			if (this.fso.fileExists(this.mdbFile))
				this.fso.deleteFile(this.mdbFile);
		
			this.run('7za e "'+ this.compressFile +'" -o"'+ this.moduleDir +'"', true);
			this.fso.deleteFile(this.compressFile);
		} catch (e) {
			alert(_('Error while updating') +': '+ e.message);
		}

		try {
			if (!this.fso.fileExists(this.mdbFile))
				throw new Error(_('MDB file not found'));

			this.openMdb();
		} catch (e) {
			this.catchError(e);
		}
	},
	
	openMdb: function() {
		var remember = this.$('remember').checked ? 1 : 0;
		this.regWrite(Conf.regPath, 'remember', remember, 'REG_DWORD');

		var programFiles = this.getEnv('ProgramFiles');
		var accessBin = programFiles +'\\Microsoft Office\\OFFICE11\\MSACCESS.EXE';

		if (!this.fso.fileExists(accessBin))
			throw new Error(_('Microsoft Access 2003 is not installed'));

		this.shell.exec('"'+ accessBin +'" "'+ this.mdbFile +'"');
		window.close();
	},
	
	catchError: function(err) {
		var clearPassword = err.name == 'BadLogin';

		if (!this.$('remember').checked || clearPassword)
			this.regWrite(Conf.dsPath, 'PWD', '', 'REG_SZ');

		this.disableUi(false);
		this.showMessage(err.message, 'error');
		this.resetForm(clearPassword);
	},

	/**
	 * Displays a non-intrusive message.
	 *
	 * @param {String} message Message to display
	 * @param {String<error|notice>} className Message type
	 */
	showMessage: function(message, className) {
		setTimeout(function() {
			App.showMessageAsync(message, className);
		});
	},
	
	showMessageAsync: function(message, className) {
		if (this.messageTimeout)
			clearTimeout(this.messageTimeout);

		var messageDiv = this.$('message');
		messageDiv.className = className;
		messageDiv.innerHTML = message;
		messageDiv.style.display = 'block';
		this.messageTimeout = setTimeout(function() {
			App.hideMessage();
		}, 10000);
	},

	onBodyClick: function() {
		this.hideMessage();
	},
	
	/**
	 * Hides the last displayed non-intrusive message.
	 */
	hideMessage: function() {
		if (this.messageTimeout) {
			this.$('message').style.display = 'none';
			clearTimeout(this.messageTimeout);
			this.messageTimeout = null;
		}
	},
	
	/**
	 * Obtains a DOM element by it's identifier.
	 *
	 * @param {String} id The element id
	 */
	$: function(id) {
		return document.getElementById(id);
	},

	run: function(command, wait) {
		if (!wait)
			wait = false;
	
		this.shell.run(command, 0, wait);
	},
	
	getEnv: function(varName) {
		return this.shell.expandEnvironmentStrings('%'+ varName +'%');
	},

	regRead: function(path, key) {
		try {
			var value = this.shell.regRead(path +'\\'+ key);
		} catch (e) {
			var value = null;
		}
		
		return value;
	},
	
	regWrite: function(path, key, value, type) {
		this.shell.regWrite(path +'\\'+ key, value, type);
	},

	regWrites: function(path, type, values) {
		for(var key in values)
			this.regWrite(path, key, values[key], type);		
	},
	
	onUnload: function() {
		this.disableUi(false);
	}
};

App.init();

function _(string) {
	var translation = Locale[Conf.defaultLocale][string];
	return translation ? translation : string;
}
