'use strict';

//Imports
const 
	path = require('path'),
	dialog = require('electron').dialog,
	fs = require("fs"),
	fse = require("fs-extra"),
	request = require('request'),
	unzip = require('unzip'),
	readline = require('readline'),
	asar = require('./asar');

const platform = (process.platform !== "win32" && process.platform !== "darwin") ? "linux" : process.platform;


var 
	config = {
	    urls: {
	        package: "https://github.com/Jiiks/BetterDiscordApp/archive/stable16.zip",
	        finish: "https://betterdiscord.net/installed"
	    },
	    discord: {
	        lastKnownVersion: "0.0.292"
	    },
	    import: "BetterDiscordApp-stable16",
	    cache: [
	        "emotes_bttv.json",
	        "emotes_bttv_2.json",
	        "emotes_ffz.json",
	        "emotes_twitch_global.json",
	        "emotes_twitch_subscriber.json",
	        "user.json"
	    ],
	    paths: {
	    	installPath: "",
	    	libPath: ""
	    }
	};


class Installer {

	constructor(app, mainWindow, ipc, utils) {
		this.app = app;
		this.utils = utils;
		this.initConfig();
		mainWindow.setSize(800, 400);
		mainWindow.center();
		mainWindow.loadURL(`file://${__dirname}/data/index.html`);
		this.webContents = mainWindow.webContents;
		ipc.on('async', (event, arg) => this.receiveAsync(event, arg));
	}

	initConfig() {
		config.paths.installPath = path.normalize(this.utils.installPath(config.discord.lastKnownVersion));
		config.paths.libPath = path.normalize(this.utils.libPath());
	}

	install() {
		this.utils.log("Config: " + JSON.stringify(config));
		this.appendLog("Initializing");
		this.locateAppPackage();
	}

	locateAppPackage() {
		var p = path.normalize(`${config.paths.installPath}/resources`);
		var pkg = fs.readdirSync(p).filter(file => {
			if(file === "app.asar") {
				return true;
			}
		});
		if(pkg.length <= 0) {
			//App package not found
			this.appendLog("Unable to locate app package, check the logs for errors");
			return;
		}

		this.appendLog("Located app package");
		this.downloadBd();
		//this.extractAppPackage();
	}

	downloadBd() {

		var self = this;
		this.appendLog("Downloading BetterDiscord");

		var error = false;
		var size = 0;
		var downloaded = 0;

		var req = request({
			method: 'GET',
			uri: config.urls.package
		});

		req.pipe(unzip.Extract({ path: path.normalize(config.paths.libPath) }));

		req.on('data', chunk => {
			downloaded += chunk.length;
			this.updatePb(downloaded, size);
		});

		req.on('response', data => {
			size = data.headers['content-length'];
		});

		req.on('error', err => {
			error = true;
			self.utils.log(err);
			self.appendLog("Failed to download BetterDiscord package, check the logs for errors");
		});

		req.on('end', () => {
			console.log("got here?");
			if(error) {
				self.appendLog("Failed to download BetterDiscord package, check the logs for errors");
			} else {
				self.extractAppPackage();
			}
		});
	}

	extractAppPackage() {
		var self = this;

		try {
			if(fs.existsSync(`${config.paths.installPath}/resources/app`)) {
				self.appendLog("Deleting old app directory");
				setTimeout(() => {
					fse.removeSync(`${config.paths.installPath}/resources/app`);
					extract();
				}, 500);
			} else {
				extract();
			}
		} catch(err) {
			self.appendLog("Could not delete old app directory, check the logs for errors");
			return;
		}

		function extract() {
			try {
				self.appendLog("Extracting app package");
				var a = new asar(`${config.paths.installPath}/resources/app.asar`);
				a.extract(msg => {
					self.appendLog(msg);
				}, (curIndex, total) => {
					self.updatePb(curIndex, total);
				}, err => {
					if(err === null) {
						self.inject();
					} else {
						self.log(err);
						self.appendLog("Failed to extract app package, check the logs for errors");
						self.clean();
					}
				});
			}catch(err) {
				console.log(err);
			}
		}
	}

	inject() {
		var self = this;
		this.updatePb(0, 5);
		this.appendLog("Injecting loader");

		var lines = [];
		var lr = readline.createInterface({
			input: fs.createReadStream(`${config.paths.installPath}/resources/app/index.js`)
		});

		lr.on('line', line => {
			lines.push(line);

			if(line.indexOf("'use strict';") > -1) { 
				lines.push(path.normalize(`var _betterDiscord = require('${config.paths.libPath}/${config.import}');`).replace(/\\/g,"/"));
				lines.push("var _betterDiscord2;");
			}

			if(line.indexOf("mainWindow = new BrowserWindow(mainWindowOptions);") > -1) { 
				lines.push(`    _betterDiscord2 = new _betterDiscord.BetterDiscord(mainWindow, false);`);
			}
		});

		lr.on('close', () => {
			fs.writeFileSync(`${config.paths.installPath}/resources/app/index.js`, lines.join('\n'));
			self.appendLog("Finished installing BetterDiscord");
			self.updatePb(5, 5);
		});
	}

	clean() {
		this.appendLog("Cleaning installation");
		try {
			if(fs.existsSync(`${config.paths.installPath}/resources/app`)) {
				self.appendLog("Deleting app directory");
				setTimeout(() => {
					fse.removeSync(`${config.paths.installPath}/resources/app`);
				}, 500);
			}
		} catch(err) {
			self.appendLog("Could not delete old app directory, check the logs for errors");
		}
	}

	appendLog(msg) {
		this.utils.log(msg);
		this.webContents.executeJavaScript(`appendLog("${msg}");`);
	}

	updatePb(cur, max) {
		this.webContents.executeJavaScript(`updatePbar(${cur}, ${max});`);
	}

	getData(e) {
		switch(e) {
			case "discordpath":
				return config.paths.installPath;
			case "libpath":
				return config.paths.libPath;
		}
	}

	receiveAsync(event, arg) {

		var data = arg.data || '';
		arg = arg.arg;

		switch(arg) {
			case "get":
				this.replyAsync(event, data, this.getData(data));
				break;
			case "browsedialog":
				var path = dialog.showOpenDialog({ properties: ['openDirectory'] });

				switch(data) {
					case "discordpath":
						path = path === undefined ? config.paths.installPath : path[0];
						config.paths.installPath = path;
						break;
					case "libpath":
						path = path === undefined ? config.paths.libPath : path[0];
						config.paths.libPath = path;
						break;
				}

				this.replyAsync(event, data, path);
				break;
			case "install":
				this.install();
				break;
			case "quit":
				this.app.quit();
				break;
		}

	}

	replyAsync(event, arg, data) {
		event.sender.send('async-reply', { "arg": arg, "data": data });
	}

}

module.exports = Installer;
