const { _ } = require('lib/locale.js');
const { BrowserWindow, Menu, Tray } = require('electron');
const { shim } = require('lib/shim');
const url = require('url')
const path = require('path')
const urlUtils = require('lib/urlUtils.js');
const { dirname, basename } = require('lib/path-utils');
const fs = require('fs-extra');

class ElectronAppWrapper {

	constructor(electronApp, env) {
		this.electronApp_ = electronApp;
		this.env_ = env;
		this.win_ = null;
		this.willQuitApp_ = false;
		this.tray_ = null;
		this.buildDir_ = null;
	}

	electronApp() {
		return this.electronApp_;
	}

	setLogger(v) {
		this.logger_ = v;
	}

	logger() {
		return this.logger_;
	}

	window() {
		return this.win_;
	}

	createWindow() {
		const windowStateKeeper = require('electron-window-state');

		// Load the previous state with fallback to defaults
		const windowState = windowStateKeeper({
			defaultWidth: 800,
			defaultHeight: 600,
			file: 'window-state-' + this.env_ + '.json',
		});

		const windowOptions = {
			x: windowState.x,
			y: windowState.y,
			width: windowState.width,
			height: windowState.height,
		};

		// Linux icon workaround for bug https://github.com/electron-userland/electron-builder/issues/2098
		// Fix: https://github.com/electron-userland/electron-builder/issues/2269
		if (shim.isLinux()) windowOptions.icon = __dirname + '/build/icons/128x128.png';

		this.win_ = new BrowserWindow(windowOptions)

		this.win_.loadURL(url.format({
			pathname: path.join(__dirname, 'index.html'),
			protocol: 'file:',
			slashes: true
		}))

		// Uncomment this to view errors if the application does not start
		// if (this.env_ === 'dev') this.win_.webContents.openDevTools();

		this.win_.on('close', (event) => {
			// If it's on macOS, the app is completely closed only if the user chooses to close the app (willQuitApp_ will be true)
			// otherwise the window is simply hidden, and will be re-open once the app is "activated" (which happens when the
			// user clicks on the icon in the task bar).

			// On Windows and Linux, the app is closed when the window is closed *except* if the tray icon is used. In which
			// case the app must be explicitely closed with Ctrl+Q or by right-clicking on the tray icon and selecting "Exit".

			if (process.platform === 'darwin') {
				if (this.willQuitApp_) {
					this.win_ = null;
				} else {
					event.preventDefault();
					this.hide();
				}
			} else {
				if (this.trayShown() && !this.willQuitApp_) {
					event.preventDefault();
					this.win_.hide();
				} else {
					this.win_ = null;
				}
			}
		})

		// Let us register listeners on the window, so we can update the state
		// automatically (the listeners will be removed when the window is closed)
		// and restore the maximized or full screen state
		windowState.manage(this.win_);
	}

	async waitForElectronAppReady() {
		if (this.electronApp().isReady()) return Promise.resolve();

		return new Promise((resolve, reject) => {
			const iid = setInterval(() => {
				if (this.electronApp().isReady()) {
					clearInterval(iid);
					resolve();
				}
			}, 10);
		});
	}

	async quit() {
		this.electronApp_.quit();
	}

	exit(errorCode = 0) {
		this.electronApp_.exit(errorCode);
	}

	trayShown() {
		return !!this.tray_;
	}

	// This method is used in macOS only to hide the whole app (and not just the main window)
	// including the menu bar. This follows the macOS way of hidding an app.
	hide() {
		this.electronApp_.hide();
	}

	buildDir() {
		if (this.buildDir_) return this.buildDir_;
		let dir = __dirname + '/build';
		if (!fs.pathExistsSync(dir)) {
			dir = dirname(__dirname) + '/build';
			if (!fs.pathExistsSync(dir)) throw new Error('Cannot find build dir');
		}

		this.buildDir_ = dir;
		return dir;
	}

	trayIconFilename_() {
		let output = '';

		if (process.platform === 'darwin') {
			output = 'macos-16x16Template.png'; // Electron Template Image format
		} else {
			output = '16x16.png';
		}

		if (this.env_ === 'dev') output = '16x16-dev.png'

		return output;
	}

	// Note: this must be called only after the "ready" event of the app has been dispatched
	createTray(contextMenu) {
		try {
			this.tray_ = new Tray(this.buildDir() + '/icons/' + this.trayIconFilename_())
			this.tray_.setToolTip(this.electronApp_.getName())
			this.tray_.setContextMenu(contextMenu)

			this.tray_.on('click', () => {
				this.window().show();
			});
		} catch (error) {
			console.error("Cannot create tray", error);
		}
	}

	destroyTray() {
		if (!this.tray_) return;
		this.tray_.destroy();
		this.tray_ = null;
	}

	ensureSingleInstance() {
		if (this.env_ === 'dev') return false;

		return new Promise((resolve, reject) => {
			const alreadyRunning = this.electronApp_.makeSingleInstance((commandLine, workingDirectory) => {
				const win = this.window();
				if (!win) return;
				if (win.isMinimized()) win.restore();
				win.show();
				win.focus();
			});

			if (alreadyRunning) this.electronApp_.quit();

			resolve(alreadyRunning);
		});
	}

	async start() {
		// Since we are doing other async things before creating the window, we might miss
		// the "ready" event. So we use the function below to make sure that the app is ready.
		await this.waitForElectronAppReady();

		const alreadyRunning = await this.ensureSingleInstance();
		if (alreadyRunning) return;

		this.createWindow();

		this.electronApp_.on('before-quit', () => {
			this.willQuitApp_ = true;
		})

		this.electronApp_.on('window-all-closed', () => {
			this.electronApp_.quit();
		})

		this.electronApp_.on('activate', () => {
			this.win_.show();
		})
	}

}

module.exports = { ElectronAppWrapper };