const Login = require('./login');
const Gui = require('./gui');

module.exports = new Class({
	Extends: Vn.Object,
	Properties: {
		conn: {
			type: Db.Connection
			,get() {
				return this._conn;
			}
		},
		hash: {
			type: Vn.Hash
			,get() {
				return this._hash;
			}
		},
		gui: {
			type: Gui
			,get() {
				return this._gui;
			}
		},
		login: {
			type: Login
			,get() {
				return this._login;
			}
		}
	}

	,initialize() {
		window.addEventListener('error',
			e => this._onWindowError(e));
		window.addEventListener('unhandledrejection',
			e => this._onWindowRejection(e));
		window.addEventListener('unload',
			() => this._onWindowUnload());

		this._hash = new Vn.Hash({window: window});

		const conn = new Db.Connection();
		this.link({_conn: conn}, {'error': this._onConnError});

		this.initAutoLogin();
	}
	
	,run() {
		if (this.tryAutoLogin()) return;
		this.showLogin();
	}

	,showLogin() {
		const login = this._login = new Login({
			app: this,
			conn: this._conn,
			hash: this._hash
		});
		login.on('login', this._onLogin, this);
		login.show();
	}
	
	,async _onLogin() {
		this._freeLogin();
		if (this._gui) return;
		
		const gui = this._gui = new Gui({
			app: this,
			conn: this._conn,
			hash: this._hash
		});
		gui.on('logout', this._onLogout, this);
		await gui.show();
	}
	
	,async _onLogout() {
		this.clearAutoLogin();
		await this._freeGui();
		this.loggingOut = false;
		this.showLogin();
	}
	
	,_onWindowUnload() {
		this.unref();
	}
	
	,async _logout() {
		if (this._gui && !this.loggingOut) {
			this.loggingOut = true;
			await this._gui.logout();
		}
	}
	
	,_newVersion() {
		if (this.ignoreVersion) return;
		this.ignoreVersion = true;

		const dialog = new Htk.Dialog({
			 message: _('New version available')
			,buttons: Htk.Dialog.Button.ACCEPT
			,icon: 'warning'
		});
		dialog.on('response', this._onNewVersionResponse, this);
		dialog.open();
	}
	
	,_onNewVersionResponse() {
		location.reload();
	}
	
	,_freeLogin() {
		if (!this._login) return;
		this._login.disconnectByInstance(this);
		this._login.hide();
		this._login.unref();
		this._login = null;
	}
	
	,async _freeGui() {
		if (!this._gui) return;
		this._gui.disconnectByInstance(this);
		await this._gui.hide();
		this._gui.unref();
		this._gui = null;
	}
	
	,_destroy() {
		this._freeLogin();
		this._freeGui();
		this.deinitAutoLogin();
		if (this._conn) this._conn.unref();
		if (this._hash) this._hash.unref();
	}
	
	// Auto login
	
	,_firstLogin: true

	,initAutoLogin() {
		const isGuest = new Vn.Param({
			lot: this._hash,
			type: Boolean,
			name: 'guest'
		});
		this.link({_isGuest: isGuest}, {'changed': this._onGuestChange});

		const token = new Vn.Param({
			lot: this._hash,
			type: String,
			name: 'token'
		});
		this.link({_token: token}, {'changed': this._onTokenChange});
	}
	
	,_onGuestChange() {
		if (this._isGuest.value)
			setTimeout(this.tryAutoLogin.bind(this));
	}
	
	,_onTokenChange() {
		if (this._token.value)
			setTimeout(this.tryAutoLogin.bind(this));
	}
	
	,deinitAutoLogin() {
		if (this._isGuest) this._isGuest.unref();
		if (this._token) this._token.unref();
	}

	,autoLogin() {
		const guest = localStorage.getItem('hederaGuest');

		if (this._isGuest.value || guest) {
			localStorage.setItem('hederaGuest', true);
			return true;
		}

		if (this._token.value)
			this._conn.token = this._token.value;
		else
			this._conn.fetchToken();
		
		if (this._conn.token)
			return true;

		return false;
	}
	
	,tryAutoLogin() {
		const ok = this.autoLogin();

		this._firstLogin = false;
		this._isGuest.value = undefined;
		this._token.value = undefined;

		if (!ok)
			return false;

		this._onLogin();
		return true;
	}
	
	,clearAutoLogin() {
		localStorage.removeItem('hederaGuest');
	}

	// Error management

	,_onWindowError(event) {
		this.globalHandler(event.error);
	}
	
	,_onWindowRejection(event) {
		this.globalHandler(event.reason);
	}

	,async _onConnError(conn, err) {
		if (!(err instanceof Vn.JsonException)) return;
		switch (err.exception) {
			case 'UserDisabledError':
				Htk.Toast.showError(_('User disabled'));
				await this._logout();
				return;
			case 'OutdatedVersionError':
				this._newVersion();
				return;
		}
		if (err.statusCode == 401 && !this._login) {
			Htk.Toast.showError(_('Session expired'));
			this._logout();
		}
	}

	,async globalHandler(err) {
		try {
			if (!err) return;
			if (err instanceof Vn.JsonException) {
				const statusCode = err.statusCode;
				if (statusCode >= 400 && statusCode < 500) {
					if (err.statusCode == 403) {
						Htk.Toast.showError(err.message ||
							_('You don\'t have enough privileges'));
					} else {
						switch (err.exception) {
							case 'UserDisabledError':
							case 'OutdatedVersionError':
								return;
						}
						if (err.statusCode == 401)
							return;
						Htk.Toast.showError(err.message);
					}
				} else
					Htk.Toast.showError(err.message);
			} else {
				Htk.Toast.showError(_('Something went wrong'));
				if (this._conn)
						await this._conn.send('core/log', {
						file: err.fileName
						,line: err.lineNumber
						,message: err.message
						,stack: err.stack
						,agent: navigator.userAgent
						,location: location.href
					});
			}
		} catch(e) {
			console.error(e);
		}
	}
});