import * as fs from "fs";
import * as path from "path";
import * as util from "util";
import { getPathFromAmdModule } from "vs/base/common/amd";
import * as lp from "vs/base/node/languagePacks";
import product from "vs/platform/product/node/product";
import { Translations } from "vs/workbench/services/extensions/common/extensionPoints";

const configurations = new Map<string, Promise<lp.NLSConfiguration>>();
const metadataPath = path.join(getPathFromAmdModule(require, ""), "nls.metadata.json");

export const isInternalConfiguration = (config: lp.NLSConfiguration): config is lp.InternalNLSConfiguration => {
	return config && !!(<lp.InternalNLSConfiguration>config)._languagePackId;
};

const DefaultConfiguration = {
	locale: "en",
	availableLanguages: {},
};

export const getNlsConfiguration = async (locale: string, userDataPath: string): Promise<lp.NLSConfiguration> => {
	const id = `${locale}: ${userDataPath}`;
	if (!configurations.has(id)) {
		configurations.set(id, new Promise(async (resolve) =>  {
			const config = product.commit && await util.promisify(fs.exists)(metadataPath)
				? await lp.getNLSConfiguration(product.commit, userDataPath, metadataPath, locale)
				: DefaultConfiguration;
			if (isInternalConfiguration(config)) {
				config._languagePackSupport = true;
			}
			resolve(config);
		}));
	}
	return configurations.get(id)!;
};

export const getTranslations = async (locale: string, userDataPath: string): Promise<Translations> => {
	const config = await getNlsConfiguration(locale, userDataPath);
	if (isInternalConfiguration(config)) {
		try {
			return JSON.parse(await util.promisify(fs.readFile)(config._translationsConfigFile, "utf8"));
		} catch (error) { /* Nothing yet. */}
	}
	return {};
};

export const getLocaleFromConfig = async (userDataPath: string): Promise<string> => {
	let locale = "en";
	try {
		const localeConfigUri = path.join(userDataPath, "User/locale.json");
		const content = stripComments(await util.promisify(fs.readFile)(localeConfigUri, "utf8"));
		locale = JSON.parse(content).locale;
	} catch (error) { /* Ignore. */ }
	return locale;
};

// Taken from src/main.js in the main VS Code source.
const stripComments = (content: string): string => {
	const regexp = /("(?:[^\\"]*(?:\\.)?)*")|('(?:[^\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;

	return content.replace(regexp, (match, _m1, _m2, m3, m4) => {
		// Only one of m1, m2, m3, m4 matches
		if (m3) {
			// A block comment. Replace with nothing
			return '';
		} else if (m4) {
			// A line comment. If it ends in \r?\n then keep it.
			const length_1 = m4.length;
			if (length_1 > 2 && m4[length_1 - 1] === '\n') {
				return m4[length_1 - 2] === '\r' ? '\r\n' : '\n';
			}
			else {
				return '';
			}
		} else {
			// We match a string
			return match;
		}
	});
};
